From 67311f73fd471b510cd12366c4e559d6593a2f85 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 1 Jun 2020 11:23:56 -0700 Subject: [PATCH] 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. --- .../docs/configuration/expressions.html.md | 11 +++ website/docs/configuration/resources.html.md | 68 +++++++++++++------ 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/website/docs/configuration/expressions.html.md b/website/docs/configuration/expressions.html.md index 040e2f732..1d0e1bdc4 100644 --- a/website/docs/configuration/expressions.html.md +++ b/website/docs/configuration/expressions.html.md @@ -578,6 +578,17 @@ together results that have a common key: {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 A _splat expression_ provides a more concise way to express a common diff --git a/website/docs/configuration/resources.html.md b/website/docs/configuration/resources.html.md index c2116a196..981772d10 100644 --- a/website/docs/configuration/resources.html.md +++ b/website/docs/configuration/resources.html.md @@ -376,6 +376,27 @@ This object has two attributes: - `each.value` — The map value corresponding to this instance. (If a set was 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 When `for_each` is set, Terraform distinguishes between the resource block itself @@ -396,16 +417,19 @@ as a whole. #### Using Sets The Terraform language doesn't have a literal syntax for -[sets](./types.html#collection-types), but you can use the `toset` function to -convert a list of strings to a set: +[set values](./types.html#collection-types), but you can use the `toset` +function to explicitly convert a list of strings to a set: ```hcl -variable "subnet_ids" { - type = list(string) +locals { + subnet_ids = toset([ + "subnet-abcdef", + "subnet-012345", + ]) } resource "aws_instance" "server" { - for_each = toset(var.subnet_ids) + for_each = local.subnet_ids ami = "ami-a1b2c3d4" 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). -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). +If you are writing a module with an [input variable](./variables.html) that +will be used as a set of strings for `for_each`, you can set its type to +`set(string)` to avoid the need for an explicit type conversion: -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 some common examples of such situations, see the -[`flatten`](/docs/configuration/functions/flatten.html) -and -[`setproduct`](/docs/configuration/functions/setproduct.html) -functions. +``` +variable "subnet_ids" { + type = set(string) +} + +resource "aws_instance" "server" { + for_each = var.subnet_ids + + # (and the other arguments as above) +} +``` ### `provider`: Selecting a Non-default Provider Configuration