Velvet Star Monitor

Standout celebrity highlights with iconic style.

general

terraform nested dynamic block with nested map

Writer 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!

1

1 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.

3

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.