Terraform
for_each
resource reference
infrastructure as code
cloud provisioning

Terraform Referencing resources created in for_each in another resource

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

When a Terraform resource uses for_each, the result is not one resource object anymore. Terraform turns that resource into a map of resource instances keyed by the for_each keys. Most confusion comes from forgetting that shape change and then trying to reference the result as though it were a single object or a list.

Once you keep the map model in mind, the rest becomes much easier. You either reference one specific instance by key, iterate over the map in another for_each, or build a derived map of the attributes you need.

Understand the Shape of a for_each Resource

Suppose you create multiple subnets from a map:

hcl
1variable "subnets" {
2  type = map(object({
3    cidr_block = string
4    az         = string
5  }))
6}
7
8resource "aws_subnet" "app" {
9  for_each = var.subnets
10
11  vpc_id            = aws_vpc.main.id
12  cidr_block        = each.value.cidr_block
13  availability_zone = each.value.az
14}

Because aws_subnet.app uses for_each, Terraform treats aws_subnet.app as a map of objects. That means you reference a specific instance by key:

hcl
aws_subnet.app["private-a"].id

This is the most important rule in the whole topic. for_each gives you a keyed map, not a numeric index.

Reference a Specific Instance in Another Resource

If another resource needs exactly one of those subnets, use the matching key directly.

hcl
1resource "aws_instance" "api" {
2  ami           = "ami-0123456789abcdef0"
3  instance_type = "t3.micro"
4  subnet_id     = aws_subnet.app["private-a"].id
5}

This works well when the dependency is one-to-one and you know which instance key you want.

The common mistake is writing something list-like such as aws_subnet.app[0].id. That fails because for_each instances are keyed by strings or set members, not numeric positions.

Chain for_each Into Another Resource

A very common pattern is creating a second resource for every instance of the first one. Terraform supports that directly because a for_each resource can feed another for_each.

hcl
1resource "aws_route_table" "app" {
2  for_each = aws_subnet.app
3
4  vpc_id = aws_vpc.main.id
5
6  tags = {
7    Name = "rt-${each.key}"
8  }
9}

Here, each.key is the subnet key and each.value is the full subnet object. This “chain pattern” is often the cleanest answer because it preserves the relationship between the two resource sets.

If you need an attribute from the upstream resource, use each.value:

hcl
1resource "aws_network_acl_association" "app" {
2  for_each       = aws_subnet.app
3  subnet_id      = each.value.id
4  network_acl_id = aws_network_acl.shared.id
5}

Build a Derived Map When You Need Only Certain Attributes

Sometimes a downstream resource or output only needs selected attributes. A for expression is useful there.

hcl
1locals {
2  subnet_ids = {
3    for key, subnet in aws_subnet.app : key => subnet.id
4  }
5}
6
7output "subnet_ids" {
8  value = local.subnet_ids
9}

This makes the data shape explicit and often improves readability, especially in modules where the output should stay small and predictable.

Be Careful With Sets Versus Maps

for_each accepts maps and sets of strings. If you use a set, each.key and each.value are effectively the same string. If you need named attributes, maps are usually clearer.

hcl
1locals {
2  names = toset(["api", "worker"])
3}
4
5resource "aws_iam_user" "user" {
6  for_each = local.names
7  name     = each.key
8}

Sets are fine for simple one-value cases. Maps are better when you want stable named instances with associated configuration.

Common Pitfalls

The biggest mistake is treating a for_each resource as one object or as an indexed list. It is a map, so references must use keys.

Another issue is generating unstable keys. If the keys change, Terraform sees different instances and may destroy and recreate resources.

A third problem is using a list when the design really needs a map. Lists encourage index thinking, while for_each works best with stable keys that express meaning.

Summary

  • A resource created with for_each becomes a map of instances.
  • Reference one instance with a key, such as resource.name["key"].id.
  • Use one for_each resource directly as the for_each of another for one-to-one chaining.
  • Build derived maps with for expressions when you need only selected attributes.
  • Prefer stable, meaningful keys so Terraform can track instances predictably.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.