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.