Terraform: modules + output from for_each

wiwa1978 picture wiwa1978 · Nov 24, 2020 · Viewed 13.2k times · Source

I have a Terraform script using modules. I want to create multiple resources so I'm using the for_each method.

Below is my variable configuration:

variable bridge_domains {
  description = "Bridge Domain"
  type     = map
  default  = {
    bd1 = {
      name  = "BD1",
    },
    bd2 = {
      name    = "BD2"
    }
  }
}

In the root main.tf file, I'm looping over that variable using for_each:

module "schema_template_bd" {
  source = "./modules/schema_template_bd"
  for_each     =    var.bridge_domains
  
  schema       =    module.tenant.mso_schema.id 
  template     =    var.template

  bd           =    each.value.name
}

Then in the modules/schema_template_bd file I have the following:

resource "mso_schema_template_bd" "bd" {
  schema_id              =      var.schema
  template_name          =      var.template
  name                   =      var.bd
}

The module has an output where I have defined the following:

output "mso_bd" {
  value = mso_schema_template_bd.bd[*]
}

The idea is to output the names from all the objects that were created. So I have defined an output.tf file (at root level) containing the following code:

output "bd_name" {
  value = module.schema_template_bd.mso_bd.*.name
}

I always get:

This object does not have an attribute named "name".

Normally the bd object has a name so the error has to do with a wrong syntax in my view.

Answer

Martin Atkins picture Martin Atkins · Nov 24, 2020

The [*] and .* operators are intended for use with lists only. Because this resource uses for_each rather than count, its value in other expressions is a map, not a list.

To make your configuration work you'll need to decide whether it's better to return a map of names where the keys are the var.bridge_domains keys, or to return just a set of ids where the caller then couldn't determine which name belongs to which of the elements of var.bridge_domains:

output "bd_name" {
  value = tomap({
    for k, bd in mso_schema_template_bd.bd : k => bd.name
  })
}

OR

output "bd_name" {
  value = toset([
    for bd in mso_schema_template_bd.bd : bd.name
  ])
}

This uses for expressions, which are the more general counterpart of splat expressions that work with collections of any type and which can produce both sequences and mappings as their result, whereas splat expressions work only with lists.