terraform/website/docs/language/expressions/for.mdx

212 lines
6.6 KiB
Plaintext

---
page_title: For Expressions - Configuration Language
description: >-
For expressions transform complex input values into complex output values.
Learn how to filter inputs and how to group results.
---
# `for` Expressions
A _`for` expression_ creates a complex type value by transforming
another complex type value. Each element in the input value
can correspond to either one or zero values in the result, and an arbitrary
expression can be used to transform each input element into an output element.
For example, if `var.list` were a list of strings, then the following expression
would produce a tuple of strings with all-uppercase letters:
```hcl
[for s in var.list : upper(s)]
```
This `for` expression iterates over each element of `var.list`, and then
evaluates the expression `upper(s)` with `s` set to each respective element.
It then builds a new tuple value with all of the results of executing that
expression in the same order.
## Input Types
A `for` expression's input (given after the `in` keyword) can be a list,
a set, a tuple, a map, or an object.
The above example showed a `for` expression with only a single temporary
symbol `s`, but a `for` expression can optionally declare a pair of temporary
symbols in order to use the key or index of each item too:
```hcl
[for k, v in var.map : length(k) + length(v)]
```
For a map or object type, like above, the `k` symbol refers to the key or
attribute name of the current element. You can also use the two-symbol form
with lists and tuples, in which case the additional symbol is the index
of each element starting from zero, which conventionally has the symbol name
`i` or `idx` unless it's helpful to choose a more specific name:
```hcl
[for i, v in var.list : "${i} is ${v}"]
```
The index or key symbol is always optional. If you specify only a single
symbol after the `for` keyword then that symbol will always represent the
_value_ of each element of the input collection.
## Result Types
The type of brackets around the `for` expression decide what type of result
it produces.
The above example uses `[` and `]`, which produces a tuple. If you use `{` and
`}` instead, the result is an object and you must provide two result
expressions that are separated by the `=>` symbol:
```hcl
{for s in var.list : s => upper(s)}
```
This expression produces an object whose attributes are the original elements
from `var.list` and their corresponding values are the uppercase versions.
For example, the resulting value might be as follows:
```hcl
{
foo = "FOO"
bar = "BAR"
baz = "BAZ"
}
```
A `for` expression alone can only produce either an object value or a tuple
value, but Terraform's automatic type conversion rules mean that you can
typically use the results in locations where lists, maps, and sets are expected.
## Filtering Elements
A `for` expression can also include an optional `if` clause to filter elements
from the source collection, producing a value with fewer elements than
the source value:
```
[for s in var.list : upper(s) if s != ""]
```
One common reason for filtering collections in `for` expressions is to split
a single source collection into two separate collections based on some
criteria. For example, if the input `var.users` is a map of objects where the
objects each have an attribute `is_admin` then you may wish to produce separate
maps with admin vs non-admin objects:
```hcl
variable "users" {
type = map(object({
is_admin = bool
}))
}
locals {
admin_users = {
for name, user in var.users : name => user
if user.is_admin
}
regular_users = {
for name, user in var.users : name => user
if !user.is_admin
}
}
```
## Element Ordering
Because `for` expressions can convert from unordered types (maps, objects, sets)
to ordered types (lists, tuples), Terraform must choose an implied ordering
for the elements of an unordered collection.
For maps and objects, Terraform sorts the elements by key or attribute name,
using lexical sorting.
For sets of strings, Terraform sorts the elements by their value, using
lexical sorting.
For sets of other types, Terraform uses an arbitrary ordering that may change
in future versions of Terraform. For that reason, we recommend converting the
result of such an expression to itself be a set so that it's clear elsewhere
in the configuration that the result is unordered. You can use
[the `toset` function](/language/functions/toset)
to concisely convert a `for` expression result to be of a set type.
```hcl
toset([for e in var.set : e.example])
```
## Grouping Results
If the result type is an object (using `{` and `}` delimiters) then normally
the given key expression must be unique across all elements in the result,
or Terraform will return an error.
Sometimes the resulting keys are _not_ unique, and so to support that situation
Terraform supports a special _grouping mode_ which changes the result to support
multiple elements per key.
To activate grouping mode, add the symbol `...` after the value expression.
For example:
```hcl
variable "users" {
type = map(object({
role = string
}))
}
locals {
users_by_role = {
for name, user in var.users : user.role => name...
}
}
```
The above represents a situation where a module expects a map describing
various users who each have a single "role", where the map keys are usernames.
The usernames are guaranteed unique because they are map keys in the input,
but many users may all share a single role name.
The `local.users_by_role` expression inverts the input map so that the keys
are the role names and the values are usernames, but the expression is in
grouping mode (due to the `...` after `name`) and so the result will be a
map of lists of strings, such as the following:
```hcl
{
"admin": [
"ps",
],
"maintainer": [
"am",
"jb",
"kl",
"ma",
],
"viewer": [
"st",
"zq",
],
}
```
Due to [the element ordering rules](#element-ordering), Terraform will sort
the users lexically by username as part of evaluating the `for` expression,
and so the usernames associated with each role will be lexically sorted
after grouping.
## Repeated Configuration Blocks
The `for` expressions mechanism is for constructing collection values from
other collection values within expressions, which you can then assign to
individual resource arguments that expect complex values.
Some resource types also define _nested block types_, which typically represent
separate objects that belong to the containing resource in some way. You can't
dynamically generate nested blocks using `for` expressions, but you _can_
generate nested blocks for a resource dynamically using
[`dynamic` blocks](/language/expressions/dynamic-blocks).