Terraform List Comprehensions
30 September, 2020Terraform 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!