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