Create enterprise applications for external access using Terraform
3rd party services such as threat management tools for Azure can add incredible value but to access services, they need a secure way of connecting to the platform. Enterprise Application give full IAM (Identity and Access Management) control and can be used to provide granular access to services.
During deployment I found a need to automate the following elements:
- Registration of application within Azure with customized API permissions
- Creation of Enterprise application (Service Principal) linked to application
- Creation of client secret with no expiry date
- Creation of custom RBAC
- Assign app to subscriptions using custom RBAC role
To make sure this process was repeatable easily and at scale the following Terraform elements were used.
Create application registration
####
#App Registration
####
resource "azuread_application" "example" {
name = "Example-App"
homepage = "https://blog.l-w.tech"
oauth2_allow_implicit_flow = false
oauth2_permissions = []
owners = [
data.azurerm_client_config.current.object_id
]
required_resource_access {
resource_app_id = "00000003-0000-0000-c000-000000000000"
resource_access {
id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d"
type = "Scope"
}
resource_access {
id = "9a5d68dd-52b0-4cc2-bd40-abcf44ac3a30"
type = "Role"
}
resource_access {
id = "5b567255-7703-4780-807c-7be8301ae99b"
type = "Role"
}
resource_access {
id = "df021288-bdef-4463-88db-98f22de89214"
type = "Role"
}
resource_access {
id = "483bed4a-2ad3-4361-a73b-c83ccdbdc53c"
type = "Role"
}
}
}
Create Service Principal
####
#Service Principal
####
resource "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
app_role_assignment_required = false
tags = [
"AppServiceIntegratedApp",
"WindowsAzureActiveDirectoryIntegratedApp",
]
}
Create client secret without expiry
####
#Client Secret
####
resource "random_password" "examplesecret" {
count = 1
length = 32
special = true
override_special = "_%@"
}
resource "azuread_application_password" "example" {
application_object_id = azuread_application.example.id
description = "example Secret"
value = random_password.examplesecret[0].result
end_date = "2299-12-31T00:00:00Z"
depends_on = [
random_password.examplesecret,
azuread_application.example
]
}
Custom RBAC and assignment
Now that the app has been registered and has a basic level of access to the tenant additional access is required to ensure the 3rd party service can read resource objects that are deployed. This involves creating both a Azure RBAC policy and then an assignment for each subscription required.
####
# RBAC Role
####
resource "azurerm_role_definition" "example-rd" {
name = "Example RBAC"
scope = "/subscriptions/00000000-0000-0000-0000-000000000000"
description = "Grants minimal set of 'read' permissions to enable discovery of resources by Example-App"
permissions {
actions = [
"Microsoft.Authorization/permissions/read",
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Compute/virtualMachineScaleSets/read",
"Microsoft.Compute/virtualMachineScaleSets/virtualMachines/*/read",
"Microsoft.Network/networkInterfaces/read",
"Microsoft.Network/publicIPAddresses/read",
"Microsoft.Network/virtualNetworks/read",
"Microsoft.Network/virtualNetworks/subnets/read",
"Microsoft.Network/virtualNetworks/subnets/virtualMachines/read",
"Microsoft.Network/virtualNetworks/virtualMachines/read",
"Microsoft.Resources/subscriptions/locations/read",
"Microsoft.Resources/subscriptions/resourceGroups/read",
"Microsoft.Authorization/policyAssignments/read",
"Microsoft.Authorization/roleAssignments/read",
"Microsoft.Authorization/roleDefinitions/read",
"Microsoft.Network/networkSecurityGroups/read",
"Microsoft.Network/networkWatchers/read",
"Microsoft.Network/networkWatchers/queryFlowLogStatus/action",
"Microsoft.Sql/servers/administrators/read",
"Microsoft.Sql/servers/auditingSettings/read",
"Microsoft.Sql/servers/databases/read",
"Microsoft.Sql/servers/databases/auditingSettings/read",
"Microsoft.Sql/servers/databases/replicationLinks/read",
"Microsoft.Sql/servers/databases/securityAlertPolicies/read",
"Microsoft.Sql/servers/databases/transparentDataEncryption/read",
"Microsoft.Sql/servers/encryptionProtector/read",
"Microsoft.Sql/servers/firewallRules/read",
"Microsoft.Sql/servers/read",
"Microsoft.Sql/servers/securityAlertPolicies/read",
"Microsoft.DBforMySQL/servers/read",
"Microsoft.DBforMySQL/servers/configurations/read",
"Microsoft.DBforPostgreSQL/servers/read",
"Microsoft.DBforPostgreSQL/servers/configurations/read",
"Microsoft.DBforMariaDB/servers/read",
"Microsoft.DBforMariaDB/servers/configurations/read",
"Microsoft.Web/sites/read",
"Microsoft.Web/sites/*/read",
"Microsoft.Web/sites/config/list/Action",
"Microsoft.Web/sites/config/Read",
"Microsoft.Insights/logprofiles/read",
"Microsoft.Insights/ActivityLogAlerts/Read",
"Microsoft.Compute/disks/read",
"Microsoft.ContainerService/managedClusters/read",
"Microsoft.KeyVault/vaults/read",
"Microsoft.KeyVault/vaults/secrets/read",
"Microsoft.Storage/storageAccounts/read",
"Microsoft.Storage/storageAccounts/blobServices/containers/read",
"Microsoft.Security/securityContacts/read",
"Microsoft.Security/pricings/read",
"Microsoft.Security/settings/read"
]
not_actions = []
}
assignable_scopes = [
"/subscriptions/00000000-0000-0000-0000-000000000000"
]
}
####
#Assignment
####
resource "azurerm_role_assignment" "example-ra" {
scope = "/subscriptions/00000000-0000-0000-0000-000000000000"
role_definition_name = "Example RBAC"
principal_id = azuread_service_principal.example.id
depends_on = [
azurerm_role_definition.example-rd
]
}
Finally its possible to have all key data exported on completion by using the following:
output "Example_App_TenantID" {
value = data.azurerm_client_config.current.tenant_id
}
output "Example_App_AppID" {
value = azuread_application.example.application_id
}
output "Example_App_Secret" {
value = azuread_application_password.example.value
}