terraform understanding for_each vs for

Maybe one of the most often used construct in terraform are the for_each and for loop. Just to clarify these 2 constructs. In Terraform, for and for_each sound almost identical, but they serve entirely different purposes.

Here is the golden rule to tell them apart:

  • for_each is a meta-argument used to duplicate resources or modules (it creates multiple infrastructure objects).

  • for is a looping expression used to transform values within a resource attribute (it outputs a new list or map).

 

Featurefor_each (Meta-argument)for (Expression)
What it doesMultiplies resources/modules.Transforms data structures.
Where it livesDirectly inside a resource or module block.Inside an attribute assignment (e.g., tags = ...).
Accepted Inputsset of strings, or a map. (Lists not allowed directly).list, set, tuple, map.
OutputMultiple cloud resources.A single list or map value.

1. The for_each Meta-argument

Use for_each when you want to spin up multiple copies of the same resource based on a collection. When using for_each, Terraform gives you access to an each object:

  • each.key: The map key (or set value).

  • each.value: The map value (or set value).

Example with a Map

Maps are the best fit for for_each because they allow you to pass complex configuration data for each individual resource.


variable "iam_users" {
  type = map(object({
    role = string
    dept = string
  }))
  default = {
    "alice" = { role = "Engineer", dept = "Dev" }
    "bob"   = { role = "Manager",  dept = "Ops" }
  }
}

resource "aws_iam_user" "users" {
  for_each = var.iam_users

  name = each.key # "alice", then "bob"
  tags = {
    Role       = each.value.role # "Engineer", then "Manager"
    Department = each.value.dept
  }
}

Example with a List (Requires toset())

for_each does not accept a standard list because list indices can change, which causes Terraform to destroy and recreate resources unpredictably. If you have a list of strings, you must convert it to a set using toset().

variable "env_names" {
  type    = list(string)
  default = ["dev", "staging", "prod"]
}

resource "aws_vpc" "vpc" {
  # toset() converts ["dev", "staging", "prod"] into a set
  for_each = toset(var.env_names)

  cidr_block = "10.0.0.0/16"
 
  tags = {
    Name = each.value # or each.key (they are identical in a set)
  }
}


2. The for Expression

Use for when you need to manipulate, filter, or transform data inside an attribute. It is always wrapped in brackets: [...] to output a list, or {...} to output a map.

Example with a List (Outputting a List)

Let's say you have a list of environment names, and you want to convert them all to uppercase to format them as tags.


variable "environments" {
  type    = list(string)
  default = ["dev", "staging", "prod"]
}

output "uppercase_environments" {
  # Loops through the list and outputs a new list
  value = [for env in var.environments : upper(env)]
}
# Output: ["DEV", "STAGING", "PROD"]


1. The Map Constructor: { for ... : key => value }

When you wrap a for loop in curly braces { } and use the => operator, you are telling Terraform: "Take this data and output a Map."

variable "roles" {
  type = map(string)
  default = {
    alice = "engineer"
    bob   = "manager"
  }
}

output "map_output" {
  value = { for name, role in var.roles : name => upper(role) }
}

2. The List Constructor: [ for ... : value ]

If you want to pull just the values (or just the keys) out of a map and turn them into a List, you must switch from curly braces { } to square brackets [ ], and drop the => operator.

(Note: In your snippet, you used curly braces without an arrow. In Terraform, writing { for name, role in var.roles : upper(role) } will actually throw a syntax error. To get a list/set, you must use square brackets).


variable "roles" {
  type = map(string)
  default = {
    alice = "engineer"
    bob   = "manager"
  }
}

output "list_output" {
  # Notice the square brackets [ ] instead of curly braces
  value = [ for name, role in var.roles : upper(role) ]
}






Comments

Popular posts from this blog

mongosh install properly

gemini cli getting file not defined error

vllm : Failed to infer device type