Last Updated on August 7, 2025 by Arnav Sharma
Terraform, a leading Infrastructure as Code (IaC) tool recommended by many on platforms like Medium, offers various meta-arguments to manage infrastructure components efficiently. One of these meta-arguments is the for_each loop, a powerful construct that allows you to deploy multiple similar resources without duplicating configuration files. In this blog, we’ll delve deep into the for_each meta-argument, its advantages, and practical examples.
Understanding the for_each Meta-Argument
The for_each meta-argument in Terraform is used to iterate over a data structure, such as a list or map, configuring resources or module blocks with each item in turn. This dynamic block is particularly useful when you have multiple similar resources, like virtual machines or Kubernetes pods, that share the same lifecycle but need different configurations. Terraform’s for_each is one of the looping constructs introduced in version 0.13, alongside the well-known count.
Basic Usage of for_each
Simple List Example:
Let’s take a list of Avengers as an input variable. Using for_each, we can iterate over this list to create multiple instances of a resource, e.g., virtual machines.
locals {
avengers = ["ironman", "captain america", "thor"]
}
resource "azurerm_virtual_machine" "avengers_vm" {
for_each = toset(local.avengers)
vm_size = "t2.nano"
name = each.value
tags = {
name = each.key
}
}
Using for_each with Maps:
Maps naturally provide uniqueness with their keys. Using for_each with a map of objects allows for more detailed configurations. For instance, deploying multiple EC2 instances using different configurations:
locals {
ec2_configs = {
"Ironman" = { instance_type = "t2.nano", ami = "ami-xyz" },
"Thor" = { instance_type = "t2.micro", ami = "ami-abc" }
}
}
resource "aws_ec2_instance" "heroes" {
for_each = local.ec2_configs
instance_type = each.value.instance_type
ami = each.value.ami
tags = {
Name = each.key
}
}
Advanced Usage with Dynamic Nested Blocks
Dynamic nested blocks in Terraform allow for building repeated nested configurations. For instance, when defining security group rules in Azure, we can use dynamic nested blocks to define multiple inbound security rules:
resource "azurerm_network_security_group" "example" {
name = "example-nsg"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
dynamic "security_rule" {
for_each = local.rules
content {
name = security_rule.key
priority = security_rule.value.priority
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = security_rule.value.port
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
}
Practical Azure Example with for_each
Using for_each, we can create multiple storage accounts in Azure. The following example demonstrates how to use for_each to create storage accounts with custom tags:
resource "azurerm_storage_account" "example" {
name = "storageaccount${each.key}"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
for_each = var.custom_tags
tags = {
environment = each.value
}
}
count vs. for_each
While both count and for_each are used for looping in Terraform, they serve different purposes. The count method is often used when you want to create a fixed number of identical resources. On the other hand, for_each is more suitable when dealing with resources that need unique configurations.
It’s essential to note that count and for_each are mutually exclusive, meaning you cannot use them together for a single resource. However, there are workarounds to combine their functionalities.
The for_each loop in Terraform provides a powerful way to manage multiple resources efficiently. By understanding its usage and nuances, developers and infrastructure engineers can harness its full potential to write cleaner and more maintainable Terraform code.