Terraform for-each with list of objects

14,304

What you are looking for is a dynamic block: https://www.terraform.io/docs/configuration/expressions.html#dynamic-blocks

Original:

site_associations {
    site_id =  each.value.site
}

Dynamic:

dynamic "site_associations"{
    for_each = each.value.site
    content {
        site_id = site_associations.value
    }
}

The dynamic block allows you to create another for_each loop over the each.value.site.

Note: the value inside the dynamic block is referenced using the block name site_associations.value

Whole main.tf:

variable tenants {
  description = "Map of project names to configuration."
  type = list(object({
    name  = string
    dname = string
    desc  = string
    site  = list(string)
  }))
  default = [{
      name  = "Tenant-1",
      dname = "Tenant-1",
      desc  = "Test Tenant 1",
      site  = ["site1", "site2"]
    },
    {
      name  = "Tenant-2",
      dname = "Tenant-2",
      desc  = "Test Tenant 2",
      site  = ["site1"]
    }]
}

resource "mso_tenant" "restenant" {
  for_each = {for i, v in var.tenants:  i => v}
    name         = each.value.name
    display_name = each.value.dname
    description  = each.value.desc
    dynamic "site_associations"{
    for_each = each.value.site
    content {
      site_id = site_associations.value
    }
    }

}

Plan Output:


  # mso_tenant.restenant["0"] will be created
  + resource "mso_tenant" "restenant" {
      + description  = "Test Tenant 1"
      + display_name = "Tenant-1"
      + id           = (known after apply)
      + name         = "Tenant-1"

      + site_associations {
          + aws_access_key_id         = (known after apply)
          + aws_account_id            = (known after apply)
          + aws_secret_key            = (known after apply)
          + azure_access_type         = (known after apply)
          + azure_active_directory_id = (known after apply)
          + azure_application_id      = (known after apply)
          + azure_client_secret       = (known after apply)
          + azure_subscription_id     = (known after apply)
          + is_aws_account_trusted    = (known after apply)
          + site_id                   = "site1"
          + vendor                    = (known after apply)
        }
      + site_associations {
          + aws_access_key_id         = (known after apply)
          + aws_account_id            = (known after apply)
          + aws_secret_key            = (known after apply)
          + azure_access_type         = (known after apply)
          + azure_active_directory_id = (known after apply)
          + azure_application_id      = (known after apply)
          + azure_client_secret       = (known after apply)
          + azure_subscription_id     = (known after apply)
          + is_aws_account_trusted    = (known after apply)
          + site_id                   = "site2"
          + vendor                    = (known after apply)
        }

      + user_associations {
          + user_id = (known after apply)
        }
    }

  # mso_tenant.restenant["1"] will be created
  + resource "mso_tenant" "restenant" {
      + description  = "Test Tenant 2"
      + display_name = "Tenant-2"
      + id           = (known after apply)
      + name         = "Tenant-2"

      + site_associations {
          + aws_access_key_id         = (known after apply)
          + aws_account_id            = (known after apply)
          + aws_secret_key            = (known after apply)
          + azure_access_type         = (known after apply)
          + azure_active_directory_id = (known after apply)
          + azure_application_id      = (known after apply)
          + azure_client_secret       = (known after apply)
          + azure_subscription_id     = (known after apply)
          + is_aws_account_trusted    = (known after apply)
          + site_id                   = "site1"
          + vendor                    = (known after apply)
        }

      + user_associations {
          + user_id = (known after apply)
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.
Share:
14,304
wiwa1978
Author by

wiwa1978

Updated on June 14, 2022

Comments

  • wiwa1978
    wiwa1978 almost 2 years

    I have the following variable in variables.tf file:

    variable tenants {
      description = "Map of project names to configuration."
      type = list(object({
        name  = string
        dname = string
        desc  = string
        site  = list(string)
      }))
      default = [{
          name  = "Tenant-1",
          dname = "Tenant-1",
          desc  = "Test Tenant 1",
          site  = ["site1", "site2"]
        },
        {
          name  = "Tenant-2",
          dname = "Tenant-2",
          desc  = "Test Tenant 2",
          site  = ["site1"]
        }]
    }
    

    In my main.tf file, I would like to loop over this list. I have the following code in main.tf file:

    resource "mso_tenant" "restenant" {
      for_each = {for i, v in var.tenants:  i => v}
        name         = each.value.name
        display_name = each.value.dname
        description  = each.value.desc
        site_associations {
          site_id =  each.value.site
      }
    }
    

    So the end result should be that 2 tenants get created with the attributes as specified in the variable file. So tenant1 will have 2 site_associations and tenant2 will have 1 association once created.

    Result should be:

    name         = "Tenant-1"
    display_name = "Tenant-1"
    description  = "Test Tenant 1"
    site_associations {
      site_id = site1
      site_id = site2
    }
    

    and

    name         = "Tenant-2"
    display_name = "Tenant-2"
    description  = "Test Tenant 2"
    site_associations {
       site_id = site1
    }
    

    I tried the following:

    resource "mso_tenant" "restenant" {
      for_each = {for i, v in var.tenants:  i => v}
        name         = each.value.name
        display_name = each.value.dname
        description  = each.value.desc
        site_associations {
          site_id =  each.value.site
      }
    }
    

    This works for the name, dname and desc but it does not iterate over the site variable (which is a list). This results in the error message:

    each.value.site is list of string with 1 element Inappropriate value for attribute "site_id": string required.

    Tried to solve as follows:

    resource "mso_tenant" "restenant" {
      for_each = {for i, v in var.tenants:  i => v}
        
        name         = each.value.name
        display_name = each.value.dname
        description  = each.value.desc
        site_associations {
          site_id = [for site in each.value.site: site]
      }
    }
    

    but this also gives:

    each.value.site is list of string with 2 elements Inappropriate value for attribute "site_id": string required.

    • Grzegorz Oledzki
      Grzegorz Oledzki over 3 years
    • Marcin
      Marcin over 3 years
      Use for_each = { for idx, v in var.tenants: idx=>v}
    • wiwa1978
      wiwa1978 over 3 years
      I edited the question to make it more clear. Your suggestion is working for the non-list items but does not iterate over the list itself (e.g. site)