| --- |
| 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. We recommend converting the expression result into a set to make it 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). |