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.
I help organisations secure their cloud infrastructure and stay ahead of evolving cyber threats. Microsoft MVP and Certified Trainer, author of Mastering Azure Security, and founder of arnav.au — a platform for practical Cloud, Cybersecurity, DevOps and AI content.
Frequently Asked Questions
The for_each meta-argument in Terraform is used to iterate over a data structure, such as a list or map, to configure resources or module blocks with each item in turn. It is particularly useful when you have multiple similar resources that share the same lifecycle but need different configurations, allowing you to deploy multiple resources without duplicating configuration code.
To use for_each with a list, you need to convert the list to a set using the toset() function, since for_each requires a map or set. You can then reference each item in the loop using each.value for the item's value and each.key for its index. For example, you can iterate over a list of names to create multiple virtual machines with different names.
Maps naturally provide uniqueness with their keys, making them ideal for for_each loops. Using maps allows you to define more detailed configurations for each resource, as map values can contain objects with multiple properties. This makes it easier to manage complex resource configurations where each instance needs different settings.
The count method is used when you want to create a fixed number of identical resources, while for_each is more suitable for resources that need unique configurations. Count and for_each are mutually exclusive, meaning you cannot use them together for a single resource, though workarounds exist to combine their functionalities.
Dynamic nested blocks allow you to build repeated nested configurations by combining the dynamic keyword with for_each. Within the dynamic block, you iterate over a data structure and use the content block to define the nested configuration, accessing loop values through the iterator name (e.g., security_rule.key and security_rule.value).