Overview
By default, one resource block can be used to configure one real infrastructure object. Similarly, one module block includes the content of the child module blocks in the configurations. If you want to manage multiple similar objects, such as a fixed pool of compute instances without writing a block for each object, you can use the Terraform count or for_each argument.
count is a meta-argument in Terraform. It allows you to declare multiple instances in a resource or module. If the resource or module contains the count argument that carries an integer value, Terraform creates that number of instances. count is used to create resource replicas based on a specified value. Each replica is an independent infrastructure object. Using count is the simplest way to create multiple resources at the same time. count is particularly ideal for resource sets that use a similar structure, such as server clusters, similar buckets, and network components.
This topic describes the syntax of the count meta-argument and how to use it. This topic also provides examples on how to use count in Alibaba Cloud environments to simplify infrastructure management.
Note that the same resource or module block cannot use count and for_each at the same time.
Syntax
count is a meta-argument defined by Terraform. count can be used together with modules and all resource types.
The value of count must be an integer. The number of resources or module instances to be created is determined by the value of argument. Each instance is associated with a unique infrastructure object. When you configure applications, each object is independently created, updated, and destroyed.
Usage
resource "alicloud_instance" "server" {
count = 4 # Create four similar ECS instances.
instance_name = "server-${count.index}"
instance_type = "ecs.s6-c1m2.small"
image_id = "aliyun_2_1903_x64_20G_alibase_20230523.vhd"
vswitch_id = "vsw-abc123456"
security_groups = ["sg-abc123456"]
tags = {
Name = "Server ${count.index}"
}
}count object
In a block that contains the count argument, the expression can use an additional count object. This allows you to modify the configurations of each instance. This object has the following attribute:
count.index: The index number of the instance. Index numbers start from 0.
Use expressions in the count argument
The count meta-argument supports numeric expressions. However, the value of count must be known before Terraform performs any remote resource operations. Therefore, count cannot reference unknown resource attributes before it is applied. For example, the unique ID generated by a remote API operation during object creation cannot be referenced by count.
Reference instances
After you configure the count argument, Terraform distinguishes between the block and the resources or module instances that are associated with the block. Instances are identified by indexes, which start from 0.
Use
<TYPE>.<NAME>ormodule.<NAME>to reference a resource block. Example:alicloud_instance.server.Use
<TYPE>.<NAME>[<INDEX>]ormodule.<NAME>[<INDEX>]to reference a single instance. Examples:alicloud_instance.server[0]andalicloud_instance.server[1].
If a resource or module does not contain the count or for_each argument, the resource or module can be referenced without an index or key.
When the resources in the child modules from multiple instances plan to output and display other UI elements, the resources are prefixed with module.<NAME>[<KEY>]. If a module does not contain the count or for_each argument, the address does not contain the module index because the module can be referenced by name.
Note that in a nested provisioner or connection block, the current resource instance is referenced by a special self object, instead of the entire resource block.
Examples
Create multiple ECS instances
resource "alicloud_instance" "web_servers" {
count = 3
instance_name = "web-server-${count.index + 1}"
instance_type = "ecs.s6-c1m2.small"
image_id = "aliyun_2_1903_x64_20G_alibase_20230523.vhd"
vswitch_id = "vsw-abc123456"
security_groups = ["sg-abc123456"]
system_disk_category = "cloud_efficiency"
system_disk_size = 40
tags = {
Name = "WebServer-${count.index + 1}"
Role = "web"
Stage = "production"
}
}Create multiple OSS buckets
variable "bucket_names" {
description = "List of bucket name prefixes"
type = list(string)
default = ["logs", "backups", "configs"]
}
resource "alicloud_oss_bucket" "buckets" {
count = length(var.bucket_names)
bucket = "${var.bucket_names[count.index]}-${random_id.suffix.hex}"
acl = "private"
tags = {
Name = var.bucket_names[count.index]
Environment = "Production"
}
}
resource "random_id" "suffix" {
byte_length = 4
}Create resources based on conditions
You can use count to create resources based on conditions:
variable "create_slb" {
description = "Whether to create SLB"
type = bool
default = true
}
resource "alicloud_slb_load_balancer" "app" {
# Create the resource only when create_slb is set to true.
count = var.create_slb ? 1 : 0
load_balancer_name = "app-lb"
address_type = "internet"
load_balancer_spec = "slb.s2.small"
tags = {
Name = "ApplicationLB"
}
}Use count in a module
variable "enable_high_availability" {
description = "Whether to deploy in high availability mode with multiple zones"
type = bool
default = false
}
module "primary_zone" {
source = "./modules/zone_deployment"
zone_id = "cn-hangzhou-h"
# Other configurations.
}
module "secondary_zone" {
source = "./modules/zone_deployment"
# The resource is deployed in the second zone only when high availability is enabled.
count = var.enable_high_availability ? 1 : 0
zone_id = "cn-hangzhou-i"
# Other configurations.
}How to choose between count and for_each
count is more applicable if the instances are nearly identical. If some of their arguments require different values that cannot be derived directly from integers, for_each provides higher security.
Before for_each is available, the common method is to derive count from the length of the list and use count.index to query the original list value. Sample code:
variable "vswitch_ids" {
type = list(string)
}
resource "alicloud_instance" "server" {
# Create an instance for each vSwitch.
count = length(var.vswitch_ids)
instance_name = "server-${count.index + 1}"
instance_type = "ecs.s6-c1m2.small"
image_id = "aliyun_2_1903_x64_20G_alibase_20230523.vhd"
vswitch_id = var.vswitch_ids[count.index]
tags = {
Name = "Server ${count.index + 1}"
}
}This method is inefficient because the resource instances are identified by their indexes instead of the string values in the list. If you delete an element from the list, each instance after the deleted element can detect the change in the vswitch_id value. The number of remote objects that need to be changed is higher than expected. for_each supports flexible configurations without making unexpected changes.
This topic is developed based on the Terraform document The count Meta-Argument. You can read the Terraform document for more details.