github.com/hugorut/terraform@v1.1.3/website/docs/language/meta-arguments/for_each.mdx (about)

     1  ---
     2  page_title: The for_each Meta-Argument - Configuration Language
     3  description: >-
     4    The for_each meta-argument allows you to manage similar infrastructure
     5    resources without writing a separate block for each one.
     6  ---
     7  
     8  # The `for_each` Meta-Argument
     9  
    10  By default, a [resource block](/language/resources/syntax) configures one real
    11  infrastructure object (and similarly, a
    12  [module block](/language/modules/syntax) includes a
    13  child module's contents into the configuration one time).
    14  However, sometimes you want to manage several similar objects (like a fixed
    15  pool of compute instances) without writing a separate block for each one.
    16  Terraform has two ways to do this:
    17  [`count`](/language/meta-arguments/count) and `for_each`.
    18  
    19  > **Hands-on:** Try the [Manage Similar Resources With For Each](https://learn.hashicorp.com/tutorials/terraform/for-each?in=terraform/configuration-language) tutorial on HashiCorp Learn.
    20  
    21  If a resource or module block includes a `for_each` argument whose value is a map or
    22  a set of strings, Terraform will create one instance for each member of
    23  that map or set.
    24  
    25  -> **Version note:** `for_each` was added in Terraform 0.12.6. Module support
    26  for `for_each` was added in Terraform 0.13; previous versions can only use
    27  it with resources.
    28  
    29  -> **Note:** A given resource or module block cannot use both `count` and `for_each`.
    30  
    31  ## Basic Syntax
    32  
    33  `for_each` is a meta-argument defined by the Terraform language. It can be used
    34  with modules and with every resource type.
    35  
    36  The `for_each` meta-argument accepts a map or a set of strings, and creates an
    37  instance for each item in that map or set. Each instance has a distinct
    38  infrastructure object associated with it, and each is separately created,
    39  updated, or destroyed when the configuration is applied.
    40  
    41  Map:
    42  
    43  ```hcl
    44  resource "azurerm_resource_group" "rg" {
    45    for_each = {
    46      a_group = "eastus"
    47      another_group = "westus2"
    48    }
    49    name     = each.key
    50    location = each.value
    51  }
    52  ```
    53  
    54  Set of strings:
    55  
    56  ```hcl
    57  resource "aws_iam_user" "the-accounts" {
    58    for_each = toset( ["Todd", "James", "Alice", "Dottie"] )
    59    name     = each.key
    60  }
    61  ```
    62  
    63  Child module:
    64  
    65  ```hcl
    66  # my_buckets.tf
    67  module "bucket" {
    68    for_each = toset(["assets", "media"])
    69    source   = "./publish_bucket"
    70    name     = "${each.key}_bucket"
    71  }
    72  ```
    73  
    74  ```hcl
    75  # publish_bucket/bucket-and-cloudfront.tf
    76  variable "name" {} # this is the input parameter of the module
    77  
    78  resource "aws_s3_bucket" "example" {
    79    # Because var.name includes each.key in the calling
    80    # module block, its value will be different for
    81    # each instance of this module.
    82    bucket = var.name
    83  
    84    # ...
    85  }
    86  
    87  resource "aws_iam_user" "deploy_user" {
    88    # ...
    89  }
    90  ```
    91  
    92  ## The `each` Object
    93  
    94  In blocks where `for_each` is set, an additional `each` object is
    95  available in expressions, so you can modify the configuration of each instance.
    96  This object has two attributes:
    97  
    98  - `each.key` — The map key (or set member) corresponding to this instance.
    99  - `each.value` — The map value corresponding to this instance. (If a set was
   100    provided, this is the same as `each.key`.)
   101  
   102  ## Limitations on values used in `for_each`
   103  
   104  The keys of the map (or all the values in the case of a set of strings) must
   105  be _known values_, or you will get an error message that `for_each` has dependencies
   106  that cannot be determined before apply, and a `-target` may be needed.
   107  
   108  `for_each` keys cannot be the result (or rely on the result of) of impure functions,
   109  including `uuid`, `bcrypt`, or `timestamp`, as their evaluation is deferred during the
   110  main evaluation step.
   111  
   112  Sensitive values, such as [sensitive input variables](/language/values/variables#suppressing-values-in-cli-output),
   113  [sensitive outputs](/language/values/outputs#sensitive-suppressing-values-in-cli-output),
   114  or [sensitive resource attributes](/language/expressions/references#sensitive-resource-attributes),
   115  cannot be used as arguments to `for_each`. The value used in `for_each` is used
   116  to identify the resource instance and will always be disclosed in UI output,
   117  which is why sensitive values are not allowed.
   118  Attempts to use sensitive values as `for_each` arguments will result in an error.
   119  
   120  If you transform a value containing sensitive data into an argument to be used in `for_each`, be aware that
   121  [most functions in Terraform will return a sensitive result if given an argument with any sensitive content](/language/expressions/function-calls#using-sensitive-data-as-function-arguments).
   122  In many cases, you can achieve similar results to a function used for this purpose by
   123  using a `for` expression. For example, if you would like to call `keys(local.map)`, where
   124  `local.map` is an object with sensitive values (but non-sensitive keys), you can create a
   125  value to pass to  `for_each` with `toset([for k,v in local.map : k])`.
   126  
   127  ## Using Expressions in `for_each`
   128  
   129  The `for_each` meta-argument accepts map or set [expressions](/language/expressions).
   130  However, unlike most arguments, the `for_each` value must be known
   131  _before_ Terraform performs any remote resource actions. This means `for_each`
   132  can't refer to any resource attributes that aren't known until after a
   133  configuration is applied (such as a unique ID generated by the remote API when
   134  an object is created).
   135  
   136  The `for_each` value must be a map or set with one element per desired
   137  resource instance. When providing a set, you must use an expression that
   138  explicitly returns a set value, like the [`toset`](/language/functions/toset)
   139  function; to prevent unwanted surprises during conversion, the `for_each`
   140  argument does not implicitly convert lists or tuples to sets.
   141  If you need to declare resource instances based on a nested
   142  data structure or combinations of elements from multiple data structures you
   143  can use Terraform expressions and functions to derive a suitable value.
   144  For example:
   145  
   146  - Transform a multi-level nested structure into a flat list by
   147    [using nested `for` expressions with the `flatten` function](/language/functions/flatten#flattening-nested-structures-for-for_each).
   148  - Produce an exhaustive list of combinations of elements from two or more
   149    collections by
   150    [using the `setproduct` function inside a `for` expression](/language/functions/setproduct#finding-combinations-for-for_each).
   151  
   152  ### Chaining `for_each` Between Resources
   153  
   154  Because a resource using `for_each` appears as a map of objects when used in
   155  expressions elsewhere, you can directly use one resource as the `for_each`
   156  of another in situations where there is a one-to-one relationship between
   157  two sets of objects.
   158  
   159  For example, in AWS an `aws_vpc` object is commonly associated with a number
   160  of other objects that provide additional services to that VPC, such as an
   161  "internet gateway". If you are declaring multiple VPC instances using `for_each`
   162  then you can chain that `for_each` into another resource to declare an
   163  internet gateway for each VPC:
   164  
   165  ```hcl
   166  variable "vpcs" {
   167    type = map(object({
   168      cidr_block = string
   169    }))
   170  }
   171  
   172  resource "aws_vpc" "example" {
   173    # One VPC for each element of var.vpcs
   174    for_each = var.vpcs
   175  
   176    # each.value here is a value from var.vpcs
   177    cidr_block = each.value.cidr_block
   178  }
   179  
   180  resource "aws_internet_gateway" "example" {
   181    # One Internet Gateway per VPC
   182    for_each = aws_vpc.example
   183  
   184    # each.value here is a full aws_vpc object
   185    vpc_id = each.value.id
   186  }
   187  
   188  output "vpc_ids" {
   189    value = {
   190      for k, v in aws_vpc.example : k => v.id
   191    }
   192  
   193    # The VPCs aren't fully functional until their
   194    # internet gateways are running.
   195    depends_on = [aws_internet_gateway.example]
   196  }
   197  ```
   198  
   199  This chaining pattern explicitly and concisely declares the relationship
   200  between the internet gateway instances and the VPC instances, which tells
   201  Terraform to expect the instance keys for both to always change together,
   202  and typically also makes the configuration easier to understand for human
   203  maintainers.
   204  
   205  ## Referring to Instances
   206  
   207  When `for_each` is set, Terraform distinguishes between the block itself
   208  and the multiple _resource or module instances_ associated with it. Instances are
   209  identified by a map key (or set member) from the value provided to `for_each`.
   210  
   211  - `<TYPE>.<NAME>` or `module.<NAME>` (for example, `azurerm_resource_group.rg`) refers to the block.
   212  - `<TYPE>.<NAME>[<KEY>]` or `module.<NAME>[<KEY>]` (for example, `azurerm_resource_group.rg["a_group"]`,
   213    `azurerm_resource_group.rg["another_group"]`, etc.) refers to individual instances.
   214  
   215  This is different from resources and modules without `count` or `for_each`, which can be
   216  referenced without an index or key.
   217  
   218  Similarly, resources from child modules with multiple instances are prefixed
   219  with `module.<NAME>[<KEY>]` when displayed in plan output and elsewhere in the UI.
   220  For a module without `count` or `for_each`, the address will not contain
   221  the module index as the module's name suffices to reference the module.
   222  
   223  -> **Note:** Within nested `provisioner` or `connection` blocks, the special
   224  `self` object refers to the current _resource instance,_ not the resource block
   225  as a whole.
   226  
   227  ## Using Sets
   228  
   229  The Terraform language doesn't have a literal syntax for
   230  [set values](/language/expressions/type-constraints#collection-types), but you can use the `toset`
   231  function to explicitly convert a list of strings to a set:
   232  
   233  ```hcl
   234  locals {
   235    subnet_ids = toset([
   236      "subnet-abcdef",
   237      "subnet-012345",
   238    ])
   239  }
   240  
   241  resource "aws_instance" "server" {
   242    for_each = local.subnet_ids
   243  
   244    ami           = "ami-a1b2c3d4"
   245    instance_type = "t2.micro"
   246    subnet_id     = each.key # note: each.key and each.value are the same for a set
   247  
   248    tags = {
   249      Name = "Server ${each.key}"
   250    }
   251  }
   252  ```
   253  
   254  Conversion from list to set discards the ordering of the items in the list and
   255  removes any duplicate elements. `toset(["b", "a", "b"])` will produce a set
   256  containing only `"a"` and `"b"` in no particular order; the second `"b"` is
   257  discarded.
   258  
   259  If you are writing a module with an [input variable](/language/values/variables) that
   260  will be used as a set of strings for `for_each`, you can set its type to
   261  `set(string)` to avoid the need for an explicit type conversion:
   262  
   263  ```hcl
   264  variable "subnet_ids" {
   265    type = set(string)
   266  }
   267  
   268  resource "aws_instance" "server" {
   269    for_each = var.subnet_ids
   270  
   271    # (and the other arguments as above)
   272  }
   273  ```