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_eachsupports maps and sets of strings- Each resource created has a unique
each.keyandeach.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
countwhen you know the exact number of resources to create. - Use
for_eachwhen 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.