I'm trying to create an Azure application in AD using Terraform.
resource "azuread_application" "my_ad_app" {
display_name = "my_app"
#API Permissions
required_resource_access {
resource_app_id = "0000000-000000000000" # Microsoft Graph
resource_access {
id = "df021288-bdef-9214" # User.Read.All
type = "Role"
}
resource_access {
id = "18a4783c662c884" # User.Read.All
type = "Role"
}
resource_access {
id = "9a5d68c3a30" # Application.Read.All
type = "Role"
}
I will be creating some more Azure AD applications with the same API permissions assigned. There are many API permissions that will be assigned and they are the same for all Azure AD applications that I will be creating. Since the API permissions are same for all the Azure AD applications which I will be creating for, I want to assign the entire required_resource_access
as a variable.
Something like this.
#API Permissions
required_resource_access {
resource_app_id = "00000003-0000-0000-c000-000000000000" # Microsoft Graph
resource_access {
assign the entire part as a variable here? Example: var.ms_graph_api_permission
}
I tried to add the below as a variable but it doesn't seem to work.
variable "ms_graph_api_permission" {
type = list(string)
default =
resource_access {
id = "df021288f22de89214"
type = "Role"
}
resource_access {
id = "18a4783e5662c884"
type = "Role"
}
resource_access {
id = "9a5d68ac3a30"
type = "Role"
}
}"
This doesn't seems to work. Any idea how to automate this?
I tried using locals.
locals {
resource_access = [
{
id = "df021288-bdde89214" # User.Read.All
type = "Role"
},
{
id = "18a4783c-85e5662c884" # User.Read.All
type = "Role"
}
]
}
resource "azuread_application" "my_ad_app" {
display_name = "my_app"
#API Permissions
required_resource_access {
for_each = {
for index, az_app_id in local.resource_access:
az_app_id.id => az_app_id
}
resource_app_id = "000000000000" # Microsoft Graph
resource_access = How should I pass values here using locals??
An important thing to note to start here is that nested blocks like resource_access
are not plain data that you can just pass in a variable: they are static configuration constructs declared by the provider in its schema. Therefore you can't "just" pass a whole set of blocks through a variable, but you can pass some data that would be used to populate those blocks, and ask the Terraform language to dynamically generate resource_access
blocks for you based on that data. The rest of this answer is an example of how you can do that.
First, the variable declaration:
variable "ms_graph_api_permission" {
type = list(object({
id = string
type = string
}))
}
This declares that var.ms_graph_api_permission
is a list of objects where each object must have two attributes -- id
and type
-- that are both strings. This seems to match the shape of the data you want to provide through the resource_access
blocks.
A value of that type would look like what you tried with a local value:
[
{
id = "df021288-bdde89214" # User.Read.All
type = "Role"
},
{
id = "18a4783c-85e5662c884" # User.Read.All
type = "Role"
},
]
You can use an expression like the above both as the default value for the variable and in the module
block that calls the module, when choosing a value for this variable.
Inside the resource block itself you'll need to ask Terraform to generate one resource_access
block for each element of that list, using a dynamic
block:
resource "azuread_application" "my_ad_app" {
display_name = "my_app"
#API Permissions
required_resource_access {
resource_app_id = "0000000-000000000000" # Microsoft Graph
dynamic "resource_access" {
for_each = var.ms_graph_api_permission
content {
id = resource_access.value.id
type = resource_access.value.type
}
}
}
}
A dynamic "resource_access"
block specifies a rule for generating zero or more resource_access
blocks based on some other collection, which in this case is var.ms_graph_api_permission
. The content
block describes how to generate the body of each of the blocks, where resource_access
is a local symbol referring to the current element of var.ms_graph_api_permission
.
Terraform Core will dynamically expand this to a series of individual resource_access
blocks before passing the configuration to the provider, so from the provider's perspective this is equivalent to you having written out a series of static resource_access
blocks as in your original example.