website: Align `count` and `for_each` sections

- Make these descriptions more similar, since they do basically the same thing.
- Add some subheaders to break up the wall of text and make it more skimmable.
- Nudge people more firmly toward `for_each` if they need to actually
  incorporate data from a variable into their instances.
- Add version note so you know whether you can use this yet.
This commit is contained in:
Nick Fagerlund 2019-08-07 16:47:42 -07:00 committed by Nick Fagerlund
parent d3dc1263bf
commit 979a2fa6d1
1 changed files with 102 additions and 50 deletions

View File

@ -226,11 +226,18 @@ maintainers understand the purpose of the additional dependency.
[inpage-count]: #count-multiple-resource-instances-by-count [inpage-count]: #count-multiple-resource-instances-by-count
By default, a single `resource` block corresponds to only one real -> **Note:** A given resource block cannot use both `count` and `for_each`.
infrastructure object. Sometimes it is desirable to instead manage a set
of _similar_ objects of the same type, such as a fixed pool of compute By default, a `resource` block configures one real infrastructure object.
instances. You can achieve this by using the `count` meta-argument, However, sometimes you want to manage several similar objects, such as a fixed
which is allowed in all `resource` blocks: pool of compute instances. Terraform has two ways to do this:
`count` and [`for_each`][inpage-for_each].
The `count` meta-argument accepts a whole number, and creates that many
instances of the resource. Each instance has a distinct infrastructure object
associated with it (as described above in
[Resource Behavior](#resource-behavior)), and each is separately created,
updated, or destroyed when the configuration is applied.
```hcl ```hcl
resource "aws_instance" "server" { resource "aws_instance" "server" {
@ -245,32 +252,45 @@ resource "aws_instance" "server" {
} }
``` ```
When the `count` meta-argument is present, a distinction exists between #### The `count` Object
the resource block itself — identified as `aws_instance.server`
and the multiple _resource instances_ associated with it, identified as
`aws_instance.server[0]`, `aws_instance.server[1]`, etc. Each instance has a
distinct infrastructure object associated with it (as described above in
[Resource Behavior](#resource-behavior)), and each is separately created,
updated, or destroyed when the configuration is applied.
When `count` is _not_ present, a resource block has only a single resource In resource blocks where `count` is set, an additional `count` object is
instance, which has no associated index. available in expressions, so you can modify the configuration of each instance.
This object has one attribute:
Within resource blocks where `count` is set, an additional `count` object is - `count.index` The distinct index number (starting with `0`) corresponding
available for use in expressions so you can modify the configuration of each to this instance.
instance. This object has one attribute, `count.index`, which provides the
distinct index number (starting with `0`) for each instance.
The `count` meta-argument accepts [expressions](./expressions.html) #### Referring to Instances
in its value, similar to the resource-type-specific arguments for a resource.
However, Terraform must interpret the `count` argument _before_ any actions
are taken from remote resources, and so (unlike the resource-type-specifc arguments)
the `count` expressions may not refer to any resource attributes that are
not known until after a configuration is applied, such as a unique id
generated by the remote API when an object is created.
For example, `count` can be used with an input variable that carries a list When `count` is set, Terraform distinguishes between the resource block itself
value, to create one instance for each element of the list: and the multiple _resource instances_ associated with it. Instances are
identified by an index number, starting with `0`.
- `<TYPE>.<NAME>` (for example, `aws_instance.server`) refers to the resource block.
- `<TYPE>.<NAME>[<INDEX>]` (for example, `aws_instance.server[0]`,
`aws_instance.server[1]`, etc.) refers to individual instances.
This is different from resources without `count` or `for_each`, which can be
referenced without an index or key.
#### Using Expressions in `count`
The `count` meta-argument accepts numeric [expressions](./expressions.html).
However, unlike most resource arguments, the `count` value must be known
_before_ Terraform performs any remote resource actions. This means `count`
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).
#### When to Use `for_each` Instead of `count`
If your resource instances are almost identical, `count` is appropriate. If some
of their arguments need distinct values that can't be directly derived from an
integer, it's safer to use `for_each`.
Before `for_each` was available, it was common to derive `count` from the
length of a list and use `count.index` to look up the original list value:
```hcl ```hcl
variable "subnet_ids" { variable "subnet_ids" {
@ -291,25 +311,30 @@ resource "aws_instance" "server" {
} }
``` ```
Note that the separate resource instances created by `count` are still This was fragile, because the resource instances were still identified by their
identified by their _index_, and not by the string values in the given _index_ instead of the string values in the list. If an element was removed from
list. This means that if an element is removed from the middle of the list, the middle of the list, every instance _after_ that element would see its
all of the indexed instances _after_ it will see their `subnet_id` values `subnet_id` value change, resulting in more remote object changes than intended.
change, which will cause more remote object changes than were probably Using `for_each` gives the same flexibility without the extra churn.
intended. The practice of generating multiple instances from lists should
be used sparingly, and with due care given to what will happen if the list is
changed later.
### `for_each`: Multiple Resource Instances Defined By a Map, or Set of Strings ### `for_each`: Multiple Resource Instances Defined By a Map, or Set of Strings
[inpage-for_each]: #for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings [inpage-for_each]: #for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings
When the `for_each` meta-argument is present, Terraform will create instances -> **Version note:** `for_each` was added in Terraform 0.12.6.
based on the keys and values present in a provided map, or set of strings, and expose the values
of the map to the resource for its configuration.
The keys and values of the map, or strings in the case of a set, are exposed via the `each` attribute, -> **Note:** A given resource block cannot use both `count` and `for_each`.
which can only be used in blocks with a `for_each` argument set.
By default, a `resource` block configures one real infrastructure object.
However, sometimes you want to manage several similar objects, such as a fixed
pool of compute instances. Terraform has two ways to do this:
[`count`][inpage-count] and `for_each`.
The `for_each` meta-argument accepts a map or a set of strings, and creates an
instance for each item in that map or set. Each instance has a distinct
infrastructure object associated with it (as described above in
[Resource Behavior](#resource-behavior)), and each is separately created,
updated, or destroyed when the configuration is applied.
```hcl ```hcl
resource "azurerm_resource_group" "rg" { resource "azurerm_resource_group" "rg" {
@ -322,16 +347,34 @@ resource "azurerm_resource_group" "rg" {
} }
``` ```
Resources created by `for_each` are identified by the key associated with the instance - #### The `each` Object
that is, if we have `azurerm_resource_group.rg` as above, the instances will be `azurerm_resource_group.rg["a_group"]`
and `azurerm_resource_group.rg["another_group"]`, as those are the keys in the map provided
to the `for_each` argument.
The `for_each` argument also supports a set of strings in addition to maps; convert a list In resource blocks where `for_each` is set, an additional `each` object is
to a set using the `toset` function. As such, we can take the example available in expressions, so you can modify the configuration of each instance.
in `count` and make it safer to use, as we can change items in our set This object has two attributes:
and because the string keys are used to identify the instances,
we will only change the items we intend to: - `each.key` The map key (or set member) corresponding to this instance.
- `each.value` — The map value corresponding to this instance. (If a set was
provided, this is the same as `each.key`.)
#### Referring to Instances
When `for_each` is set, Terraform distinguishes between the resource block itself
and the multiple _resource instances_ associated with it. Instances are
identified by a map key (or set member) from the value provided to `for_each`.
- `<TYPE>.<NAME>` (for example, `azurerm_resource_group.rg`) refers to the resource block.
- `<TYPE>.<NAME>[<KEY>]` (for example, `azurerm_resource_group.rg["a_group"]`,
`azurerm_resource_group.rg["another_group"]`, etc.) refers to individual instances.
This is different from resources without `count` or `for_each`, which can be
referenced without an index or key.
#### 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:
```hcl ```hcl
variable "subnet_ids" { variable "subnet_ids" {
@ -343,7 +386,7 @@ resource "aws_instance" "server" {
ami = "ami-a1b2c3d4" ami = "ami-a1b2c3d4"
instance_type = "t2.micro" instance_type = "t2.micro"
subnet_id = each.key # note, each.key and each.value will be the same on a set subnet_id = each.key # note: each.key and each.value are the same for a set
tags { tags {
Name = "Server ${each.key}" Name = "Server ${each.key}"
@ -351,6 +394,15 @@ resource "aws_instance" "server" {
} }
``` ```
#### 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).
### `provider`: Selecting a Non-default Provider Configuration ### `provider`: Selecting a Non-default Provider Configuration
[inpage-provider]: #provider-selecting-a-non-default-provider-configuration [inpage-provider]: #provider-selecting-a-non-default-provider-configuration