Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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                   = "http://${var.prefix}-${var.env}-spn"
  identifier_uris            = ["http://${var.prefix}-${var.env}-spn"]
  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!

like image 355
Abu Belal Avatar asked Jun 05 '20 17:06

Abu Belal


1 Answers

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.

like image 168
Helder Sepulveda Avatar answered Oct 23 '22 01:10

Helder Sepulveda