Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

count in Terraform

The count meta-argument creates multiple instances of a resource from a single block. It’s the simplest way to provision N identical (or nearly identical) resources — subnets across availability zones, IAM users from a list, replicas of a server.


Basic count Usage

# Create 3 identical EC2 instances
resource "aws_instance" "workers" {
count = 3
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Name = "worker-${count.index}" # worker-0, worker-1, worker-2
}
}

count.index is the zero-based index of the current instance (0, 1, 2, …).


count.index for Unique Naming

# Subnets across availability zones
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = var.availability_zones[count.index]
tags = {
Name = "public-${var.availability_zones[count.index]}"
Tier = "public"
}
}
# Reference by index
output "public_subnet_ids" {
value = aws_subnet.public[*].id # All subnet IDs as a list
}
# Access a specific instance
output "first_public_subnet_id" {
value = aws_subnet.public[0].id
}

count from Variable

variable "instance_count" {
type = number
default = 2
}
resource "aws_instance" "app" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = {
Name = "${var.environment}-app-${count.index + 1}" # 1-based for display
Index = count.index
}
}
# Scale to 0 to create no instances
# var.instance_count = 0 → creates nothing

count from a List

variable "user_names" {
type = list(string)
default = ["alice", "bob", "charlie"]
}
resource "aws_iam_user" "team" {
count = length(var.user_names)
name = var.user_names[count.index]
tags = {
CreatedBy = "terraform"
Index = count.index
}
}
output "user_arns" {
value = aws_iam_user.team[*].arn # All ARNs as a list
}

Conditional Resource Creation

Set count = 0 to create no instances, count = 1 to create one. Useful for optionally creating a resource:

variable "enable_bastion" {
type = bool
default = false
}
resource "aws_instance" "bastion" {
count = var.enable_bastion ? 1 : 0
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
}
# Access the conditionally-created resource
output "bastion_ip" {
value = var.enable_bastion ? aws_instance.bastion[0].public_ip : null
}
# Conditionally create an ElasticSearch domain in production only
resource "aws_elasticsearch_domain" "search" {
count = var.environment == "production" ? 1 : 0
domain_name = "search-production"
elasticsearch_version = "8.10"
}

Splat Expressions: Accessing All Instances

i.id]
# Collect all instance IDs
output "instance_ids" {
value = aws_instance.app[*].id
}
# Pass all subnet IDs to a load balancer
resource "aws_lb" "main" {
subnets = aws_subnet.public[*].id
}
# All security group IDs
resource "aws_db_instance" "main" {
vpc_security_group_ids = [aws_security_group.database[*].id]
}

count vs for_each: When to Use Which

# Use count when:
# - Creating N identical or index-differentiated resources
# - You only care about the position, not the identity
# Creating 5 identical workers
resource "aws_instance" "workers" {
count = 5
# ...
}
# Use for_each when:
# - Each instance has a unique, stable identity
# - You want to reference instances by name, not index
variable "users" {
type = set(string)
default = ["alice", "bob", "charlie"]
}
resource "aws_iam_user" "team" {
for_each = var.users
name = each.key # Stable identity: "alice", "bob", "charlie"
}

The critical count limitation: if you remove an element from the middle of a list, all subsequent indices shift — Terraform destroys and recreates resources unexpectedly.

# If users = ["alice", "bob", "charlie"] and you remove "alice":
# count = 3 → count = 2
# aws_iam_user.team[0] was "alice", becomes "bob"
# aws_iam_user.team[1] was "bob", becomes "charlie"
# aws_iam_user.team[2] ("charlie") is deleted
# Result: Terraform destroys "bob" and "charlie" and recreates them as "bob" and "charlie"
# But their history/permissions are lost during recreation
# With for_each: removing "alice" only deletes "alice" — "bob" and "charlie" are untouched

Use count for homogeneous resources where identity doesn’t matter (replicas, subnets). Use for_each when each instance is distinct.