- The OpenTofu Language
- Meta-Arguments
- The count Meta-Argument
The count Meta-Argument
A given resource or module block cannot use count together with enabled or for_each.
By default, a resource block configures one real
infrastructure object. (Similarly, a
module block includes a
child module's contents into the configuration one time.)
However, sometimes you want to manage several similar objects (like a fixed
pool of compute instances) without writing a separate block for each one.
OpenTofu has two ways to do this:
count and for_each.
If a resource or module block includes a count argument whose value is a whole number,
OpenTofu will create that many instances.
Basic Syntaxβ
count is a meta-argument defined by the OpenTofu language. It can be used
with modules and with every resource type.
The count meta-argument accepts a whole number, and creates that many
instances of the resource or module. Each instance has a distinct infrastructure object
associated with it, and each is separately created,
updated, or destroyed when the configuration is applied.
resource "aws_instance" "server" {
count = 4 # create four similar EC2 instances
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
tags = {
Name = "Server ${count.index}"
}
}
If you're using count = var.enabled ? 1 : 0 to conditionally enable/disable a
single resource or module, consider using the the enabled argument in a lifecycle block
argument instead. It provides cleaner syntax without requiring array indexing and supports
automatic migration from the count pattern.
The count Objectβ
In blocks where count is set, an additional count object is
available in expressions, so you can modify the configuration of each instance.
This object has one attribute:
count.indexβΒ The distinct index number (starting with0) corresponding to this instance.
Using Expressions in countβ
The count meta-argument accepts numeric expressions.
However, unlike most arguments, the count value must be known
before OpenTofu performs any remote resource actions. This means count
can't refer to any resource attributes that aren't known until after a
configuration is applied (such as a unique ID generated by the remote API when
an object is created).
Referring to Instancesβ
When count is set, OpenTofu distinguishes between the block itself
and the multiple resource or module instances associated with it. Instances are
identified by an index number, starting with 0.
<TYPE>.<NAME>ormodule.<NAME>(for example,aws_instance.server) refers to the resource block.<TYPE>.<NAME>[<INDEX>]ormodule.<NAME>[<INDEX>](for example,aws_instance.server[0],aws_instance.server[1], etc.) refers to individual instances.
This is different from resources and modules without count or for_each, which can be
referenced without an index or key.
Similarly, resources from child modules with multiple instances are prefixed
with module.<NAME>[<KEY>] when displayed in plan output and elsewhere in the UI.
For a module without count or for_each, the address will not contain
the module index as the module's name suffices to reference the module.
Within nested provisioner or connection blocks, the special
self object refers to the current resource instance, not the resource block
as a whole.
When to Use for_each Instead of countβ
If your instances are almost identical, count is appropriate. If some
of their arguments need distinct values that can't be directly derived from an
integer, it's safer to use for_each.
Before for_each was available, it was common to derive count from the
length of a list and use count.index to look up the original list value:
variable "subnet_ids" {
type = list(string)
}
resource "aws_instance" "server" {
# Create one instance for each subnet
count = length(var.subnet_ids)
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
subnet_id = var.subnet_ids[count.index]
tags = {
Name = "Server ${count.index}"
}
}
This was fragile, because the resource instances were still identified by their
index instead of the string values in the list. If an element was removed from
the middle of the list, every instance after that element would see its
subnet_id value change, resulting in more remote object changes than intended.
Using for_each gives the same flexibility without the extra churn.