DEV Community

Cover image for Terraform - Understanding Count and For_Each Loops
Marcel.L
Marcel.L

Posted on β€’ Edited on

Terraform - Understanding Count and For_Each Loops

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}"
  }
}
Enter fullscreen mode Exit fullscreen mode

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 the count parameter. Assume that you initially set the count 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)
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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 than count 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

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

πŸ‘‹ Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay