# How to Iterate Through a Terraform Map Variable: Complete Guide to `for` Expressions and `for_each`

> Learn to iterate through Terraform map variables using for expressions and for_each. Transform data and create multiple resources efficiently with this complete guide.

- Repository: [HashiCorp/terraform](https://github.com/hashicorp/terraform)
- Tags: how-to-guide
- Published: 2026-02-19

---

**Use `for` expressions to transform map data inside locals or outputs, and use the `for_each` meta-argument to create multiple resource instances from a map.**

Terraform map variables are collections of key-value pairs that require specific iteration patterns to process effectively. In the `hashicorp/terraform` repository, the language implementation distinguishes between transforming data with expressions and instantiating infrastructure with meta-arguments. This guide covers both approaches using actual source patterns from the Terraform language documentation.

## Understanding Terraform Map Variables

A **map variable** in Terraform is defined as `map(<type>)` and contains unique keys paired with values of the specified type. Unlike lists, maps provide named access to values, making them ideal for configuration objects like tags, region mappings, or user definitions.

```hcl
variable "server_ami" {
  type = map(string)
  default = {
    "us-east-1" = "ami-0c55b159cbfafe1f0"
    "eu-west-1" = "ami-0a1b2c3d4e5f67890"
  }
}

```

## Method 1: Using `for` Expressions

The `for` expression is the primary mechanism for iterating through a Terraform map variable to transform data. According to the source documentation in [`website/docs/language/expressions/for.md`](https://github.com/hashicorp/terraform/blob/main/website/docs/language/expressions/for.md), this expression creates new collections without instantiating resources.

### Basic Syntax and List Transformation

The standard syntax iterates over key-value pairs and produces a list:

```hcl
locals {
  ami_list = [for region, ami in var.server_ami : ami]
}

```

This pattern extracts all values from the map into a list. You can also capture keys: `[for k, v in var.map : k]`.

### Filtering Maps with Conditional Logic

Add an `if` clause to filter entries during iteration:

```hcl
variable "users" {
  type = map(object({
    enabled = bool
    role    = string
  }))
}

locals {
  active_admins = {
    for name, info in var.users : name => info.role
    if info.enabled && info.role == "admin"
  }
}

```

### Transforming to Complex Objects

Create nested objects by returning a map with the `=>` syntax:

```hcl
locals {
  enriched_servers = {
    for region, ami in var.server_ami : region => {
      ami_id       = ami
      region_label = upper(region)
      timestamp    = timestamp()
    }
  }
}

```

## Method 2: Using `for_each` Meta-Argument

When you need to create one resource instance per map entry, use the `for_each` meta-argument. The implementation in [`website/docs/language/meta-arguments/for_each.md`](https://github.com/hashicorp/terraform/blob/main/website/docs/language/meta-arguments/for_each.md) specifies that this creates a distinct resource object for each element.

### Resource Creation Pattern

```hcl
variable "buckets" {
  type = map(string)  # {"logs" = "us-east-1", "backups" = "eu-central-1"}

}

resource "aws_s3_bucket" "storage" {
  for_each = var.buckets

  bucket = "${each.key}-data"
  region = each.value

  tags = {
    Environment = "production"
    BucketType  = each.key
  }
}

```

### Key Constraints and Behavior

The `for_each` meta-argument has specific requirements according to the Terraform source:

- **Known values required**: The map must be fully known at plan time. Terraform cannot determine resource instance addresses if keys are computed values unknown until apply.
- **Resource addressing**: Each instance receives an address like `aws_s3_bucket.storage["logs"]`, allowing individual targeting.
- **Access syntax**: Inside the block, `each.key` provides the map key and `each.value` provides the associated value.

## Helper Functions: `keys()` and `values()`

Terraform provides built-in functions that often eliminate the need for explicit `for` expressions. These are defined in the core language functions:

```hcl
locals {
  all_regions   = keys(var.server_ami)    # Returns ["us-east-1", "eu-west-1"]

  all_amis      = values(var.server_ami)  # Returns ["ami-0c55...", "ami-0a1b..."]

}

```

Use `keys()` when you need only the identifiers, and `values()` when processing just the data elements.

## Common Patterns and Examples

### Conditional Resource Creation

Filter a map before passing to `for_each`:

```hcl
resource "aws_instance" "server" {
  for_each = {
    for name, config in var.instances : name => config
    if config.enabled
  }

  ami           = each.value.ami
  instance_type = each.value.instance_type
}

```

### Converting Map to List of Objects

Useful when a module requires list input but you have map data:

```hcl
locals {
  user_list = [
    for name, role in var.users : {
      username = name
      role     = role
    }
  ]
}

```

## Summary

- Use **`for` expressions** in `locals` or `outputs` to transform map data into lists, sets, or new maps without creating resources.
- Use **`for_each`** when creating resources or modules, with each map entry generating one instance accessible via `each.key` and `each.value`.
- Reference the canonical documentation in [`website/docs/language/expressions/for.md`](https://github.com/hashicorp/terraform/blob/main/website/docs/language/expressions/for.md) and [`website/docs/language/meta-arguments/for_each.md`](https://github.com/hashicorp/terraform/blob/main/website/docs/language/meta-arguments/for_each.md) for implementation details.
- Leverage built-in **`keys()`** and **`values()`** functions for simple extraction tasks.
- Ensure maps used with `for_each` contain only known values at plan time to avoid evaluation errors.

## Frequently Asked Questions

### What is the difference between `for` and `for_each` in Terraform?

A `for` expression is a functional construct that transforms data within expressions, producing new collections like lists or maps. It operates purely in memory and never creates infrastructure. The `for_each` meta-argument is a resource-level configuration that instructs Terraform to create a separate resource instance for each element in a map or set, with each instance maintaining its own state.

### Can I use `for_each` with a map that has computed values?

No. According to the Terraform source implementation in [`website/docs/language/meta-arguments/for_each.md`](https://github.com/hashicorp/terraform/blob/main/website/docs/language/meta-arguments/for_each.md), the keys of a map used with `for_each` must be known at plan time. If the map keys are derived from computed attributes (such as resource IDs generated during apply), Terraform cannot determine the resource instance addresses during the planning phase, resulting in an error. Use static keys or data sources that resolve during refresh.

### How do I access the key and value inside a `for_each` resource?

Inside a resource or module block that uses `for_each`, Terraform provides two special objects: `each.key` contains the current map key (or set element), and `each.value` contains the associated map value. For example, if iterating over `var.users` where keys are usernames and values are role strings, `each.key` would be `"admin"` and `each.value` would be `"Administrator"` for that specific instance.

### When should I use `keys()` or `values()` instead of a `for` expression?

Use the built-in `keys()` function when you need only the map keys as a list, and `values()` when you need only the values as a list. These functions are more concise and readable than writing `[for k, v in var.map : k]` or `[for k, v in var.map : v]`. However, if you need to filter entries with an `if` clause, transform values, or construct objects, you must use a full `for` expression.