212 lines
6.6 KiB
Plaintext
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).
|