0
0
Terraformcloud~15 mins

For_each for map-based instances in Terraform - Deep Dive

Choose your learning style9 modes available
Overview - For_each for map-based instances
What is it?
In Terraform, 'for_each' is a way to create multiple resources from a map of values. Instead of writing one resource at a time, you can use 'for_each' to loop over a map and create one resource for each key-value pair. This helps manage many similar resources efficiently and clearly.
Why it matters
Without 'for_each' for maps, you would have to write repetitive code for each resource, which is error-prone and hard to maintain. 'for_each' lets you automate resource creation based on data, saving time and reducing mistakes. This makes infrastructure changes safer and faster.
Where it fits
Before learning 'for_each' with maps, you should understand basic Terraform resources and variables. After this, you can learn about dynamic blocks and modules to further automate and organize infrastructure code.
Mental Model
Core Idea
'For_each' lets you create one resource per map entry, using each key and value to customize that resource.
Think of it like...
Imagine you have a list of friends and their favorite drinks. Instead of making one drink at a time, you set up a machine that takes each friend's name and drink choice and makes all drinks at once, each personalized.
Resources with for_each over a map:

  Map: { key1: value1, key2: value2, key3: value3 }

  for_each loop:
  ┌─────────────┐
  │ Resource 1  │ uses key1, value1
  ├─────────────┤
  │ Resource 2  │ uses key2, value2
  ├─────────────┤
  │ Resource 3  │ uses key3, value3
  └─────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Terraform resource basics
🤔
Concept: Learn what a Terraform resource is and how it is defined.
A Terraform resource is a block that describes one piece of infrastructure, like a server or a database. You write it with a type and a name, then specify its settings. For example: resource "aws_instance" "example" { ami = "ami-123456" instance_type = "t2.micro" } This creates one server with the given settings.
Result
You can create one resource manually by writing its block.
Understanding resources is the foundation for automating multiple resources later.
2
FoundationWhat is a map in Terraform variables
🤔
Concept: Learn how Terraform uses maps to store key-value pairs.
A map is a variable type that holds pairs of keys and values. For example: variable "servers" { type = map(string) default = { web = "ami-111111" db = "ami-222222" } } Here, 'web' and 'db' are keys, and the AMI IDs are values.
Result
You can store related data in one variable with keys and values.
Maps let you organize data clearly, which is perfect for looping with 'for_each'.
3
IntermediateUsing for_each with simple lists
🤔Before reading on: do you think 'for_each' works the same with lists and maps? Commit to your answer.
Concept: Learn how 'for_each' creates multiple resources from a list of values.
You can use 'for_each' to loop over a list and create one resource per item. Example: variable "names" { default = ["alice", "bob"] } resource "aws_instance" "example" { for_each = toset(var.names) ami = "ami-123456" instance_type = "t2.micro" tags = { Name = each.key } } This creates two instances named 'alice' and 'bob'.
Result
Terraform creates one resource per list item, using each item as a key.
Knowing how 'for_each' works with lists prepares you to use it with maps, which add keys and values.
4
IntermediateApplying for_each to map-based resources
🤔Before reading on: do you think 'each.key' and 'each.value' are available when looping over maps? Commit to your answer.
Concept: 'for_each' can loop over maps, giving access to both keys and values for each resource.
When you use 'for_each' with a map, Terraform creates one resource per key-value pair. You can use 'each.key' and 'each.value' inside the resource to customize it. Example: variable "servers" { default = { web = "ami-111111" db = "ami-222222" } } resource "aws_instance" "example" { for_each = var.servers ami = each.value instance_type = "t2.micro" tags = { Name = each.key } } This creates two instances with different AMIs and names.
Result
Resources are created with settings based on map keys and values.
Using maps with 'for_each' lets you create customized resources easily without repeating code.
5
IntermediateAccessing map keys and values inside resources
🤔
Concept: Learn how to use 'each.key' and 'each.value' to customize resource attributes.
Inside a resource using 'for_each' with a map, 'each.key' is the map key and 'each.value' is the map value. You can use them anywhere in the resource. For example: resource "aws_instance" "example" { for_each = var.servers ami = each.value instance_type = "t2.micro" tags = { Name = each.key Role = "server" } } This tags each instance with its key name and a fixed role.
Result
Each resource has attributes set uniquely based on the map entry.
Knowing how to use 'each.key' and 'each.value' unlocks flexible resource definitions.
6
AdvancedHandling complex map values with nested attributes
🤔Before reading on: do you think 'for_each' can handle maps with nested objects as values? Commit to your answer.
Concept: 'for_each' supports maps where values are objects with multiple properties, enabling rich resource customization.
You can define a map where each value is an object with several fields. Example: variable "servers" { default = { web = { ami = "ami-111111", type = "t2.small" } db = { ami = "ami-222222", type = "t2.medium" } } } resource "aws_instance" "example" { for_each = var.servers ami = each.value.ami instance_type = each.value.type tags = { Name = each.key } } This creates instances with different AMIs and types per map entry.
Result
Resources are created with multiple customized attributes from nested map values.
Understanding nested maps lets you manage complex infrastructure setups cleanly.
7
ExpertAvoiding pitfalls with for_each and map keys
🤔Before reading on: do you think changing map keys after deployment is safe? Commit to your answer.
Concept: Changing keys in a map used by 'for_each' can cause resource replacement, which may disrupt infrastructure.
Terraform tracks resources by the keys in the 'for_each' map. If you rename a key or remove it, Terraform will destroy the old resource and create a new one with the new key. This can cause downtime or data loss if not planned. To avoid this, keep keys stable or use lifecycle rules. Example problem: variable "servers" { default = { web = "ami-111111" db = "ami-222222" } } If you rename 'web' to 'frontend', Terraform will destroy the 'web' instance and create 'frontend'. Use lifecycle "prevent_destroy" or carefully plan key changes.
Result
Understanding key stability prevents accidental resource destruction.
Knowing how Terraform tracks resources by keys helps avoid costly infrastructure mistakes.
Under the Hood
'for_each' works by creating a map of resource instances keyed by the map keys you provide. Terraform uses these keys as unique IDs to track each resource's state. When you apply changes, Terraform compares the current keys to the previous ones to decide which resources to add, update, or delete. Inside each resource, 'each.key' and 'each.value' provide access to the key and value for that instance, allowing dynamic configuration.
Why designed this way?
Terraform needed a way to manage multiple similar resources without repeating code. Using map keys as unique identifiers allows stable tracking of resources even if their order changes. This design avoids fragile indexing and supports complex configurations. Alternatives like using lists with indexes were less reliable because indexes can shift, causing unintended resource replacements.
Terraform for_each internal flow:

  Input map: { key1: val1, key2: val2 }
          │
          ▼
  Terraform creates resource instances:
  ┌─────────────┐  ┌─────────────┐
  │ Resource[key1]│  │ Resource[key2]│
  └─────────────┘  └─────────────┘
          │                 │
          ▼                 ▼
  each.key = key1      each.key = key2
  each.value = val1    each.value = val2
          │                 │
          ▼                 ▼
  Resource configured with key1,val1 and key2,val2

  On apply, Terraform compares keys to detect changes.
Myth Busters - 4 Common Misconceptions
Quick: Does changing the order of map entries cause resource replacement? Commit to yes or no.
Common Belief:Changing the order of entries in a map used by 'for_each' will cause Terraform to replace resources.
Tap to reveal reality
Reality:Terraform tracks resources by map keys, not order. Changing order alone does not cause replacement.
Why it matters:Believing order matters can cause unnecessary worry or incorrect attempts to preserve order, complicating code.
Quick: Can you use 'count' instead of 'for_each' for maps without issues? Commit to yes or no.
Common Belief:'count' can be used interchangeably with 'for_each' for maps to create resources.
Tap to reveal reality
Reality:'count' works with lists and numbers but does not support maps well because it uses indexes, which are unstable for maps.
Why it matters:Using 'count' with maps leads to fragile code and unexpected resource replacements.
Quick: Does 'each.value' always represent a simple string? Commit to yes or no.
Common Belief:'each.value' is always a simple string or number when using 'for_each' with maps.
Tap to reveal reality
Reality:'each.value' can be complex objects, including nested maps or lists, allowing rich resource configuration.
Why it matters:Underestimating 'each.value' limits your ability to model complex infrastructure efficiently.
Quick: Is it safe to rename keys in a map used by 'for_each' anytime? Commit to yes or no.
Common Belief:Renaming keys in a map used by 'for_each' is safe and does not affect existing resources.
Tap to reveal reality
Reality:Renaming keys causes Terraform to destroy old resources and create new ones, which can cause downtime.
Why it matters:Ignoring this can lead to accidental resource loss and service interruptions.
Expert Zone
1
Terraform uses the map keys as stable resource IDs, so keys must be unique and consistent across runs to avoid resource churn.
2
When using nested objects as map values, you can combine 'for_each' with dynamic blocks for even more flexible resource definitions.
3
Terraform's state file stores resource instances keyed by 'for_each' keys, so manual state edits must respect these keys to avoid corruption.
When NOT to use
Avoid using 'for_each' with maps when resource count is very large and performance matters; consider modules or external orchestration instead. Also, if resource uniqueness depends on complex conditions not representable as map keys, use other patterns like 'count' with careful indexing or dynamic blocks.
Production Patterns
In production, teams use 'for_each' with maps to manage multiple similar resources like servers, databases, or DNS records. They keep keys stable to prevent accidental replacements and use nested maps for detailed configurations. Combining 'for_each' with modules allows scalable, reusable infrastructure code.
Connections
Hash Tables (Computer Science)
Both use key-value pairs to organize and access data efficiently.
Understanding how hash tables use keys to find values helps grasp why Terraform uses map keys as stable resource identifiers.
Batch Cooking (Real Life)
Creating multiple meals at once from a recipe list is like creating multiple resources from a map.
Seeing infrastructure as batch tasks helps appreciate automation benefits and the importance of consistent inputs.
Database Primary Keys (Databases)
Map keys in 'for_each' act like primary keys uniquely identifying each resource instance.
Knowing about primary keys clarifies why keys must be unique and stable to avoid data loss or duplication.
Common Pitfalls
#1Changing map keys after deployment causes resource destruction.
Wrong approach:variable "servers" { default = { frontend = "ami-111111" db = "ami-222222" } } # Previously 'web' was the key, now renamed to 'frontend'.
Correct approach:variable "servers" { default = { web = "ami-111111" db = "ami-222222" } } # Keep keys stable or plan replacement carefully.
Root cause:Misunderstanding that keys are resource IDs tracked by Terraform.
#2Using 'count' with maps instead of 'for_each'.
Wrong approach:resource "aws_instance" "example" { count = length(var.servers) ami = var.servers[count.index] instance_type = "t2.micro" } # This fails because 'var.servers' is a map, not a list.
Correct approach:resource "aws_instance" "example" { for_each = var.servers ami = each.value instance_type = "t2.micro" }
Root cause:Confusing 'count' (list-based) with 'for_each' (map-based) iteration.
#3Assuming 'each.value' is always a simple string.
Wrong approach:resource "aws_instance" "example" { for_each = var.servers ami = each.value instance_type = "t2.micro" } # But 'each.value' is actually an object, causing errors.
Correct approach:resource "aws_instance" "example" { for_each = var.servers ami = each.value.ami instance_type = each.value.type }
Root cause:Not accounting for nested map values in resource attributes.
Key Takeaways
'for_each' in Terraform creates one resource per map entry, using keys as stable identifiers.
Maps let you organize resource data clearly, and 'each.key' and 'each.value' let you customize each resource.
Changing map keys after deployment causes resource replacement, so keys must be stable.
'for_each' is better than 'count' for maps because it avoids fragile index-based tracking.
Using nested map values with 'for_each' enables complex, flexible infrastructure definitions.