skip to Main Content

Given the following terraform script, how can I grant the app_registration permission to the API it defines?

I’ve tried adding a required_resource_access section but I cannot set resource_app_id to the id of my app because it gives a circular reference error.

data "azuread_client_config" "current" {}
data "azuread_application_published_app_ids" "well_known" {}

resource "random_uuid" "app_scope_id" {}

resource "azuread_service_principal" "msgraph" {
  application_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph
  use_existing   = true
}

resource "azuread_application" "app_registration" {
  display_name     = "My app name"
  identifier_uris  = ["api://${var.environment}productname"]
  owners           = [data.azuread_client_config.current.object_id]
  sign_in_audience = "AzureADMultipleOrgs"

  api {
    mapped_claims_enabled          = true
    # requested_access_token_version = 2

    oauth2_permission_scope {
      admin_consent_description  = "Allow the application to access a user's name and email address."
      admin_consent_display_name = "Access user name and email address"
      enabled                    = true
      id                         = random_uuid.app_scope_id.result
      type                       = "User"
      user_consent_description   = "Allow the application to access your name and email address."
      user_consent_display_name  = "Access your name and email address"
      value                      = "access_as_user"
    }
  }

  required_resource_access {
    resource_app_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph // "00000003-0000-0000-c000-000000000000"

    resource_access {
      id   =  azuread_service_principal.msgraph.oauth2_permission_scope_ids["User.Read"] // "e1fe6dd8-ba31-4d61-89e7-88639da4683d" 
      type = "Scope"
    }

    resource_access {
      id   = azuread_service_principal.msgraph.oauth2_permission_scope_ids["email"] // "37f7f235-527c-4136-accd-4a02d197296e"
      type = "Scope"
    }

    resource_access {
      id   = azuread_service_principal.msgraph.oauth2_permission_scope_ids["openid"] // "64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0"
      type = "Scope"
    }
  }

  web {
    homepage_url  = "https://${var.environment}productname.azurewebsites.net"
    logout_url    = "https://${var.environment}productname.azurewebsites.net/logout"
    redirect_uris = ["https://${var.environment}productname.azurewebsites.net//authentication/login-callback"]

    implicit_grant {
      access_token_issuance_enabled = true
      id_token_issuance_enabled     = true
    }
  }
}

The steps in Azure would be

  1. Go to portal.azure.com
  2. Switch to the directory where your app is
  3. Select "Azure Active Directory" from the burger menu at the top left of the page
  4. Click "App registrations"
  5. Click the application
  6. Click "API permissions"
  7. Click "Add a permission"
  8. Select "My APIs" tab
  9. Click your app in the list
  10. Tick the permission "access_as_user"
  11. Click "Add permissions"

I would also like to set identifier_uris to the apps UUID, but that doesn’t seem possible either.

enter image description here

2

Answers


  1. Chosen as BEST ANSWER

    There is a work-around I can employ.

    The first time the script is executed for a new environment, it must be run twice. Once to create everything, and a second time to add the permissions.

    Terraform doesn't have a conditional block of code so we can't optionally include a block, but we can use a ternary statement to make a local variable an empty array or an array with a single element, and then use for_each

    First, add a variable

    variable "update_roles" {
      type = string
    }
    

    Next, define a data-source to retrieve the existing application_id, but only if the parameters say we should (i.e. it is the 2nd+ execution of the script).

    data "azuread_application" "app_registration" {
      display_name = local.app_display_name
      count        = var.update_roles == "y" ? 1 : 0
    }
    

    Finally, use a dynamic block with a for_each that will iterate over 1 element (if the 2nd+ execution of the script) or over an empty array (i.e. not execute).

      dynamic "required_resource_access" {
        for_each = var.update_roles == "y" ? [1] : [] 
        content {
          resource_app_id = data.azuread_application.app_registration[0].application_id
    
          resource_access {
            id   =  random_uuid.app_scope_id.result
            type = "Scope"
          }
        }
      }
    

    First time you run your script

    terraform apply --var update_roles=n
    

    All subsequent runs

    terraform apply --var update_roles=n
    

    If anyone can give an answer that doesn't require at least two executions for a new environment, I will be happy to accept it.


  2. As you used, adding the required_resource_access block to the azuread_application resource grants the "app registration permission" to the API it defines. You can specify the resource_app_id as the application id of the azuread_service_principal resource that represents the API in this block.

    So, I’ve given resource app id as:

     resource_app_id = azuread_service_principal.msgraph.application_id
    

    I modified your script as below and it registered an app successfully with the required API permissions.

    main.tf:

    provider "azurerm"{
    features{}
    }
    data "azuread_client_config" "current" {}
    data "azuread_application_published_app_ids" "well_known" {}
    
    resource "random_uuid" "app_scope_id" {}
    
    resource "azuread_service_principal" "msgraph" {
      application_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph
      use_existing   = true
    }
    resource "azuread_application" "app_registration" {
      display_name     = "My app name"
      identifier_uris  = ["api://${var.environment}productname"]
      owners           = [data.azuread_client_config.current.object_id]
      sign_in_audience = "AzureADMultipleOrgs"
    
      api {
        mapped_claims_enabled          = true
        # requested_access_token_version = 2
    
        oauth2_permission_scope {
          admin_consent_description  = "Allow the application to access a user's name and email address."
          admin_consent_display_name = "Access user name and email address"
          enabled                    = true
          id                         = random_uuid.app_scope_id.result
          type                       = "User"
          user_consent_description   = "Allow the application to access your name and email address."
          user_consent_display_name  = "Access your name and email address"
          value                      = "access_as_user"
        }
      }
    
      required_resource_access {
        resource_app_id = azuread_service_principal.msgraph.application_id
    
        resource_access {
          id   = azuread_service_principal.msgraph.oauth2_permission_scope_ids["User.Read"]
          type = "Scope"
        }
    
        resource_access {
          id   = azuread_service_principal.msgraph.oauth2_permission_scope_ids["email"]
          type = "Scope"
        }
    
        resource_access {
          id   = azuread_service_principal.msgraph.oauth2_permission_scope_ids["openid"]
          type = "Scope"
        }
      }
    
      web {
        homepage_url  = "https://${var.environment}productname.azurewebsites.net"
        logout_url    = "https://${var.environment}productname.azurewebsites.net/logout"
        redirect_uris = ["https://${var.environment}productname.azurewebsites.net//authentication/login-callback"]
    
        implicit_grant {
          access_token_issuance_enabled = true
          id_token_issuance_enabled     = true
        }
      }
    }
    

    var.tf:

    variable "environment"{}
    

    Executed terraform init:

    enter image description here

    Validated the configuration using terraform validate:

    enter image description here

    Executed terraform plan:

    enter image description here

    Executed terraform apply:

    enter image description here

    Deployment succeeded and got the expected output:

    enter image description here

    Note: Regarding random_uuid, it is not possible to set the identifier_uris to the app's UUID directly in Terraform. However, you can generate a UUID with the random_uuid resource and use it in the identifier_uris list.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search