Terraform for_each with Maps for Stable Multi-Resource Addressing
Use Terraform for_each with a map to create multiple resources with stable addresses. The key becomes part of the resource address, preventing index drift that count causes on inserts and deletes.
01 Dec 2025, 13:37 UTC

Use for_each with a map when you need stable resource addresses
The practical problem with count is index drift. Adding or removing an item in the middle shifts indices, so Terraform plans replacements for resources that did not change. for_each creates one instance per element of a map or set of strings and addresses it as resource_type.name[key]. The address is tied to the map key, not position, so inserts and deletes affect only the changed element.
Use for_each when the set of things you manage is named and you want adds/removes to be independent of ordering. Use count when you need an ordered, numerically indexed set where shifting is acceptable.
Mechanism with a single worked configuration
Define a map variable where each key is a stable name and each value holds per-instance attributes.
variable "instances" {
type = map(object({
ami = string
instance_type = string
tags = map(string)
}))
default = {
web-a = {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = { Role = "web" }
}
web-b = {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = { Role = "web" }
}
}
}Reference it with for_each. each.key is the map key, each.value is the object.
resource "aws_instance" "app" {
for_each = var.instances
ami = each.value.ami
instance_type = each.value.instance_type
tags = merge(each.value.tags, {
Name = each.key
})
}With this, Terraform creates aws_instance.app["web-a"] and aws_instance.app["web-b"]. The address includes the key, so the resource identity is stable.
Module input pattern. Pass the same map into a module and keep the for_each inside the module for encapsulation.
module "apps" {
source = "./modules/ec2"
instances = var.instances
}Inside the module, the resource uses for_each = var.instances. Changing the map in the root only changes the module input; the module's for_each remains stable as long as keys are preserved.
Where to run and what to check
Run locally in a directory containing main.tf, variables.tf and terraform.tf. Required permissions are read access to the config and write access to .terraform and the state backend. No cloud credentials are needed for plan inspection.
terraform init
terraform planExpected checks in the plan output: resource addresses appear with the ["key"] suffix, e.g., aws_instance.app["web-a"]. Adding a new key to var.instances should show only one to-add. Removing a key should show only one to-remove. No other instances should be marked for replacement.
Risk: for_each changes the state. Renaming a map key changes the address and Terraform will plan destroy and create for that resource. Plan carefully when renaming keys.
Limits
- The collection for for_each must be known during plan. Computed values from data sources or other resources cannot be used directly. Terraform will error about an unknown collection.
- for_each and count are mutually exclusive on the same resource. Do not set both.
- Map keys must be strings and unique. Sets of strings are allowed, but sets of objects are not.
- for_each was introduced for resources in Terraform 0.12. Provider support is version sensitive.
- Keys appear in state. If values contain sensitive data, use a remote backend with encryption.
Common mistakes
Passing a list to for_each. A list is implicitly converted to a set of strings, losing per-element attributes. Use a map or set of strings intentionally.
Reusing map keys across environments in the same state. The key is part of the address, so the same key in dev and prod must live in separate workspaces or states.
Assuming for_each preserves order for outputs. for_each produces an unordered collection. Use keys explicitly when you need deterministic ordering.
Changing a map key to rename a resource. Terraform sees a new address and plans destroy/create. Keep keys stable and use tags for display names.
Verification steps
- Create a minimal main.tf with the map variable and for_each resource above. Run terraform init and terraform plan and inspect planned resource addresses for the [key] suffix.
- Modify the map by adding or removing a key and re-run plan to confirm only the affected instance is added or removed without shifting others.
- Attempt to use a computed value for for_each to observe the plan error about an unknown collection.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.