Understanding the Differences Between Terraform Count and For_each
4 min read

Terraform, as a powerful Infrastructure as Code (IaC) tool, provides two key constructs—count
and for_each
—to manage resource creation dynamically. While both enable you to create multiple resources efficiently, they differ in their flexibility and use cases. In this article, we’ll explore the differences between count
and for_each
, and when to use each.
What is Terraform Count?
The count
meta-argument is a simple way to create multiple instances of a resource. By specifying a numeric value for count
, Terraform will create that many instances of the resource.
Example:
resource "azurerm_virtual_machine" "example" {
count = 3
name = "vm-${count.index}"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
network_interface_ids = [azurerm_network_interface.example[count.index].id]
vm_size = "Standard_DS1_v2"
}
In this example:
Terraform creates three virtual machines.
The
count.index
value (0, 1, 2) is used to generate unique names for each instance.
Key Features of Count:
Index-based: Resources are identified by their index.
Simple: Best suited for scenarios where all instances share similar configurations.
Limitations: Less flexible when dealing with heterogeneous resource configurations.
What is Terraform For_each?
The for_each
meta-argument is more flexible and allows you to create resources based on a set, map, or list. Each instance is uniquely identified by a key rather than an index.
Example:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=3.47.0"
}
}
}
#https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret
provider "azurerm" {
features {}
client_id = "00000000-0000-0000-0000-000000000000"
client_secret = "20000000-0000-0000-0000-000000000000"
tenant_id = "10000000-0000-0000-0000-000000000000"
subscription_id = "20000000-0000-0000-0000-000000000000"
}
#variables are declared here
variable "resourcedetails" {
type = map(object({
name = string
location = string
size = string
rg_name = string
vnet_name = string
subnet_name = string
}))
default = {
westus = {
rg_name = "westus-rg"
name = "west-vm"
location = "westus2"
size = "Standard_B2s"
vnet_name = "west-vnet"
subnet_name = "west-subnet"
}
eastus = {
rg_name = "eastus-rg"
name = "east-vm"
location = "eastus"
size = "Standard_B1s"
vnet_name = "east-vnet"
subnet_name = "east-subnet"
}
}
}
resource "azurerm_resource_group" "myrg" {
for_each = var.resourcedetails
name = each.value.rg_name
location = each.value.location
}
resource "azurerm_virtual_network" "myvnet" {
for_each = var.resourcedetails
name = each.value.vnet_name
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.myrg[each.key].location
resource_group_name = azurerm_resource_group.myrg[each.key].name
}
resource "azurerm_subnet" "mysubnet" {
for_each = var.resourcedetails
name = each.value.subnet_name
address_prefixes = ["10.0.0.0/24"]
virtual_network_name = azurerm_virtual_network.myvnet[each.key].name
resource_group_name = azurerm_resource_group.myrg[each.key].name
}
resource "azurerm_network_interface" "mynic" {
for_each = var.resourcedetails
name = "my-nic"
location = azurerm_resource_group.myrg[each.key].location
resource_group_name = azurerm_resource_group.myrg[each.key].name
ip_configuration {
name = "my-ip-config"
subnet_id = azurerm_subnet.mysubnet[each.key].id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_virtual_machine" "vm" {
for_each = var.resourcedetails
name = each.value.name
location = azurerm_resource_group.myrg[each.key].location
resource_group_name = azurerm_resource_group.myrg[each.key].name
network_interface_ids = [azurerm_network_interface.mynic[each.key].id]
vm_size = each.value.size
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
storage_os_disk {
name = "${each.value.name}-osdisk"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
os_profile {
computer_name = each.value.name
admin_username = "adminuser"
admin_password = "Password1234!"
}
os_profile_linux_config {
disable_password_authentication = false
}
}
In this example:
- Two virtual machines are created, each with a unique name and size
Key Features of For_each:
Key-based: Resources are identified by unique keys.
Flexible: Ideal for scenarios where instances require different configurations.
Dynamic: Can adapt to changes in the input set or map.
Key Differences Between Count and For_each
Feature | Count | For_each |
Resource Identification | Index-based (count.index ) | Key-based (each.key ) |
Input Type | Numeric value | Set, map, or list |
Use Case | Homogeneous resource configurations | Heterogeneous resource configurations |
Flexibility | Limited | High |
Dynamic Updates | Challenging with changing counts | Adapts easily to input changes |
When to Use Count
All resources have similar configurations.
You know the exact number of resources required upfront.
Simpler scenarios where flexibility is not a priority.
Example Use Case:
Creating multiple identical storage accounts.
resource "azurerm_storage_account" "example" {
count = 5
name = "storage${count.index}"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
When to Use For_each
Resources require unique configurations.
You need to manage resources based on dynamic or changing input data.
Resources need to be uniquely identified by a key.
Example Use Case:
Creating VMs with different configurations for a development and production environment.
resource "azurerm_virtual_machine" "example" {
for_each = {
dev = "Standard_DS1_v2"
prod = "Standard_DS2_v2"
}
name = each.key
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
vm_size = each.value
}
Conclusion
Both count
and for_each
are powerful tools in Terraform for managing multiple resources, but they serve different purposes. Use count
for simple, uniform resource creation and for_each
for more complex, dynamic scenarios. Understanding their differences and use cases will help you write more efficient and maintainable Terraform configurations.
By leveraging the right construct for the right situation, you can optimize your Infrastructure as Code workflows and better manage your cloud resources.