Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azuread_application_api_access not allowing flexibility when creating multiple permissions from MicrosoftGraph #1247

Closed
bdorplatt opened this issue Nov 9, 2023 · 4 comments

Comments

@bdorplatt
Copy link

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritise this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritise the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Terraform (and AzureAD Provider) Version

Terraform: v1.3.7
Provider: v2.45.0

Affected Resource(s)

  • azuread_application_api_access

Terraform Configuration Files

Old method with resource_access blocks within azuread_application resource

       dynamic "resource_access" {
       for_each = var.spn_app_permissions 
        content{
        id   = azuread_service_principal.msgraph.app_role_ids[resource_access.value.id] 
        type = "Role"
        }
       }
       dynamic "resource_access" {
       for_each = var.spn_delegated_permissions
        content{
        id   = azuread_service_principal.msgraph.oauth2_permission_scope_ids[resource_access.value.id] 
        type = "Scope"
        }
       }

Using the new separate resource azuread_application_api_access

resource "azuread_application_api_access" "msgraph_app" {
   for_each = var.spn_app_permissions
  application_id = azuread_application.app_reg.id
  api_client_id  = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]
  role_ids  =  [
    azuread_service_principal.msgraph.app_role_ids[each.value]
  ]
}

resource "azuread_application_api_access" "msgraph_delegated" {
   for_each = var.spn_delegated_permissions
  application_id = azuread_application.app_reg.id
  api_client_id  = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]
  scope_ids  =  [
    azuread_service_principal.msgraph.oauth2_permission_scope_ids[each.value]
  ]
}

We also tried this to pull from the separate lists into the same resource block but it is still trying to create multiple conflicting resources

locals {
  my_product = {for val in setproduct(var.spn_app_permissions, var.spn_delegated_permissions):
                "${val[0]}-${val[1]}" => val}  
}
resource "azuread_application_api_access" "msgraph_app" {
   for_each = local.my_product
  application_id = azuread_application.app_reg.id
  api_client_id  = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]
  role_ids  =  [
    azuread_service_principal.msgraph.app_role_ids[each.value[0]]
  ]
  scope_ids  =  [
    azuread_service_principal.msgraph.oauth2_permission_scope_ids[each.value[1]]
  ]
}

Debug Output

Panic Output

Expected Behavior

Permissions are added while iterating through a list of needed permissions from a variable.

Actual Behavior

It seems that you can only have 1 resource created for MicrosoftGraph with all role_id and scope_id underneath it. This isn't allowing multiple azuread_application_api_access resources for the same API to be created. With the old nested resource_access block, we could iterate through two separate lists for the delegated and application permissions as in the above example.

Error: A resource with the ID "/applications/11111111-1111-1111-1111-111111111111/apiAccess/00000003-0000-0000-c000-000000000000" already exists - to be managed via Terraform this resource needs to be imported into the State. Please see the resource documentation for "azuread_application_api_access" for more information.

Steps to Reproduce

  1. terraform apply

Important Factoids

References

  • #0000
@manicminer
Copy link
Contributor

Hi @bdorplatt, thanks for raising this issue. You are correct, the azuread_application_api_access resource intentionally manages all application roles and delegated scopes for a particular resource API. As this new resource does not use blocks, you can't iterate in the same way as you might with dynamic blocks in say the azuread_application resource. Instead, have you tried an inline for expression?

locals {
  my_roles = [
    "Application.Read.All",
    "Group.Read.All",
    "User.Read.All",
  ]

  my_scopes = [
    "email",
    "openid",
    "User.ReadWrite",
  ]
}

data "azuread_application_published_app_ids" "well_known" {}

data "azuread_service_principal" "msgraph" {
  client_id = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]
}

resource "azuread_application_registration" "example" {
  display_name = "my-application-2n3hf4gQ"
}

resource "azuread_application_api_access" "example_msgraph" {
  application_id = azuread_application_registration.example.id
  api_client_id  = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]

  role_ids  = [for v in local.my_roles: data.azuread_service_principal.msgraph.app_role_ids[v]]
  scope_ids = [for v in local.my_scopes: data.azuread_service_principal.msgraph.oauth2_permission_scope_ids[v]]
}

@bdorplatt
Copy link
Author

@manicminer Thank you for the clarification on this and the suggestion to use an inline for expression. This indeed worked!

Now we are trying to add an API access for a permission scope in the app itself and can't determine the syntax (or what to point to for referencing it correctly. The following is what we've tried and it doesn't work:

resource "azuread_application_permission_scope" "access_as_user_app" {
  count          = var.spn_api_access_as_user_app ? 1 : 0
  admin_consent_description  = "Allow the application to access ${var.application} on behalf of the signed-in user."
  admin_consent_display_name = "Access the API for ${var.application} on behalf of the signed-in user."
  application_id = azuread_application.app_reg.id
  scope_id       = random_uuid.scope_id_access_as_user.result
  type           = "Admin"
  value          = "access_as_user"
  user_consent_description   = "Allow the application to access ${var.application} on your behalf."
  user_consent_display_name  = "Access the API for ${var.application} on your behalf."
}

resource "azuread_application_api_access" "access_app" {
  application_id = azuread_application.app_reg.id
  api_client_id  = azuread_application.app_reg.id 
  scope_ids  =  [
    [azuread_application_permission_scope.access_as_user_app[0].value]
  ]
}

This results in:
Inappropriate value for attribute "scope_ids": element 0: string required.

We had previously accomplished this as follows with provisioners but are moving away from that since the new resources are available:

# Add Delegated API Permission for access_as_user 
resource "null_resource" "api_app_perm" {
  count = var.spn_api_access_as_user_app ? 1 : 0
  provisioner "local-exec" {
    interpreter = ["PowerShell", "-Command"]
    command = <<-EOT
    az ad app permission add --id ${azuread_application.app_reg.client_id} --api ${azuread_application.app_reg.client_id} --api-permissions "${random_uuid.scope_id_access_as_user.result}=Scope" 
    EOT
  }
depends_on = [
    null_resource.log_in,
    azuread_application_identifier_uri.app,
  ]
}

resource "null_resource" "api_app_perm_grant" {
  count = var.spn_api_access_as_user_app  ? 1 : 0
  provisioner "local-exec" {
    interpreter = ["PowerShell", "-Command"]
    command = <<-EOT
    az ad app permission grant --id ${azuread_application.app_reg.client_id} --api ${azuread_application.app_reg.client_id} --scope "access_as_user" --consent-type AllPrincipals
    EOT
  }
depends_on = [
    null_resource.api_app_perm,
  ]
}

@bdorplatt
Copy link
Author

After some trial and error, we were able to get this to work as expected using the following:

resource "azuread_application_api_access" "access_app" {
  application_id = azuread_application.app_reg.id
  api_client_id  = azuread_application.app_reg.client_id 
  scope_ids  =  [
    azuread_application_permission_scope.access_as_user_app[0].scope_id
  ]
}

@manicminer
Copy link
Contributor

Great to hear! I hope this helps with usage of the new resources. Since there's no further action for maintainers, I'm going to go ahead and close this one out. If you need further assistance with the provider, I would recommend our community resources, as well as our Slack community to which you can find a link in the project readme.

@manicminer manicminer closed this as not planned Won't fix, can't repro, duplicate, stale Nov 16, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 16, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants