The 'Missing resource instance key' error occurs when you try to access attributes of a resource that has for_each set without specifying which instance to access. Fix it by using specific instance keys like aws_instance.example[each.key].id or using a for expression in outputs.
This error occurs when Terraform tries to reference a resource that uses the `for_each` meta-argument without specifying which instance to access. When a resource is created with `for_each`, it becomes a map of instances rather than a single resource. Each instance is identified by a unique key from the `for_each` collection. Terraform cannot infer which specific instance you want to reference when you try to access a resource attribute directly. For example, if you have `aws_instance.web_app` with `for_each = var.instance_config`, you cannot access `aws_instance.web_app.id` because Terraform doesn't know which instance's ID you want. You must specify the key: `aws_instance.web_app["instance-1"].id` or iterate over all instances. This validation prevents ambiguous resource references that could lead to unpredictable behavior or unintended resource destruction when your configuration changes.
When accessing a resource created with for_each, always specify the key using bracket notation:
# WRONG - will cause error
resource "aws_security_group_rule" "example" {
source_security_group_id = aws_security_group.web_app.id
}
# CORRECT - specify the instance key
resource "aws_security_group_rule" "example" {
for_each = var.rules
source_security_group_id = aws_security_group.web_app[each.key].id
}The key each.key refers to the current key from the for_each iteration. You can also use specific string keys if accessing a different for_each resource:
value = aws_instance.web_app["production"].idWhen building outputs that reference multiple for_each instances, use a for expression to iterate over all instances:
# WRONG
output "instance_ids" {
value = aws_instance.web_app.id
}
# CORRECT - iterate using for expression
output "instance_ids" {
value = { for k, v in aws_instance.web_app : k => v.id }
}
# Or as a list
output "instance_ids" {
value = [for instance in aws_instance.web_app : instance.id]
}This creates an output that contains the IDs of all instances, properly referencing each one individually.
When building IAM policies that reference resources created with for_each, use a for expression:
# WRONG - causes error
resource "aws_iam_role_policy" "example" {
policy = jsonencode({
Statement = [{
Action = "s3:GetObject"
Resource = aws_s3_bucket.example.arn # ERROR if bucket uses for_each
}]
})
}
# CORRECT - iterate over instances
resource "aws_iam_role_policy" "example" {
for_each = var.bucket_configs
policy = jsonencode({
Statement = [{
Action = "s3:GetObject"
Resource = aws_s3_bucket.example[each.key].arn
}]
})
}
# Or use a dynamic block
resource "aws_iam_role_policy" "example" {
policy = jsonencode({
Statement = [{
Action = "s3:GetObject"
Resource = [for bucket in aws_s3_bucket.example : bucket.arn]
}]
})
}For more complex scenarios, use Terraform's dynamic block to generate resource configuration based on for_each instances:
resource "aws_security_group" "main" {
name = "main-sg"
dynamic "ingress" {
for_each = aws_network_interface.example
content {
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [ingress.value.security_groups]
}
}
}The dynamic block iterates over the for_each collection, making each instance available as ingress.value.
If you're migrating from count to for_each, ensure all references are updated:
# OLD (with count)
resource "aws_instance" "web_app" {
count = var.instance_count
# ...
}
output "ids" {
value = aws_instance.web_app[*].id # Works with count
}
# NEW (with for_each)
resource "aws_instance" "web_app" {
for_each = var.instance_map
# ...
}
output "ids" {
value = [for instance in aws_instance.web_app : instance.id] # Must use for expression
}The splat syntax ([*]) works with count, but for_each requires explicit for expressions.
Difference between count and for_each:
With count, resources are indexed by position (0, 1, 2...) and stored as a list. With for_each, resources are indexed by a user-specified key and stored as a map. This means:
- count uses splat syntax: aws_instance.web[*].id
- for_each requires explicit iteration: [for instance in aws_instance.web : instance.id]
Changing resource counts or reordering items in a count list causes Terraform to recreate downstream resources. Using for_each with stable keys prevents this issue, making it the preferred approach for complex configurations.
When to use each.key vs each.value:
- each.key - The key from your for_each map (usually a string identifier)
- each.value - The complete value object from your for_each map (useful for passing entire configuration objects)
Most commonly, you'll use each.key to access sibling resources and each.value to access the current resource's configuration.
Error: Error rendering template: template not found
How to fix "template not found" error in Terraform
Error: Error generating private key
How to fix 'Error generating private key' in Terraform
Error creating Kubernetes Service: field is immutable
How to fix "field is immutable" errors in Terraform
Error: Error creating local file: open: permission denied
How to fix "Error creating local file: permission denied" in Terraform
Error: line endings have changed from CRLF to LF
Line endings have changed from CRLF to LF in Terraform