website: Draw better attention to for and for_each patterns

When helping folks in the community forum, I commonly see questions around
more complex patterns in transforming deep data structures into different
shapes to work with for_each. We have examples of these patterns in the
docs for the functions that they rely on, but they were not previously
very discoverable in the main configuration language documentation
sections.

Here I've moved the "Using Expressions in for_each" subsection on the
Resources page above some of the other sub-sections to hopefully make it
easier to see, and written out in more detail the two specific patterns
that answer a significant number of for_each-related user questions in
the hope that readers will be more likely to realize that the links are
relevant to what their goals.

I also added some more elaboration about the behavior of converting from
list to set in the "Using Sets" subsection, because this feature is often
a user's first encounter with the set data type and I've inferred from
some of the questions I've answered that a number of Terraform users don't
have prior experience with set data types in other languages to draw
assumptions from.

Finally, I added some similar links to the for_each patterns within the
for expression documentation itself, to try to make those examples more
visible to those who might be discovering the documentation in a different
sequence, e.g. by following a deep link shared in an answer to a question
in the community forum.
This commit is contained in:
Martin Atkins 2020-06-01 11:23:56 -07:00
parent ded30b6836
commit 67311f73fd
2 changed files with 58 additions and 21 deletions

View File

@ -578,6 +578,17 @@ together results that have a common key:
{for s in var.list : substr(s, 0, 1) => s... if s != ""} {for s in var.list : substr(s, 0, 1) => s... if s != ""}
``` ```
For expressions are particularly useful when combined with other language
features to combine collections together in various ways. For example,
the following two patterns are commonly used when constructing map values
to use with [resource `for_each`](./resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings):
* Transform a multi-level nested structure into a flat list by
[using nested `for` expressions with the `flatten` function](./functions/flatten.html#flattening-nested-structures-for-for_each).
* Produce an exhaustive list of combinations of elements from two or more
collections by
[using the `setproduct` function inside a `for` expression](./functions/setproduct.html#finding-combinations-for-for_each).
## Splat Expressions ## Splat Expressions
A _splat expression_ provides a more concise way to express a common A _splat expression_ provides a more concise way to express a common

View File

@ -376,6 +376,27 @@ This object has two attributes:
- `each.value` — The map value corresponding to this instance. (If a set was - `each.value` — The map value corresponding to this instance. (If a set was
provided, this is the same as `each.key`.) provided, this is the same as `each.key`.)
#### Using Expressions in `for_each`
The `for_each` meta-argument accepts map or set [expressions](./expressions.html).
However, unlike most resource arguments, the `for_each` value must be known
_before_ Terraform performs any remote resource actions. This means `for_each`
can't refer to any resource attributes that aren't known until after a
configuration is applied (such as a unique ID generated by the remote API when
an object is created).
The `for_each` value must be a map or set with one element per desired
resource instance. If you need to declare resource instances based on a nested
data structure or combinations of elements from multiple data structures you
can use Terraform expressions and functions to derive a suitable value.
For example:
* Transform a multi-level nested structure into a flat list by
[using nested `for` expressions with the `flatten` function](./functions/flatten.html#flattening-nested-structures-for-for_each).
* Produce an exhaustive list of combinations of elements from two or more
collections by
[using the `setproduct` function inside a `for` expression](./functions/setproduct.html#finding-combinations-for-for_each).
#### Referring to Instances #### Referring to Instances
When `for_each` is set, Terraform distinguishes between the resource block itself When `for_each` is set, Terraform distinguishes between the resource block itself
@ -396,16 +417,19 @@ as a whole.
#### Using Sets #### Using Sets
The Terraform language doesn't have a literal syntax for The Terraform language doesn't have a literal syntax for
[sets](./types.html#collection-types), but you can use the `toset` function to [set values](./types.html#collection-types), but you can use the `toset`
convert a list of strings to a set: function to explicitly convert a list of strings to a set:
```hcl ```hcl
variable "subnet_ids" { locals {
type = list(string) subnet_ids = toset([
"subnet-abcdef",
"subnet-012345",
])
} }
resource "aws_instance" "server" { resource "aws_instance" "server" {
for_each = toset(var.subnet_ids) for_each = local.subnet_ids
ami = "ami-a1b2c3d4" ami = "ami-a1b2c3d4"
instance_type = "t2.micro" instance_type = "t2.micro"
@ -417,24 +441,26 @@ resource "aws_instance" "server" {
} }
``` ```
#### Using Expressions in `for_each` Conversion from list to set discards the ordering of the items in the list and
removes any duplicate elements. `toset(["b", "a", "b"])` will produce a set
containing only `"a"` and `"b"` in no particular order; the second `"b"` is
discarded.
The `for_each` meta-argument accepts map or set [expressions](./expressions.html). If you are writing a module with an [input variable](./variables.html) that
However, unlike most resource arguments, the `for_each` value must be known will be used as a set of strings for `for_each`, you can set its type to
_before_ Terraform performs any remote resource actions. This means `for_each` `set(string)` to avoid the need for an explicit type conversion:
can't refer to any resource attributes that aren't known until after a
configuration is applied (such as a unique ID generated by the remote API when
an object is created).
The `for_each` value must be a map or set with one element per desired ```
resource instance. If you need to declare resource instances based on a nested variable "subnet_ids" {
data structure or combinations of elements from multiple data structures you type = set(string)
can use Terraform expressions and functions to derive a suitable value. }
For some common examples of such situations, see the
[`flatten`](/docs/configuration/functions/flatten.html) resource "aws_instance" "server" {
and for_each = var.subnet_ids
[`setproduct`](/docs/configuration/functions/setproduct.html)
functions. # (and the other arguments as above)
}
```
### `provider`: Selecting a Non-default Provider Configuration ### `provider`: Selecting a Non-default Provider Configuration