Last Updated on August 7, 2025 by Arnav Sharma
To loop over a list in Terraform, you can use a for loop within a resource or module definition, or in any other part of your Terraform code where iteration might be necessary. Here’s a general approach to using a for loop in Terraform:
Example 1: Creating Multiple Azure Virtual Machines
In this example, we’ll create multiple Azure virtual machines (VMs) using for_each. Let’s assume we have a list of other resources like VM names.
locals {
vm_names = ["vm1", "vm2", "vm3"]
}
resource "azurerm_virtual_machine" "example" {
for_each = toset(local.vm_names)
name = each.value
location = "East US"
resource_group_name = azurerm_resource_group.example.name
network_interface_ids = [azurerm_network_interface.example[each.key].id]
vm_size = "Standard_F2"
# Other necessary configurations...
}
Here, each.value represents each VM name in the list.
Example 2: Creating Multiple Network Interfaces with Different Settings
If you have a list of settings for network interfaces and want to create a distinct network interface for each set of settings:
locals {
nics_settings = [
{ name = "nic1", ip_configuration_name = "ipconfig1" },
{ name = "nic2", ip_configuration_name = "ipconfig2" },
{ name = "nic3", ip_configuration_name = "ipconfig3" }
]
}
resource "azurerm_network_interface" "example" {
for_each = { for nic in local.nics_settings : nic.name => nic }
name = each.value.name
location = "East US"
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = each.value.ip_configuration_name
subnet_id = azurerm_subnet.example.id
private_ip_address_allocation = "Dynamic"
# Other configurations...
}
}
In this example, each network interface is configured with different settings based on the list of nics_settings.
Example 3: Using count to Create Multiple Storage Accounts
Here, we’ll create a specified number of Azure storage accounts using the count argument:
variable "storage_account_count" {
description = "Number of storage accounts to create"
default = 3
}
resource "azurerm_storage_account" "example" {
count = var.storage_account_count
name = "storageaccount${count.index}"
resource_group_name = azurerm_resource_group.example.name
location = "East US"
account_tier = "Standard"
account_replication_type = "LRS"
# Other configurations...
}
In this case, count.index is used to create a unique name for each storage account.
Note:
- Resource Dependencies: When creating multiple resources that depend on each other (like VMs and their network interfaces), ensure that your configuration accounts for these dependencies.
- State Management: Be mindful of how Terraform manages state. Using
for_eachwith a map or a set can make your infrastructure more resilient to changes in the list, as opposed to usingcount, which relies on the order of the list. - Azure Provider Version: Ensure that you are using a compatible version of the Azure provider for Terraform, as some features and resources depend on the provider version.
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
for_each iterates over a map or set and is more resilient to changes in the list order, while count relies on numerical indices and the order of items. Using for_each is generally preferred because if you remove an item from the middle of a list, it won't cause unintended changes to other resources, whereas count may reassign indices and affect state management.
When using for_each, you access items using the each object, which has two main attributes: each.key (the key from the map) and each.value (the actual value). In the examples, each.value is used to get the VM name or network interface settings for each iteration.
Yes, you can loop over a list of objects by converting it to a map using a for expression. As shown in Example 2, you can use `for_each = { for nic in local.nics_settings : nic.name => nic }` to iterate over a list of objects and access their properties using each.value.
count.index is a zero-based numerical index that represents the position of the current item in the iteration. It's useful for creating unique names or identifiers, such as in Example 3 where `"storageaccount${count.index}"` generates names like storageaccount0, storageaccount1, etc.
When creating resources that depend on each other, such as VMs requiring network interfaces, you must ensure proper dependency configuration in your Terraform code. Additionally, be mindful of state management—using for_each with maps is generally more resilient than count for handling infrastructure changes over time.