← Back to list

for_each vs count a practical guide in Terraform — Safer, Predictable, and Scalable IaC

✨ Introduction

Anilkumar Maheswarla · 2025-11-04 09:35 · 0 claps · 4.9 min read paywalled
#terraform #terraform-modules #foreach-loop #count #azure
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

for_each vs count: A practical guide in Terraform — Safer, Predictable, and Scalable IaC

✨ Introduction

When building reusable Terraform modules, choosing between count and for_each can make or break your flexibility. In this post, we’ll compare both using a real-world example: provisioning Azure Storage containers and blobs using both strategies.

🧱 Setup Overview

We’ll provision:

  • A single Azure Storage Account
  • Multiple containers and blobs using both count and for_each
  • Controlled via toggles: create_container_for_each and create_container_count

🔍 What’s the Difference?

Let’s deep dive with practical example

blob_for_each.tf

# for_each version of blobs
resource "azurerm_storage_container" "demo" {
  for_each = var.create_container_for_each? var.containers_for_each : {}

  name                  = each.key # images # docs
  storage_account_id    = azurerm_storage_account.demo.id
  container_access_type = each.value # private # blob
}

resource "azurerm_storage_blob" "demo" {
  for_each = var.create_container_for_each? var.blobs_for_each : {}

  name                   = each.key
  storage_account_name   = azurerm_storage_account.demo.name
  storage_container_name = azurerm_storage_container.demo[each.value.container].name #each.value.container #azurerm_storage_container.demo[each.value.container].name # images 
  type                   = each.value.type
  source                 = each.value.source
}

Non-Medium member but still want to read this article ? Use this link https://medium.com/@anilkumar.mah99/for-each-vs-count-a-practical-guide-in-terraform-safer-predictable-and-scalable-iac-3da0d9744861?sk=7560c21e3686c10747f36f6a9b2a3840

variables_for_each.tf

variable "create_container_for_each" {
  description = "conditon to true or false"
  type = bool
  default = true
}

variable "containers_for_each" {
  type = map(string)
  default = {
    images = "private"
    docs   = "blob"
  }
}

variable "blobs_for_each" {
  type = map(object({
    container = string
    source    = string
    type      = string
  }))
  default = {
    "data.csv" = {
      container = "images"
      source    = "images/data.csv"
      type      = "Block"
    },
    "Terraform.png" = {
      container = "images"
      source    = "images/Terraform.png"
      type      = "Block"
    }
  }
  validation {
    condition     = length(keys(var.blobs_for_each)) <= 3
    error_message = "You can only define up to 2 blob objects."
  }
}

terraform plan

  # azurerm_storage_blob.demo["Terraform.png"] will be created
  + resource "azurerm_storage_blob" "demo" {
      + access_tier            = (known after apply)
      + content_type           = "application/octet-stream"
      + id                     = (known after apply)
      + metadata               = (known after apply)
      + name                   = "Terraform.png"
      + parallelism            = 8
      + size                   = 0
      + source                 = "images/Terraform.png"
      + storage_account_name   = (known after apply)
      + storage_container_name = "images"
      + type                   = "Block"
      + url                    = (known after apply)
    }

  # azurerm_storage_blob.demo["data.csv"] will be created
  + resource "azurerm_storage_blob" "demo" {
      + access_tier            = (known after apply)
      + content_type           = "application/octet-stream"
      + id                     = (known after apply)
      + metadata               = (known after apply)
      + name                   = "data.csv"
      + parallelism            = 8
      + size                   = 0
      + source                 = "images/data.csv"
      + storage_account_name   = (known after apply)
      + storage_container_name = "images"
      + type                   = "Block"
      + url                    = (known after apply)
    }

  # azurerm_storage_container.demo["docs"] will be created
  + resource "azurerm_storage_container" "demo" {
      + container_access_type             = "blob"
      + default_encryption_scope          = (known after apply)
      + encryption_scope_override_enabled = true
      + has_immutability_policy           = (known after apply)
      + has_legal_hold                    = (known after apply)
      + id                                = (known after apply)
      + metadata                          = (known after apply)
      + name                              = "docs"
      + resource_manager_id               = (known after apply)
      + storage_account_id                = (known after apply)
    }

  # azurerm_storage_container.demo["images"] will be created
  + resource "azurerm_storage_container" "demo" {
      + container_access_type             = "private"
      + default_encryption_scope          = (known after apply)
      + encryption_scope_override_enabled = true
      + has_immutability_policy           = (known after apply)
      + has_legal_hold                    = (known after apply)
      + id                                = (known after apply)
      + metadata                          = (known after apply)
      + name                              = "images"
      + resource_manager_id               = (known after apply)
      + storage_account_id                = (known after apply)
    }

As you see terraform plan each blob creation is with its key. If any addition or removal of an element in variable “blobs_for_each” there is no destroy and recreate for the existing elements.

Now lets test the code with count:

blob_count.tf

resource "azurerm_storage_container" "demo_count" {
  count                 = var.create_container_count? length(var.containers_count) : 0
  name                  = var.containers_count[count.index].name
  storage_account_id    = azurerm_storage_account.demo.id
  container_access_type = var.containers_count[count.index].access_type
}

resource "azurerm_storage_blob" "demo_count" {
  count = var.create_container_count ? length(var.blobs_count) : 0

  name                   = var.blobs_count[count.index].name
  storage_account_name   = azurerm_storage_account.demo.name
  storage_container_name = azurerm_storage_container.demo_count[
    local.container_index_map[var.blobs_count[count.index].container]
  ].name
  type                   = var.blobs_count[count.index].type
  source                 = var.blobs_count[count.index].source
}

Variables_count.tf

variable "create_container_count" {
  description = "conditon to true or false"
  type = bool
  default = true
}

variable "containers_count" {
  type = list(object({
    name  = string
    access_type = string
  }))
  default = [
    { name = "images-count", access_type = "private" },
    { name = "docs-count", access_type = "blob" }
  ]
}

variable "blobs_count" {
  type = list(object({
    name      = string
    container = string
    type      = string
    source    = string
  }))
  default = [ 
   { 
      name      = "Terraform.png"
      container = "images-count"
      source    = "images/Terraform.png"
      type      = "Block"
  },
  {
      name      = "data.csv"
      container = "images-count"
      source    = "images/data.csv"
      type      = "Block"
  }
  ]
  validation {
    condition     = length(var.blobs_count) <= 3
    error_message = "You can only define up to 2 blob objects."
  }
}

terraform plan

 # azurerm_storage_blob.demo_count[0] will be created
  + resource "azurerm_storage_blob" "demo_count" {
      + access_tier            = (known after apply)
      + content_type           = "application/octet-stream"
      + id                     = (known after apply)
      + metadata               = (known after apply)
      + name                   = "Terraform.png"
      + parallelism            = 8
      + size                   = 0
      + source                 = "images/Terraform.png"
      + storage_account_name   = (known after apply)
      + storage_container_name = "images-count"
      + type                   = "Block"
      + url                    = (known after apply)
    }

  # azurerm_storage_blob.demo_count[1] will be created
  + resource "azurerm_storage_blob" "demo_count" {
      + access_tier            = (known after apply)
      + content_type           = "application/octet-stream"
      + id                     = (known after apply)
      + metadata               = (known after apply)
      + name                   = "guide.pdf"
      + parallelism            = 8
      + size                   = 0
      + source                 = "docs/guide.pdf"
      + storage_account_name   = (known after apply)
      + storage_container_name = "docs-count"
      + type                   = "Block"
      + url                    = (known after apply)
    }

  # azurerm_storage_container.demo_count[0] will be created
  + resource "azurerm_storage_container" "demo_count" {
      + container_access_type             = "private"
      + default_encryption_scope          = (known after apply)
      + encryption_scope_override_enabled = true
      + has_immutability_policy           = (known after apply)
      + has_legal_hold                    = (known after apply)
      + id                                = (known after apply)
      + metadata                          = (known after apply)
      + name                              = "images-count"
      + resource_manager_id               = (known after apply)
      + storage_account_id                = (known after apply)
    }

  # azurerm_storage_container.demo_count[1] will be created
  + resource "azurerm_storage_container" "demo_count" {
      + container_access_type             = "blob"
      + default_encryption_scope          = (known after apply)
      + encryption_scope_override_enabled = true
      + has_immutability_policy           = (known after apply)
      + has_legal_hold                    = (known after apply)
      + id                                = (known after apply)
      + metadata                          = (known after apply)
      + name                              = "docs-count"
      + resource_manager_id               = (known after apply)
      + storage_account_id                = (known after apply)
    }

As you see terraform plan each blob creation is with its index. If any addition or removal of an element in variable “blobs_count” there will be recreation of blobs to adjust its index for reference in state file.

When to Use What?

Use **for_each when:**

  • You have a map of named resources
  • You want stable keys and dynamic access

Use **count when:**

  • You’re iterating over a simple list
  • You don’t need to reference by name

Final Thougts:

✅ Why I Recommend for_each for Resource Stability

When working with dynamic infrastructure — especially in production or collaborative environments — resource stability is critical. This is where for_each shines over count.

🔁 The Problem with count

Using count ties each resource to a numeric index. If you insert or remove an element in the middle of the list, all subsequent resources shift, causing Terraform to destroy and recreate them—even if their content hasn’t changed.

🙏 If this guide helped you to understand importance of using for_each over count. Please give it a few claps 👏👏👏 to show your support! 🔔 Follow me for more practical insights on cloud automation, security, and DevOps best practices.


메타데이터
post_id
3da0d9744861
slug
for-each-vs-count-a-practical-guide-in-terraform-safer-predictable-and-scalable-iac-3da0d9744861
url
https://medium.com/@anilkumar.mah99/for-each-vs-count-a-practical-guide-in-terraform-safer-predictable-and-scalable-iac-3da0d9744861
canonical_url
https://medium.com/@anilkumar.mah99/for-each-vs-count-a-practical-guide-in-terraform-safer-predictable-and-scalable-iac-3da0d9744861
author_url
https://medium.com/@anilkumar.mah99
status
ok
fetched_at
2026-06-23 17:05:31