terraform nested dynamic block with nested map
Emily Wong
I'm trying to get tf 0.12.x new dynamic feature to work with a nested map, config is below.
As you can see below (simplified for this) I'm defining all the variables and adding variable required_resource_access which contains a map.
I was hoping to use new dynamic feature to create read this map in a nested dyanmic block.
variable prefix { description = "Prefix to applied to all top level resources" default = "abx"
}
variable suffix { description = "Suffix to applied to all valid top level resources, usually this is 2 letter region code such as we (westeurope), ne (northeurope)." default = "we"
}
variable env { description = "3 letter environment code appied to all top level resources" default = "dev"
}
variable location { description = "Where to create all resources in Azure" default = "westeurope"
}
variable available_to_other_tenants { default = false
}
variable oauth2_allow_implicit_flow { default = true
}
variable public_client { default = false
}
# other option is native
variable application_type { default = "webapp/api"
}
variable required_resource_access { type = list(object({ resource_app_id = string resource_access = object({ id = string type = string }) })) default = [{ resource_app_id = "00000003-0000-0000-c000-000000000000" resource_access = { id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" type = "Role" } }]
}
variable reply_urls { default = []
}
variable group_membership_claims { default = "All"
}
resource "azuread_application" "bootstrap" { name = "${var.prefix}-${var.env}-spn" homepage = "" identifier_uris = [""] reply_urls = var.reply_urls available_to_other_tenants = var.available_to_other_tenants oauth2_allow_implicit_flow = var.oauth2_allow_implicit_flow type = var.application_type group_membership_claims = var.group_membership_claims dynamic "required_resource_access" { for_each = var.required_resource_access content { resource_app_id = required_resource_access.value["resource_app_id"] dynamic "resource_access" { for_each = required_resource_access.value["resource_access"] content { id = resource_access.value["id"] type = resource_access.value["type"] } } } }
}But for reasons beyond my knowledge it keeps giving me this error (notice it's priting it twice as well), I've tried a few other options but this is the closest I managed to get where it would at least give me a meaningful error.
------------------------------------------------------------------------
Error: Invalid index on pe_kubernetes.tf line 24, in resource "azuread_application" "bootstrap": 24: id = resource_access.value["id"] |---------------- | resource_access.value is "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
This value does not have any indices.
Error: Invalid index on pe_kubernetes.tf line 24, in resource "azuread_application" "bootstrap": 24: id = resource_access.value["id"] |---------------- | resource_access.value is "Role"
This value does not have any indices.
Error: Invalid index on pe_kubernetes.tf line 25, in resource "azuread_application" "bootstrap": 25: type = resource_access.value["type"] |---------------- | resource_access.value is "7ab1d382-f21e-4acd-a863-ba3e13f7da61"
This value does not have any indices.
Error: Invalid index on pe_kubernetes.tf line 25, in resource "azuread_application" "bootstrap": 25: type = resource_access.value["type"] |---------------- | resource_access.value is "Role"
This value does not have any indices.Spent the best part of 2 days on this with no luck so any help or pointers would be much appreciated!
11 Answer
I had some time to test my comment...
If I change the resource_access to a list it works.
See code below:
variable required_resource_access { type = list(object({ resource_app_id = string resource_access = list(object({ id = string type = string })) })) default = [{ resource_app_id = "00000003-0000-0000-c000-000000000000" resource_access = [{ id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" type = "Role" }] }]
}
resource "azuread_application" "bootstrap" { name = "test" type = "webapp/api" group_membership_claims = "All" dynamic "required_resource_access" { for_each = var.required_resource_access content { resource_app_id = required_resource_access.value["resource_app_id"] dynamic "resource_access" { for_each = required_resource_access.value["resource_access"] content { id = resource_access.value["id"] type = resource_access.value["type"] } } } }
}And the plan shows:
Terraform will perform the following actions: # azuread_application.bootstrap will be created + resource "azuread_application" "bootstrap" { + application_id = (known after apply) + available_to_other_tenants = false + group_membership_claims = "All" + homepage = (known after apply) + id = (known after apply) + identifier_uris = (known after apply) + name = "test" + oauth2_allow_implicit_flow = true + object_id = (known after apply) + owners = (known after apply) + public_client = (known after apply) + reply_urls = (known after apply) + type = "webapp/api" + oauth2_permissions { + admin_consent_description = (known after apply) ... } + required_resource_access { + resource_app_id = "00000003-0000-0000-c000-000000000000" + resource_access { + id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61" + type = "Role" } } }
Plan: 1 to add, 0 to change, 0 to destroy.I removed a lot of your variables an some of the optional Arguments for azuread_application to keep the code as small as possible, but the same principle applies to your code, use lists on for_each or it will loop on the object properties.