djmetzle.io

Terraform List Comprehensions

30 September, 2020

Terraform list comprehensions are very powerful! They can be useful to craft custom outputs.

It took me some time to really grok the way these work. Hopefully, this can help someone else out in the future.

Lets see an example terraform config:

locals {
  list_of_keys = [ "a", "b", "c" ]
  map_of_maps = {
    a = {
      id = "foo"
      some_property = 123
      another_property = "bam"
    },
    b = {
      id = "bar"
      some_property = 456
      another_property = "bang"
    },
    c = {
      id = "baz"
      some_property = 789
      another_property = "whiz"
    }
  }
}

In this example, our map_of_maps local could be resources or modules created by the rest of your terraform module.

Lets add some outputs to see that terraform understands these:

output "list" {
  value = local.list_of_keys
}

output "map" {
  value = local.map_of_maps
}

Running this configuration gives us the following output:

$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

list = [
  "a",
  "b",
  "c",
]
map = {
  "a" = {
    "another_property" = "bam"
    "id" = "foo"
    "some_property" = 123
  }
  "b" = {
    "another_property" = "bang"
    "id" = "bar"
    "some_property" = 456
  }
  "c" = {
    "another_property" = "whiz"
    "id" = "baz"
    "some_property" = 789
  }
}

But perhaps we don't want to expose the entire map_of_maps as an output. Maybe it contains some sensitive fields that we dont want exposed as state.

We can slice and dice this output freely using, you guessed it, list comprehensions!

Lets add a new output to curate these:

output "as_list" {
  value = [
    for key, value in local.map_of_maps :
    {
      id = value.id
      new_property = value.another_property
    }
  ]
}

Which gives us the following outputs:

$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

as_list = [
  {
    "id" = "foo"
    "new_property" = "bam"
  },
  {
    "id" = "bar"
    "new_property" = "bang"
  },
  {
    "id" = "baz"
    "new_property" = "whiz"
  },
]

We can map from one map to another as well!

Lets add:

output "as_map" {
  value = {
    for key, value in local.map_of_maps :
    key => {
      id = value.id
      new_property = value.another_property
    }
  }
}

Now we'll get a map in our output:

$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

as_map = {
  "a" = {
    "id" = "foo"
    "new_property" = "bam"
  }
  "b" = {
    "id" = "bar"
    "new_property" = "bang"
  }
  "c" = {
    "id" = "baz"
    "new_property" = "whiz"
  }
}

We can do even more sophisticated transformations using these comprehensions:

output "from_keys" {
  value = {
    for key in local.list_of_keys :
    key => {
      id = local.map_of_maps[key].id
      new_property = local.map_of_maps[key].another_property
    }
  }
}

Notice we're using keys from the list_of_keys local to index into the map!

This gives us the following output:

$ terraform apply

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

from_keys = {
  "a" = {
    "id" = "foo"
    "new_property" = "bam"
  }
  "b" = {
    "id" = "bar"
    "new_property" = "bang"
  }
  "c" = {
    "id" = "baz"
    "new_property" = "whiz"
  }
}

Be careful with this approach though. If we define a key that doesn't exist in the map, we'll get an error:

locals {
  list_of_keys = [ "a", "b", "c", "d" ]
  map_of_map = { ... }
}
$ terraform apply

Error: Invalid index

  on main.tf line 26, in output "from_keys":
  26:       id = local.map_of_maps[key].id
    |----------------
    | local.map_of_maps is object with 3 attributes

The given key does not identify an element in this collection value.


Error: Invalid index

  on main.tf line 27, in output "from_keys":
  27:       new_property = local.map_of_maps[key].another_property
    |----------------
    | local.map_of_maps is object with 3 attributes

The given key does not identify an element in this collection value.

Hopefully this helps to understand how the for expression can be useful to craft outputs.

For some further reading, see the Terraform Docs about for expressions.

Cheers!