In this post, we’ll cover the Terraform for in loop construct. Though it performs looping, its primary purpose is really for manipulating data structures. You can do a few things to data structures with it:

  1. Transform: Change the data structure.
  2. Filter: Select only desired items.
  3. Group: Group elements together in a new List by a key.

Input Source and Output Result

The input source and output result can be either be a List or a Map. Then for will either transform and or filter it. Here’s a diagram to help explain:

Simple Transformations Examples

Though the for syntax is pretty straightforward in these examples, it can get confusing without understanding how different types work with it.

List to List

Here’s an example with a List input source:

locals {
  list = ["a","b","c"]
}
output "list" {
  value = [for s in local.list : upper(s)]
}

Returns in:

$ terraform apply
Outputs:

list = ["A", "B", "C"]

A straightforward upper transformation has been done. The resulting value is another List. This is indicated by the surrounding [...] square brackets. Yes, the surrounding brackets indicate what value type is returned. So it’s important to pay attention to what they are.

Map to List

Here’s another example with a Map as the input source:

locals {
  list = {a = 1, b = 2, c = 3}
}
output "result1" {
  value = [for k,v in local.list : "${k}-${v}" ]
}
output "result2" {
  value = [for k in local.list : k ]
}

Results in:

$ terraform apply
Outputs:

result1 = ["a-1", "b-2", "c-3"]
result2 = [1, 2, 3]

So when the input source is a Map, you can access the key and value. The resulting value is another List. This is indicated by the surrounding [...] square brackets again.

List to Map

The examples we’ve been using have a resulting Output type of List due to the [...] square brackets. To return a Map type instead, we would use the {...} curly brackets. Here’s an example:

locals {
  list = ["a","b","c"]
}
output "result" {
  value = {for i in local.list : i => i }
}

Results in:

$ terraform apply
Outputs:

result = {
  "a" = "a"
  "b" = "b"
  "c" = "c"
}

As you can see, the result output type is a Map now. It is important to note that we must provide an expression with the => when returning a Map. Otherwise, you’ll get this error:

Error: Invalid 'for' expression

  on main.tf line 5, in output "result":
   5:   value = {for i in local.list : i }

Map to Map

Now a Map to Map example:

locals {
  list = {a = 1, b = 2, c = 3}
}
output "result" {
  value = {for k,v in local.list : k => v }
}

Results in:

$ terraform apply
Outputs:

result = {
  "a" = 1
  "b" = 2
  "c" = 3
}

Review & Reiterate

The wrapping bracket indicates return value type:

  1. When a for expression is wrapped in square brackets [...], the result type is a List.
  2. When a for expression is wrapped in curly brackets {...}, the result type is a Map.
  3. Also, when returning a Map, the expression elements are separated by =>.

We’ve only shown simple transformations to introduce things. You can do many more transformations with Terraform functions.

Multi-Line Expression

When the transformation logic gets more complicated, it can be nice to write for expressions across multiple lines.

locals {
  list = ["a","b","c"]
}
output "list" {
  value = [
    for s in local.list :
      upper(s)
  ]
}

locals {
  map = {a = 1, b = 2, c = 3}
}
output "map" {
  value = {
    for k,v in local.map :
      k => v
  }
}

Results in:

$ terraform apply
Outputs:

list = ["A", "B", "C"]
map = { "a" = 1, "b" = 2, "c" = 3 }

Simple Filtering Examples

We can also filter the Input to an Output result. This means filtering always results in a collection that is smaller or equal to the original Input size. Filtering is achieved by tacting on the if clause to the end of the transformation expression. Let’s go through some examples:

Filter Simple Number Elements

In this example, we’ll filter for numbers that are less than 3.

locals {
  list = [1,2,3,4,5]
}
output "list" {
  value = [for i in local.list : i if i < 3]
}

Results in:

$ terraform apply
Outputs:

list = [1, 2]

Filter Map Elements

In this example, the elements are Maps. We’ll transform the data to only return the values, but only if the b key’s value is greater than 6.

locals {
  list = [
    {a = 1, b = 5},
    {a = 2, b = 6},
    {a = 3, b = 7},
    {a = 4, b = 8},
  ]
}
output "list" {
  value = [for m in local.list : values(m) if m.b > 6 ]
}

Results in:

$ terraform apply
Outputs:

list = [
  [3,7]
  [4,8],
]

Filter Inconsistent Map Elements

Let’s say the Map elements are a little bit inconsistent, like some of them lack the b key. We can filter for that.

locals {
  list = [
    {a = 1, b = 5},
    {a = 2},
    {a = 3},
    {a = 4, b = 8},
  ]
}
output "list" {
  value = [for m in local.list : m if contains(keys(m), "b") ]
}

Results in:

$ terraform apply
Outputs:

list = [
  {"a" = 1, "b" = 5},
  {"a" = 4, "b" = 8},
]

Splats

Let’s cover splats also. Splats are a shorthand way to write for in expressions. These 2 expressions produce the same result:

for in expression

[for minion in local.minions : minion.name ]

splat expression

local.minions[*].name

Here’s a working example:

locals {
  minions = [{
    name: "bob"
  },{
    name: "kevin",
  },{
    name: "stuart"
  }]
}
output "minions" {
  value = local.minions[*].name
}

Results in:

$ terraform apply
Outputs:

minions = [
  "bob",
  "kevin",
  "stuart",
]

Group Mode

The final form of for we’ll cover is group mode. The syntax is a little weird for this one. You use it by adding ... to the end of the value expression. Example:

locals {
  list = [
    "mr bob",
    "mr kevin",
    "mr stuart",
    "ms anna",
    "ms april",
    "ms mia",
  ]
}
output "list" {
  value = {for s in local.list : substr(s, 0, 2) => s...}
}

Results in:

$ terraform apply
Outputs:

list = {
  "mr" = [
    "mr bob",
    "mr kevin",
    "mr stuart",
  ]
  "ms" = [
    "ms anna",
    "ms april",
    "ms mia",
  ]
}

Group mode works kind of like SQL group by. The key is used as the group by and groups items together in a List.

Summary

In this post, we covered the basics of the for in loop construct. It is useful for data manipulation. In the next post, we’ll cover more practical examples of the for construct.

Want It to be Easier to Work with Terraform?

Check out Terraspace: The Terraform Framework.

The Terraform HCL Language Intro Tutorials