Skip to main content

Using For-Each Meta-Argument

1. Using For-Each with Maps

Property Description Example
for_each Iterates over a map or set of strings to create multiple resources or blocks for_each = { env1 = "prod", env2 = "staging" }

Example: Creating Multiple S3 Buckets Using a Map

resource "aws_s3_bucket" "bucket" {
  for_each = {
    prod   = "production-bucket"
    staging = "staging-bucket"
  }

  bucket = each.value
  tags = {
    Environment = each.key
  }
}
Note: The each.key and each.value variables are available inside the loop.

Key Points

  • for_each supports maps and sets of strings
  • Each resource created has a unique each.key and each.value
  • Useful for creating multiple resources with different configurations

2. Using For-Each with Sets

The for_each meta-argument in Terraform allows you to create multiple instances of a resource or module based on a set or map. When using sets, for_each iterates over the elements of the set.

Property Description Example
for_each Defines the set or map to iterate over. Each element in the set becomes a separate instance of the resource. for_each = ["app1", "app2", "app3"]

Example: Creating Multiple S3 Buckets Using a Set

resource "aws_s3_bucket" "bucket" {
  for_each = toset([
    "my-bucket-1",
    "my-bucket-2",
    "my-bucket-3"
  ])

  bucket = each.value
}

Each instance of the resource will have a unique each.value, which is the value from the set. The resource can be referenced using aws_s3_bucket.bucket["my-bucket-1"].

Note: The for_each meta-argument is not supported in the root module and requires a set or map as input.

3. Accessing Each Iterator

Property Description Example
each.key Key of the current item in the loop each.key
each.value Value of the current item in the loop each.value

Example: Using each.key and each.value

resource "aws_instance" "web" {
  for_each = {
    dev = "t2.micro"
    prod = "t3.large"
  }

  instance_type = each.value
  tags = {
    Environment = each.key
  }
}

4. Referencing For-Each Resources

Resource Reference Syntax Description
aws_instance.example aws_instance.example["name"] Reference a specific instance from a for_each loop using the key name
aws_s3_bucket.example aws_s3_bucket.example["bucket_name"] Access a specific bucket created with for_each by its key
local_file.example local_file.example["file_name"] Access a specific local file resource created with for_each

Example: Referencing For-Each Resources in Expressions

resource "aws_instance" "web" {
  for_each = {
    us-west-1 = "t2.micro"
    us-east-1 = "t2.small"
  }

  instance_type = each.value
}

output "instance_ids" {
  value = [for instance in aws_instance.web : instance.id]
}
Note: When referencing for_each resources, the key must match exactly what was defined in the for_each map or set.
Warning: Using each.key and each.value inside resource blocks is only valid within the context of a for_each loop.

5. Comparing Count vs For-Each

Feature Count For-Each
Usage count = 3 for_each = var.instances
Iteration Type Integer-based (0, 1, 2) Map or Set-based
Access to Index Yes: count.index No direct index access
Resource Reference resource.name[count.index] resource.name[key]
State Management Recreates resources if count changes Updates state without recreating
Use Case Fixed number of resources Dynamic resource creation from map/set

Example: Count Usage

resource "aws_instance" "web" {
  count = 3
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  tags = {
    Name = "web-${count.index}"
  }
}

Example: For-Each Usage

variable "instances" {
  type = map(string)
  default = {
    "web1" = "t2.micro"
    "web2" = "t2.small"
  }
}

resource "aws_instance" "web" {
  for_each = var.instances
  ami           = "ami-12345678"
  instance_type = each.value
  tags = {
    Name = "web-${each.key}"
  }
}

When to Use Which?

  • Use count when you know the exact number of resources to create.
  • Use for_each when the number of resources is determined dynamically from a map or set.

6. Using For-Each with Modules

Property Description Example
for_each Iterates over a map or set of strings to create multiple instances of a module for_each = var.instances

Example: Using for_each with a Module

module "web_server" {
  source = "./web_server_module"
  for_each = var.instances
  name   = each.key
  count  = each.value.count
}
Note: The each.key and each.value are available inside the module to reference the current iteration's key and value.

Example: Module Input Variables

# In the module
variable "name" {
  type = string
}

variable "count" {
  type = number
}

Example: Variable Definition

variable "instances" {
  type = map(object({
    count = number
  }))
  default = {
    "prod" = { count = 3 }
    "dev"  = { count = 1 }
  }
}

7. Converting Lists to Maps

When using for_each, Terraform requires a map or set of strings. If you have a list, you can convert it to a map using the zipmap function.

Function Description Example
zipmap Creates a map from two lists: one for keys and one for values. zipmap(keys, values)

Example: Converting a List to a Map

locals {
  servers = [
    "web1",
    "web2",
    "db1"
  ]

  server_map = zipmap(
    local.servers,
    [for s in local.servers : "server-" + s]
  )
}

resource "aws_instance" "example" {
  for_each = local.server_map

  instance_type = "t3.micro"
  ami           = "ami-12345678"
}
Note: The zipmap function is useful for creating maps from lists where each list element becomes a key, and the value can be derived from the same list or another source.

8. Handling For-Each Changes

Change Type Description Impact Action Required
Adding new elements to for_each Adding new keys to the for_each map or list New resources/instances created Yes, Terraform creates new resources
Removing elements from for_each Deleting keys from the for_each map or list Resources destroyed Yes, Terraform destroys removed resources
Modifying existing keys/values Changing values for existing keys in for_each Resources updated or recreated Yes, depending on resource type and change
Reordering elements in list Changing order of items in for_each list No resource changes No, Terraform handles reordering safely

Example: Adding new elements to for_each

resource "aws_instance" "web" {
  for_each = {
    dev = "t3.micro"
    prod = "t3.large"
    staging = "t3.medium"  // New element added
  }

  instance_type = each.value
  ami           = "ami-12345678"
}
Note: When modifying for_each, Terraform compares the new and old sets of keys to determine what needs to be created, updated, or destroyed.
Warning: Changing for_each values can lead to resource recreation if the change affects the resource's configuration. Always review the plan before applying.

9. Using Dynamic Keys

When using for_each with dynamic keys, you can leverage expressions to generate keys dynamically based on input values or other attributes.

Key Expression Description Example
for_each Used to iterate over a map or list of strings to create multiple resources for_each = var.instances
each.key Represents the key of the current iteration name = each.key
each.value Represents the value of the current iteration ami = each.value.ami_id

Example: Dynamic Keys from a Map

variable "instances" {
  type = map(object({
    ami_id = string
    instance_type = string
  }))
}

resource "aws_instance" "web" {
  for_each = var.instances

  ami           = each.value.ami_id
  instance_type = each.value.instance_type
  tags = {
    Name = each.key
  }
}
Note: When using for_each, the keys must be strings. If your input is not a string, you can use toString() to convert it.

Example: Converting Non-String Keys to Strings

variable "instance_ids" {
  type = list(number)
}

resource "aws_instance" "web" {
  for_each = { for id in var.instance_ids : tostring(id) => id }

  ami           = "ami-12345678"
  instance_type = "t2.micro"
}

10. Filtering For-Each Collections

Property Description Example
for_each Iterates over a map or set of strings to create multiple resources or modules for_each = { name1 = "value1", name2 = "value2" }
each.key Accesses the key of the current iteration name = each.key
each.value Accesses the value of the current iteration value = each.value

Example: Filtering with for_each

resource "aws_instance" "web" {
  for_each = { 
    "prod" = "t3.medium"
    "dev" = "t3.micro"
  }

  instance_type = each.value
  tags = {
    environment = each.key
  }
}
Note: The for_each meta-argument is useful for creating multiple instances of a resource with different configurations based on a map or set.

Was this article helpful?