for_each vs count a practical guide in Terraform — Safer, Predictable, and Scalable IaC
✨ Introduction
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
countandfor_each - Controlled via toggles:
create_container_for_eachandcreate_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