Overview
When working with Terraform, you may need to create multiple instances of the same resource. This is where count and for_each loops come in. These loops allow you to create multiple resources with the same configuration, but with different values. This guide will explain how to use count and for_each loops in Terraform.
Count in Terraform
The count
parameter in Terraform allows you to create a specified number of identical resources. It is an integral part of a resource block that defines how many instances of a particular resource should be created.
Here's an example of how to use count
in Terraform:
resource "azurerm_resource_group" "example" {
count = 3
name = "resourceGroup-${count.index}"
location = "East US"
tags = {
iteration = "Resource Group number ${count.index}"
}
}
In the example above, we create three identical resource groups in the Azure region "East US" with differing names using the count
parameter.
Pros:
-
Simple to use: The
count
parameter is straightforward for creating multiple instances of a resource. -
Suitable for homogeneous resources: When all the resources you're creating are identical except for an identifier,
count
is likely a good fit.
Cons:
-
Lacks key-based identification:
count
doesn’t include a way to address a resource with a unique key directly; you have to rely on an index. -
Immutable: If you remove an item from the middle of the
count
list, Terraform marks all subsequent resources for recreation which can be disruptive in certain scenarios. For example: Let's say you have a Terraform configuration that manages a fleet of virtual machines in Azure using thecount
parameter. Assume that you initially set thecount
parameter to 5, which provisioned five VMs:
resource "azurerm_virtual_machine" "vm" {
count = 5
name = "vm-${count.index}"
location = "East US"
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [azurerm_network_interface.nic[count.index].id]
# ... (other configuration details)
}
In the above example. Say After some time, you decide that you no longer need the second VM ("vm-1", since "count.index" is zero-based). To remove this VM, you might change the count
to 4
and adjust your resource names or indexes, which might intuitively seem like the correct approach.
The problem arises here: Terraform determines the creation and destruction of resources based on their index. If you simply remove or comment out the definition for "vm-1", Terraform won't know that you specifically want to destroy "vm-1". It would interpret that every VM from index 1 and onward (vm-1, vm-2, vm-3, and vm-4) should be destroyed and recreated because their indices have changed.
This could have several disruptive consequences:
- Downtime: Recreating VMs would lead to downtime for the services running on them, which may be unacceptable in a production environment.
- Data Loss: If there's local data on the VMs that you haven't backed up, it would be lost when the VMs are destroyed and recreated.
- IP Changes: If the VMs are assigned dynamic public IPs, these IPs would change and could cause connectivity issues.
- Costs: Destroying and recreating resources might incur unnecessary costs in terms of the compute hours consumed.
To avoid such issues with count
, you'd want to use create_before_destroy
lifecycle rules or consider whether for_each
is a better choice for such a scenario because it provides a way to uniquely identify resources without relying on sequence. With for_each
, each VM would be managed individually, and you could remove a single map entry that corresponds to the unwanted VM, leading to the destruction of only that particular VM without impacting the others.
For_Each in Terraform
The for_each
loop in Terraform, used within the for_each
argument, iterates over a map or a set of strings, allowing you to create resources that correspond to the given elements.
Here's an example of how to use for_each
in Terraform:
resource "azurerm_resource_group" "example" {
for_each = toset(["rg-prod", "rg-dev", "rg-test"])
name = each.value
location = "East US"
tags = {
Name = each.value
}
}
# Alternatively, using a map
resource "azurerm_storage_account" "example" {
for_each = {
prod = "eastus2"
dev = "westus"
test = "centralus"
}
name = "storage${each.key}"
resource_group_name = azurerm_resource_group.example[each.key].name
location = each.value
account_tier = "Standard"
account_replication_type = "GRS"
}
In the first example using a set of strings, we create resource groups with specific names: "rg-prod", "rg-dev", and "rg-test".
In the second example using a map, we create storage accounts in different locations and with associations to corresponding resource groups.
Pros:
-
Detailed declaration:
for_each
provides greater control when creating resources that require specific attributes or configurations. -
Key-based identification: Resources created with
for_each
can be directly identified and accessed by their keys, making modifications more manageable. - Non-destructive updates: If you remove an item from the map or set, only that specific resource will be affected.
Cons:
-
Complexity:
for_each
is more complex to use thancount
and requires more planning. - Requires a set or map: You must provide a set or map of items to iterate over, which might not be necessary or straightforward for all situations.
When to Use Count vs. For_each
Both constructs are powerful, but they shine in different situations. Here's a quick reference to determine which to use:
Use Count when:
- You need to create a fixed number of similar resources.
- Resource differences can be represented by an index.
Use For_each when:
- You're dealing with a collection of items that have unique identifiers.
- Your resources are not perfectly identical and require individual configurations.
- You plan to make future modifications that should not affect all resources.
Conclusion
Choosing between count
and for_each
largely depends on the scenario at hand. The count
parameter is excellent for simplicity and when you're dealing with homogenous resources. Meanwhile, for_each
is perfect for a more controlled resource declaration, offering flexibility and precision especially beneficial in complex infrastructures.
Author
Like, share, follow me on: 🐙 GitHub | 🐧 X/Twitter | 👾 LinkedIn
Top comments (0)