Search code examples
azure-active-directoryterraform-provider-azure

How to use azuread_claims_mapping_policy?


I'm trying to provision azuread_claims_mapping_policy but it fails due to missing permissions.

│ Error: retrieving Claims Mapping Policy with object ID: "<GUID HERE>"
│ 
│   with azuread_claims_mapping_policy.integration_claims_mapping[0],
│   on active_directory.tf line 54, in resource "azuread_claims_mapping_policy" "integration_claims_mapping":
│   54: resource "azuread_claims_mapping_policy" "integration_claims_mapping" {
│ 
│ ClaimsMappingPolicyClient.BaseClient.Get(): unexpected status 403 with
│ OData error: Authorization_RequestDenied: Insufficient privileges to
│ complete the operation.

The code is executed in context of Managed Identity

As it is written here in documentation:

When authenticated with a service principal, this resource requires the following application roles: Policy.ReadWrite.ApplicationConfiguration and Policy.Read.All

When authenticated with a user principal, this resource requires one of the following directory roles: Application Administrator or Global Administrator

I added Application Administrator role to that identity. But it failed with te result above. It passes with Global Administrator, but I would prefer to avoid this.

Is there a way to provision azuread_claims_mapping_policy without Global Administrator role?

Here is the code I used:

locals {
  entity_ids = {
    "dev" : ["entity-id"],
  }

  redirect_uris = {
    "dev" : ["redirect-uri"],
  }
}

data "azuread_client_config" "current" {}

resource "azuread_application" "saml_integration" {
  count           = var.env == "prod" ? 0 : 1
  display_name    = "SAML integration - ${upper(var.env)}"
  owners          = [data.azuread_client_config.current.object_id]
  identifier_uris = local.entity_ids[var.env]

  web {
    redirect_uris = local.redirect_uris[var.env]
  }

  feature_tags {
    enterprise            = true
    gallery               = false
    custom_single_sign_on = true
  }

  app_role {
    allowed_member_types = ["Application", "User"]
    description          = "Default role for standard users used for SAML integration"
    display_name         = "IntegrationUser"
    enabled              = true
    id                   = "GUID HERE"
    value                = "IntegrationUser"
  }
}

resource "azuread_service_principal" "saml_integration_service_principal" {
  count                        = var.env == "prod" ? 0 : 1
  application_id               = azuread_application.saml_integration[0].application_id
  app_role_assignment_required = false
  owners                       = [data.azuread_client_config.current.object_id]

  use_existing                  = true
  preferred_single_sign_on_mode = "saml"

  feature_tags {
    enterprise            = true
    gallery               = false
    custom_single_sign_on = true
  }
}

resource "azuread_service_principal_token_signing_certificate" "signing_certificate" {
  service_principal_id = azuread_service_principal.saml_integration_service_principal[0].id
}

resource "azuread_service_principal_claims_mapping_policy_assignment" "app" {
  count                    = var.env == "prod" ? 0 : 1
  claims_mapping_policy_id = azuread_claims_mapping_policy.integration_claims_mapping[0].id
  service_principal_id     = azuread_service_principal.saml_integration_service_principal[0].id
}

resource "azuread_claims_mapping_policy" "integration_claims_mapping" {
  count        = var.env == "prod" ? 0 : 1
  display_name = "Claims Mapping for ${azuread_application.saml_integration[0].display_name}"
  definition = [
    jsonencode(
      {
        ... hidden
    )
  ]
}

resource "azuread_user" "test_user" {
  count               = var.env == "prod" ? 0 : 1
  user_principal_name = "test-saml-user-${var.env}@contoso.com"
  mail                = "test-saml-user-${var.env}@contoso.com"
  display_name        = "Test Contoso SSO User ${var.env}"
  given_name = "Test"
  surname = "User ${upper(var.env)}"
  password            = "SUBERPASSWORD"
}

resource "azuread_group" "test_app_users_group" {
  count            = var.env == "prod" ? 0 : 1
  display_name     = "Test SAML integration - ${upper(var.env)}"
  owners           = [data.azuread_client_config.current.object_id]
  security_enabled = true
  members = [
    azuread_user.test_user[0].object_id
  ]
}

resource "azuread_app_role_assignment" "test_user_group_assignment" {
  app_role_id         = azuread_service_principal.saml_integration_service_principal[0].app_role_ids["IntegrationUser"]
  principal_object_id = azuread_group.test_app_users_group[0].object_id
  resource_object_id  = azuread_service_principal.saml_integration_service_principal[0].object_id
}

Please note that some values are hidden to do not expose confidential data.


Solution

  • I had add following permissions:

    • Application.ReadWrite.All
    • Policy.ReadWrite.ApplicationConfiguration
    • Policy.Read.All

    But since there is no possibility to do this via Portal (for managed identities) I used script from this article.

    $TenantID = "provide the tenant ID"
    $GraphAppId = "00000003-0000-0000-c000-000000000000"
    $DisplayNameOfMSI = "Provide the Logic App name"
    $PermissionNames = @("Application.ReadWrite.All", "Policy.ReadWrite.ApplicationConfiguration", "Policy.Read.All") 
    
    # Install the module
    Install-Module AzureAD
    
    Connect-AzureAD -TenantId $TenantID
    
    $MSI = (Get-AzureADServicePrincipal -Filter "displayName eq '$DisplayNameOfMSI'")
    Start-Sleep -Seconds 10
    
    $GraphServicePrincipal = Get-AzureADServicePrincipal -Filter "appId eq '$GraphAppId'"
    
    foreach ($PermissionName in $PermissionNames) {
        $AppRole = $GraphServicePrincipal.AppRoles | Where-Object { $_.Value -eq $PermissionName -and $_.AllowedMemberTypes -contains "Application" }
        New-AzureAdServiceAppRoleAssignment -ObjectId $MSI.ObjectId -PrincipalId $MSI.ObjectId -ResourceId $GraphServicePrincipal.ObjectId -Id $AppRole.Id
    }
    
    Write-Output "Permissions added successfully."
    

    enter image description here