Explain `for` expressions — how do you transform a list or map into another list or map?

5 minintermediateterraformfor-expressionshcl

Quick Answer

A `for` expression is HCL's list/map comprehension: `[for s in var.subnets : s.id]` transforms a list into a new list (here, extracting just the `id` from each object), while `{for s in var.subnets : s.name => s.id}` produces a map keyed by name. An optional `if` clause filters elements (`[for s in var.subnets : s.id if s.public]`), and wrapping the whole expression in `{...}` instead of `[...]` is what switches the result from a list to a map. `for` expressions are the standard way to reshape data pulled from one resource/variable into the exact shape another resource or a `for_each` needs.

Detailed Answer

for expressions are HCL's answer to "map over a list" or "build a dictionary from these elements" — the kind of data reshaping that comes up constantly once you're passing computed values from one resource into another's for_each or a structured output.

List-to-list

variable "subnets" {
  type = list(object({
    id     = string
    public = bool
  }))
}

locals {
  subnet_ids = [for s in var.subnets : s.id]
  # -> ["subnet-aaa", "subnet-bbb", "subnet-ccc"]
}

Square brackets ([...]) produce a list, with one output element per input element (unless filtered out).

List-to-map

locals {
  subnets_by_id = {for s in var.subnets : s.id => s}
  # -> { "subnet-aaa" = {...}, "subnet-bbb" = {...}, ... }
}

Curly braces ({...}) with a key => value pair instead produce a map — this single syntax change ([...] vs {...}) is what determines whether you get a list or a map out.

Filtering with if

locals {
  public_subnet_ids = [for s in var.subnets : s.id if s.public]
}

The optional trailing if clause skips elements that don't satisfy the condition — here, only including subnets where public == true.

Iterating over maps

locals {
  # var.tags is a map(string)
  tag_list = [for k, v in var.tags : "${k}=${v}"]
}

When iterating a map, for k, v in ... gives you both the key and value per iteration.

Why this matters practically

for expressions are the standard glue between resources: turning a list of objects a data source returned into the exact map shape a for_each needs, or reshaping a module's raw output into something a consuming resource can use directly — without writing a single line of imperative loop code, since HCL has no for statement, only this expression form.

Related Resources