---
layout: "language"
page_title: "The for_each Meta-Argument - Configuration Language"
---

# The `for_each` Meta-Argument

-> **Version note:** `for_each` was added in Terraform 0.12.6. Module support
for `for_each` was added in Terraform 0.13, and previous versions can only use
it with resources.

-> **Note:** A given resource or module block cannot use both `count` and `for_each`.

> **Hands-on:** Try the [Manage Similar Resources With For Each](https://learn.hashicorp.com/tutorials/terraform/for-each?in=terraform/0-13&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn.

By default, a [resource block](/docs/language/resources/syntax.html) configures one real
infrastructure object (and similarly, a
[module block](/docs/language/modules/syntax.html) includes a
child module's contents into the configuration one time).
However, sometimes you want to manage several similar objects (like a fixed
pool of compute instances) without writing a separate block for each one.
Terraform has two ways to do this:
[`count`](/docs/language/meta-arguments/count.html) and `for_each`.

If a resource or module block includes a `for_each` argument whose value is a map or
a set of strings, Terraform will create one instance for each member of
that map or set.

## Basic Syntax

`for_each` is a meta-argument defined by the Terraform language. It can be used
with modules and with every resource type.

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, and each is separately created,
updated, or destroyed when the configuration is applied.

Map:

```hcl
resource "azurerm_resource_group" "rg" {
  for_each = {
    a_group = "eastus"
    another_group = "westus2"
  }
  name     = each.key
  location = each.value
}
```

Set of strings:

```hcl
resource "aws_iam_user" "the-accounts" {
  for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
  name     = each.key
}
```

Child module:

```hcl
# my_buckets.tf
module "bucket" {
  for_each = toset(["assets", "media"])
  source   = "./publish_bucket"
  name     = "${each.key}_bucket"
}
```

```hcl
# publish_bucket/bucket-and-cloudfront.tf
variable "name" {} # this is the input parameter of the module

resource "aws_s3_bucket" "example" {
  # Because var.name includes each.key in the calling
  # module block, its value will be different for
  # each instance of this module.
  bucket = var.name

  # ...
}

resource "aws_iam_user" "deploy_user" {
  # ...
}
```

## The `each` Object

In blocks where `for_each` is set, an additional `each` object is
available in expressions, so you can modify the configuration of each instance.
This object has two attributes:

- `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`.)

## Limitations on values used in `for_each`

The keys of the map (or all the values in the case of a set of strings) must
be _known values_, or you will get an error message that `for_each` has dependencies
that cannot be determined before apply, and a `-target` may be needed.

`for_each` keys cannot be the result (or rely on the result of) of impure functions,
including `uuid`, `bcrypt`, or `timestamp`, as their evaluation is deferred during the
main evaluation step.

Sensitive values, such as [sensitive input variables](https://www.terraform.io/docs/language/values/variables.html#suppressing-values-in-cli-output),
[sensitive outputs](https://www.terraform.io/docs/language/values/outputs.html#sensitive-suppressing-values-in-cli-output),
or [sensitive resource attributes](https://www.terraform.io/docs/language/expressions/references.html#sensitive-resource-attributes)
(if the `provider_sensitive_attrs` experiment is enabled), cannot be used as arguments
to `for_each`. The value used in `for_each` is used to identify the resource instance
and will always be disclosed in UI output, which is why sensitive values are not allowed.
Attempts to use sensitive values as `for_each` arguments will result in an error.

If you transform a value containing sensitive data into an argument to be used in `for_each`, be aware that
[most functions in Terraform will return a sensitive result if given an argument with any sensitive content](https://www.terraform.io/docs/language/expressions/function-calls.html#using-sensitive-data-as-function-arguments).
In many cases, you can achieve similar results to a function used for this purpose by
using a `for` expression. For example, if you would like to call `keys(local.map)`, where
`local.map` is an object with sensitive values (but non-sensitive keys), you can create a
value to pass to  `for_each` with `toset([for k,v in local.map : k])`.

## Using Expressions in `for_each`

The `for_each` meta-argument accepts map or set [expressions](/docs/language/expressions/index.html).
However, unlike most 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. When providing a set, you must use an expression that
explicitly returns a set value, like the [`toset`](/docs/language/functions/toset.html)
function; to prevent unwanted surprises during conversion, the `for_each`
argument does not implicitly convert lists or tuples to sets.
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](/docs/language/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](/docs/language/functions/setproduct.html#finding-combinations-for-for_each).

## Referring to Instances

When `for_each` is set, Terraform distinguishes between the block itself
and the multiple _resource or module instances_ associated with it. Instances are
identified by a map key (or set member) from the value provided to `for_each`.

- `<TYPE>.<NAME>` or `module.<NAME>` (for example, `azurerm_resource_group.rg`) refers to the block.
- `<TYPE>.<NAME>[<KEY>]` or `module.<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 and modules without `count` or `for_each`, which can be
referenced without an index or key.

Similarly, resources from child modules with multiple instances are prefixed
with `module.<NAME>[<KEY>]` when displayed in plan output and elsewhere in the UI.
For a module without `count` or `for_each`, the address will not contain
the module index as the module's name suffices to reference the module.

-> **Note:** Within nested `provisioner` or `connection` blocks, the special
`self` object refers to the current _resource instance,_ not the resource block
as a whole.

## Using Sets

The Terraform language doesn't have a literal syntax for
[set values](/docs/language/expressions/type-constraints.html#collection-types), but you can use the `toset`
function to explicitly convert a list of strings to a set:

```hcl
locals {
  subnet_ids = toset([
    "subnet-abcdef",
    "subnet-012345",
  ])
}

resource "aws_instance" "server" {
  for_each = local.subnet_ids

  ami           = "ami-a1b2c3d4"
  instance_type = "t2.micro"
  subnet_id     = each.key # note: each.key and each.value are the same for a set

  tags = {
    Name = "Server ${each.key}"
  }
}
```

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.

If you are writing a module with an [input variable](/docs/language/values/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:

```hcl
variable "subnet_ids" {
  type = set(string)
}

resource "aws_instance" "server" {
  for_each = var.subnet_ids

  # (and the other arguments as above)
}
```
