Terraform Nested Dynamic Block with Nested Map

Terraform Nested Dynamic Block with Nested Map

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

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.

Chloe Bennett
Author

Chloe Bennett

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.