Terraform

Safely Spawning Multiple Instances with Terraform's count Meta-Argument

2026-04-19
NicheeLab Editorial Team

count is the meta-argument for spawning multiple identical resources or modules from a single block. It expresses anything from 0 to N instances with minimal syntax.

Frequent exam topics include count.index, the splat operator ([*]), conditional creation (? 1 : 0), and how count differs from for_each.

count Fundamentals and Where It Applies

count is a meta-argument that produces N identical instances from a single resource block. Just feeding it a number lets you toggle between 0 (no instance) and N. Every reference becomes indexed, so even with a single instance you must write [0].

count works on modules as well as resources. It cannot be combined with for_each on the same block, so you have to pick one. References take the form resource.type.name[index].attribute for resources, and module.name[index].output for modules.

  • count must be a numeric expression. Expressions that cannot be resolved at plan time may raise an error.
  • When count = 0, no instances are created (the reference is treated as an empty list).
  • count.index is zero-based and can be used inside attribute references.
  • Best suited for creating identical instances from a single block when named identifiers are not required.

Real-World Patterns: Conditional Creation and N-Instance Fan-Out

The two patterns you'll use most often in practice are: toggling creation with a flag (count = var.enabled ? 1 : 0) and fanning out N identical resources (count = var.size). For the latter, weaving count.index into names and tags makes debugging much easier.

When a resource may have count=0, guard the reference (try or length check) to avoid evaluation errors during planning.

  • For conditional creation, reference safely with try(resource[0].attr, null).
  • For N-instance fan-out, embed the index in the naming convention and keep the configuration identical with no external dependencies.

Basic examples for conditional creation and N-instance fan-out

# Example: create a single NAT Gateway only when the flag is on
variable "enable_nat" {
  type    = bool
  default = false
}

resource "aws_nat_gateway" "nat" {
  count = var.enable_nat ? 1 : 0
  allocation_id = var.enable_nat ? aws_eip.nat[0].id : null
  subnet_id     = var.enable_nat ? aws_subnet.public[0].id : null
  tags = {
    Name = var.enable_nat ? "nat-0" : null
  }
}

# Safe guard on the reference side
output "nat_gateway_id" {
  value = try(aws_nat_gateway.nat[0].id, null)
}

# Example: spin up N identical web servers
variable "web_count" {
  type    = number
  default = 3
}

resource "aws_instance" "web" {
  count         = var.web_count
  ami           = var.ami_id
  instance_type = var.instance_type
  tags = {
    Name = format("web-%02d", count.index)
  }
}

# Collect every ID at once via splat
output "web_ids" {
  value = aws_instance.web[*].id
}

References: count.index and the Splat Operator

count.index is the zero-based index unique to each instance. Use it for naming, pulling values out of arrays, and tagging. Don't forget: if you used count, references need [0] even when there's only one instance.

To pull a specific attribute from every instance in one go, the splat operator ([*]) is your friend. resource.type.name[*].attribute returns a list that's easy to pass to other modules or data sources.

  • Single reference: resource.name[0].id
  • Bulk reference: resource.name[*].id
  • Bulk reference to module outputs: module.mod[*].output_name

Behavior on Changes and Plan Stability

count tends to produce large diffs when the count itself changes or when index-dependent attributes shift. In particular, if you assign names or CIDRs based on the index, reordering the source array forces many resources to be replaced.

The count value must be evaluable at plan time. If it depends on values that aren't known until apply (such as unknown attributes of other resources), planning fails with an error.

To keep destructive diffs in check: lock the input order (sort it), reach for for_each when you need identification that doesn't depend on the index, and design lifecycle blocks deliberately.

  • Reordering the array drags every index-based assignment along with it and can trigger sweeping replacements.
  • When count goes 3 → 2, index=2 is destroyed. When count goes 1 → 2, index=1 is newly created.
  • Avoid making count depend on values that aren't yet known at plan time.

Choosing Between count and for_each (with Comparison Table)

Both produce multiple instances, but they differ in how they handle identifiers and how resilient they are to changes. Use for_each when you want named, individualized instances; reach for count when you just need a fixed number of identical ones. This trade-off shows up often on the Associate exam.

  • If the keys are stable, for_each keeps diffs smaller.
  • If a count-based scaling model is enough, count is the most concise and readable option.
Aspectcountfor_eachNotes
IdentifierSequential numbers (0..n-1)Arbitrary key (string or number)Key stability is what minimizes diffs
Ordering / StabilityOrder-dependent (indexes get reassigned)Key-dependent (order-independent)for_each is resilient to reordering
Impact of ScalingTrailing reductions or gaps trigger reassignmentOnly the affected key is added or removedfor_each produces diffs that are easier to read in ops
Typical Use CaseN identical instances, toggle with a flagNamed subnets, map- or set-drivenMix and match depending on the requirements
Referencesres[idx].attr / res[*].attrres["key"].attr / res[*].attrThe syntax is the same for modules
Module SupportYesYesBoth are available in the current stable release

Using count with Modules and Handling Their Outputs

count works on modules too. Reference an individual instance with an index, like module.vpc[0], or grab them all at once via splat, like module.vpc[*].vpc_id. Module outputs become lists, which makes it safe to pipe them from a parent module into other modules or resources.

Just like resources, references take the module.vpc[0].output form even when count=1. When count=0 is possible, guard the access with try(module.vpc[0].output, null).

  • Aggregate module outputs into a list with module.mod[*].out.
  • Use module.mod[index].out for an individual reference.
  • When count=0 is allowed, wrap the reference in try for safety.

Conceptual diagram of count applied to a module

root modulemodule "vpc" { count = 2 }vpc (index 0)module.vpc[0].vpc_idvpc (index 1)module.vpc[1].vpc_idroot module → module.vpc[0] / module.vpc[1] → outputs

Check Your Understanding

Associate

問題 1

You assign subnet CIDRs using count and count.index, driven by the order of the list var.cidr_blocks. If you reorder var.cidr_blocks, which describes Terraform's behavior best?

  1. Many subnets are likely to be flagged for replacement (because the index-based assignment shifts)
  2. Terraform automatically preserves the previous index-to-CIDR mapping, so no replacements occur
  3. count is ignored at apply time and only one instance is ever created
  4. Writing depends_on locks the order and eliminates the diff

正解: A

count identifies instances by numeric index, so reordering the input shifts every index-based assignment (names, CIDRs, and so on), potentially causing widespread replacement. There is no mechanism that automatically preserves the previous mapping. To stabilize ordering, switch to for_each with stable keys.

Frequently Asked Questions

Should I use count or for_each?

Use count when you just want to scale identical resources up and down by number. Use for_each when you want each instance managed by a stable identifier (a name or ID). If minimizing diffs and resilience to future changes matter, for_each is the safer choice.

Referencing a resource that may have count=0 throws an error. How do I prevent that?

Guard the reference site with try(resource[0].attr, null) or length(resource) > 0 ? resource[0].attr : null. When count=0, the resource reference is an empty list, so taking [0] directly throws an evaluation error.

When count is applied to a module, how do I reference its outputs?

Use module.mod[index].output for an individual reference, or module.mod[*].output to collect every output. Even with count=1 you must use [0], and if count=0 is possible, protect the access with try(module.mod[0].output, null).

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Terraform

HCL Syntax: Terraform's Configuration Language (2026)

HCL2 fundamentals for Terraform — blocks, attributes, expres...

Terraform

Terraform Authoring & Operations Pro: Complete Guide (2026)

Tactics for the Terraform Pro exam — module authoring, works...

Terraform

Terraform Providers: Plugin Management Fundamentals (2026)

Provider mechanics — required_providers, versions, mirrors, ...

Terraform

Terraform Resource Blocks: Declarative Infra Units (2026)

Resource block fundamentals — addresses, references, common ...

Terraform

Terraform Data Sources: Read-Only External Data (2026)

Data source basics — declaration, refresh behavior, dependen...

Browse all Terraform articles (102)
© 2026 NicheeLab All rights reserved.