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:
parent
d3dc1263bf
commit
979a2fa6d1
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue