If you've ever found yourself copying and pasting the same Terraform resource block just to create slight variations, you're doing more work than necessary. Terraform's count meta-argument exists exactly for this scenario.
Instead of duplicating code, you can declaratively define how many instances of a resource you want—and let Terraform handle the rest.
What is Terraform Count?
count is a meta-argument that allows you to create multiple instances of a resource (or module) using a single block of configuration.
At its simplest, it looks like this:
1resource "aws_instance" "web" {
2 count = 3
3
4 ami = "ami-123456"
5 instance_type = "t3.micro"
6}This will create three EC2 instances:
- aws_instance.web[0]
- aws_instance.web[1]
- aws_instance.web[2]
Each instance is indexed numerically, starting from 0.
Using count.index for Customization
Here's where things get interesting. Terraform gives you access to count.index, which represents the current instance number.
You can use it to customize each resource:
1resource "aws_instance" "web" {
2 count = 3
3
4 ami = "ami-123456"
5 instance_type = "t3.micro"
6
7 tags = {
8 Name = "web-${count.index}"
9 }
10}This produces instances named:
- web-0
- web-1
- web-2
It’s simple but surprisingly powerful.
Driving Count with Variables
Hardcoding numbers isn’t very flexible. A common pattern is to control count through variables:
1variable "instance_count" {
2 type = number
3 default = 2
4}
5
6resource "aws_instance" "web" {
7 count = var.instance_count
8
9 ami = "ami-123456"
10 instance_type = "t3.micro"
11}This makes your infrastructure adaptable across environments. For example:
- Dev → 1 instance
- Staging → 2 instances
- Production → 5 instances
Conditional Resource Creation with Count
A neat trick developers often use is leveraging count for conditional logic.
1resource "aws_s3_bucket" "logs" {
2 count = var.enable_logs ? 1 : 0
3
4 bucket = "my-app-logs"
5}If enable_logs is false, Terraform simply doesn't create the resource.
This avoids messy conditional blocks and keeps configurations clean.
Referencing Resources Created with Count
When you use count, you must reference resources using index notation.
1output "instance_ids" {
2 value = aws_instance.web[*].id
3}The [*] syntax collects values from all instances.
To access a specific instance:
1aws_instance.web[0].idWhere Count Starts to Break Down
Count is great—until your resources stop being identical.
Consider this scenario:
1variable "instance_names" {
2 default = ["api", "worker", "frontend"]
3}
4
5resource "aws_instance" "web" {
6 count = length(var.instance_names)
7
8 tags = {
9 Name = var.instance_names[count.index]
10 }
11}This works fine initially. But what happens if you remove "worker" from the list?
Terraform will shift indexes and potentially destroy and recreate resources unexpectedly.
This is one of the biggest pitfalls of Terraform count: it is index-based, not identity-based.
Count vs for_each (Quick Reality Check)
A common mistake developers make is overusing count when for_each is a better fit.
Use count when:
- Resources are nearly identical
- You only need a number (e.g., 3 instances)
Use for_each when:
- Each resource has a unique identity
- You are working with maps or sets
- You want stable resource tracking
If you’re dealing with named resources or frequently changing lists, for_each will save you from painful state issues.
Practical Example: Multi-AZ Deployment
Let’s put everything together with a realistic scenario:
1variable "availability_zones" {
2 default = ["us-east-1a", "us-east-1b", "us-east-1c"]
3}
4
5resource "aws_instance" "web" {
6 count = length(var.availability_zones)
7
8 ami = "ami-123456"
9 instance_type = "t3.micro"
10 availability_zone = var.availability_zones[count.index]
11
12 tags = {
13 Name = "web-${var.availability_zones[count.index]}"
14 }
15}This distributes instances across availability zones without repeating configuration.
Performance and State Considerations
Using count doesn’t just affect readability—it impacts Terraform state behavior:
- Index shifts can trigger resource recreation
- Large counts can slow down plan/apply operations
- Debugging becomes harder with numeric indexing
For small, predictable sets, count is efficient and clean. For dynamic infrastructure, it can become fragile.
Common Mistakes to Avoid
- Relying on list order that might change later
- Mixing count with complex conditional logic
- Using count where for_each provides better stability
- Forgetting to update references when switching from single to multiple resources
When Count Really Shines
Despite its limitations, count is perfect for:
- Stateless compute instances
- Auto-scaled baseline infrastructure
- Temporary or disposable environments
- Feature toggles (enable/disable resources)
In these cases, its simplicity is actually a strength.
Wrapping It Up
Terraform count is one of those features that feels trivial at first—but becomes incredibly useful once you start scaling infrastructure.
Use it when you need repetition without complexity. But be cautious when your infrastructure starts requiring identity and stability.
If you're ever unsure whether to use count or something more advanced, it's usually a sign to pause and evaluate how your resources evolve over time.