LW.

Glorified Notepad

Create enterprise applications for external access using Terraform

2021-01-08 Azure 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
}