Suppose several EC2 instances need the same basic configuration but different names and instance types. Put their configurations in a map and assign it to `for_each` in one resource block. Terraform creates an instance for each entry; `each.key` supplies its name, while `each.value` supplies its settings. The same approach works with modules. section 4.8.2
Instances with different sizes
A `machine_configs` map can pair `web_server` with `t3.nano` and `background_processor` with `t3.micro`. In an `aws_instance` block, set `for_each = local.machine_configs`, `instance_type = each.value.type`, and the `Name` tag to `each.key`. section 4.8.2
`for_each` accepts a map, object, or set, rather than the integer used by `count`. For a set, the element is both `each.key` and `each.value`; this suits resources that differ by one value, such as VPC endpoints for selected services. A list cannot be passed directly. `toset` converts it, but removes duplicates and ordering. A block can use either `for_each` or `count`, not both. section 4.8.2 section 4.8
The collection must be known when Terraform plans, so it can create the necessary resource-graph nodes. Values derived from resource attributes or functions such as `timestamp` and `uuid` can cause a planning error. Prefer an input value over a computed resource attribute when possible. Another workaround is to use a known `count` and select configuration with `count.index`; splitting deployment into batches is possible but complicates normal deployment and teardown. section 4.8.4 section 5.7.5
Do not confuse resource or module `for_each` with the similarly named parameter in a `dynamic` block. The latter repeats nested configuration blocks, such as security-group ingress rules, rather than creating resource instances. section 4.10
Listing 4.36 Using for_each to create resources with different parameters
locals {
machine_configs = {
"web_server" = {
type = "t3.nano"
}
"background_processor" = {
type = "t3.micro"
}
}
}
resource "aws_instance" "for_each" {
for_each = local.machine_configs #1
subnet_id = var.subnet_id #2
instance_type = each.value.type #3
ami = data.aws_ami.main.id #4
tags = {
Name = each.key #5
}
}
locals { #6
vpc_endpoints = [ #7
"s3",
"dynamodb"
]
}
data "aws_region" "current" {} #8
resource "aws_vpc_endpoint" "main" { #9
for_each = toset(local.machine_configs) #10
vpc_id = var.vpc_id
service_name = "com.amazonaws.${data.aws_region.current.name}.${each.value}" #11
}
The listing shows `each.key` and `each.value` assigning different names and instance types from one resource block. section 4.8.2
