Working on Infrastructure as Code means writing different configs for different sets of resources. As the projects grow, you might have redundant code used to deploy similar resources. One way to streamline this is using the count meta-argument.
In this blog, we’ll learn more about count, describe its use cases, and show how you can use it to create multiple resources with ease – with or without conditional expressions.
We’ll also answer frequent questions, provide some examples and even take a quick look at for_each argument, and see how it compares to count.
Disclaimer
All examples and use cases of count meta-argument discussed here, in context of Terraform, work similarly in OpenTofu, the open-source Terraform alternative. However, to keep it simple and familiar for DevOps engineers, we’ll refer to these as “Terraform” count throughout this blog post.
What is Terraform Count?
In Terraform, or OpenTofu, the count meta-argument allows you to create multiple resource instances from a single configuration block.
Setting the Terraform count value tells Terraform how many copies of the resource to develop, simplifying your infrastructure’s management and scaling. Each instance in the Terraform configuration block can be uniquely identified using the count.index.
What is the Terraform Count Index?
The count.index is a special variable that Terraform creates inside a resource block using count argument. This helps you uniquely identify each instance of the resource. Indices start from 0 and grow by increments of 1 for each resource instance created.
The count.index differentiates between the same resources created using the count meta-argument. Based on each resource’s index, it can also be used to apply unique naming conventions, configurations, or dependencies.
Let’s look at an example of creating multiple AWS S3 buckets using Terraform count.
We’ll create three instances of an aws_s3_bucket named env0-bucket-0, env0-bucket-1, env0-bucket-2 using the count value ‘3’.
Each bucket will differ by the index variable count.index, which Terraform automatically assigns in main.tf file:
region = “us-west-2”
}
resource “aws_s3_bucket” “bucket” {
count = 3
bucket = “env0-my-bucket-${count.index}”
tags = {
Name = “env0-bucket-${count.index}”
}
}
Now, run the terraform init command to download the required providers’ plugins and the terraform apply command to deploy the infrastructure in AWS Cloud. This will create three state buckets using the count indices 0, 1, and 2:
After the successful creation of the resources, you can see the buckets in S3 via the AWS Console. Had you not used Terraform count, you would have wasted a lot of time creating three different S3 bucket resource blocks.
Use Cases of Terraform Count
You can use Terraform count in your configuration in various scenarios. Let’s look at some of the major ones:
Dynamic Resource Creation – You can use count to generate resources dynamically based on a count value instead of manually duplicating resource blocks. For example, multiple AWS VPCs can be created using count.
Index Variable – The count meta-argument creates an implicit index variable, which can define and differentiate between the duplicate resources created with the meta-argument. For example, using the count.index variable to differentiate between the VPCs created with the count argument.
Conditional Creation – You can use a conditional expression like count > 1 to decide when to develop resources based on a specified condition. This allows you to control resource creation dynamically. For example, using the conditional expression count = var.isTrue ? 1 : 0 where isTrue is a boolean-type variable. The resource will be created only if the value of isTrue is true.
Let’s explore these below.
Using Terraform Count and Count.Index
To demonstrate how things work, beyond the basic dynamic resource provisioning, here are some examples of how you can use count.index in your Terraform code.
Creating Multiple Instances using a Map
Let’s create an instance_types variable, a map of objects. Each object contains the attributes of an instance, which will be fetched by the respective instances using count.index in the variable.tf file.
region = “ap-south-1”
}
variable “instance_types” {
description = “Map of instance types with properties”
type = map(object({
instance_type = string
subnet_id = string
availability_zone = string
}))
default = {
“instance1” = {
instance_type = “t2.medium”
subnet_id = “subnet-0b9bf47f2dd51a793”
availability_zone = “ap-south-1a”
},
“instance2” = {
instance_type = “t2.micro”
subnet_id = “subnet-095f35df6f4711c19”
availability_zone = “ap-south-1b”
},
“instance3” = {
instance_type = “t2.small”
subnet_id = “subnet-095f35df6f4711c19”
availability_zone = “ap-south-1b”
}
}
}
Now, create the resource using count.index to access the values of the keys in the map of objects in the main.tf file:
count = length(var.instance_types)
ami = “ami-0f5ee92e2d63afc18”
instance_type = var.instance_types[keys(var.instance_types)[count.index]].instance_type
subnet_id = var.instance_types[keys(var.instance_types)[count.index]].subnet_id
security_groups = [“sg-04d9e1d30402432ce”]
availability_zone = var.instance_types[keys(var.instance_types)[count.index]].availability_zone
tags = {
Name = “env0-${keys(var.instance_types)[count.index]}”
}
}
Here, count.index will take indices from 0 to 2, i.e., three, the length of the instance_types variable. Run terraform init followed by terraform apply to deploy your infrastructure:
To verify the creation of the instances, you can check the AWS console:
Creating Multiple S3 Buckets using a List
We will create a bucket_names variable, a list of strings with defined default values. Set the count argument of the aws_s3_bucket resource as the length of the list and pass the variable values as the resource name attribute according to their index.
First, let’s define the bucket_names variable in the var.tf file:
region = “us-west-2”
}
variable “bucket_names” {
description = “List of bucket names”
type = list(string)
default = [“env0-bucket1”, “env0-bucket2”, “env0-bucket3”]
}
In main.tf, define the [.code]aws_s3_bucket [.code] resource with the [.code]count[.code] value equal to the length of the [.code]bucket_names[.code] variable:
resource “aws_s3_bucket” “bucket” {
count = length(var.bucket_names)
bucket = var.bucket_names[count.index]
tags = {
Name = var.bucket_names[count.index]
}
}
count.index will take the length of the variable bucket_names, which is three indices from 0 to 2. Run the terraform init command to download the provider plugins, and the terraform apply command to create the resources:
After successful execution, verify the resources on the AWS console:
Conditional Expressions with Terraform Count 0
In Terraform, you can use conditional expressions with the count meta-argument to create resources based on specific conditions. When the count is set to 0, it indicates that Terraform will create no aws_instance. The configuration below creates an EC2 instance only if the create_instance value is set to true:
region = “ap-south-1”
}
variable “create_instance” {
description = “Flag to create instance”
default = true
}
resource “aws_instance” “instance” {
count = var.create_instance ? 1 : 0
ami = “ami-0f5ee92e2d63afc18”
instance_type = “t2.micro”
subnet_id = “subnet-095f35df6f4711c19”
availability_zone = “ap-south-1b”
tags = {
Name = “conditional-instance”
}
}
Run the terraform init command and the terraform apply command which displays that one aws_instance is created according to the value of the Terraform count argument.
Now, if you change the value of create_instance variable to false and run the terraform apply command. Since the count value is set to 0, the instance will be destroyed from your infrastructure as an update to your state.
By setting the value of count to 0, we skip the deployment of resources without removing a block of code and dynamically take control of the deployment.
Terraform Count vs. For_each
By now, we know that Terraform count allows you to deploy multiple instances, similar to what Terraform for_each is used for. Let’s look at how they differ and which would be best for your use case.
When to use Count instead of For_each?
You should use Terraform count instead of for_each in the following instances:
Uniform Resources – When the resources you create are essentially the same, and any differences can be managed with count.index.
Conditional Creation – When you need to create resources based on a simple boolean condition.
When managing infrastructure with Terraform, it’s crucial to implement robust CI/CD pipelines to handle code changes and deployment efficiently. In real-world scenarios, running Terraform commands locally is not recommended, and we should use a governed pipeline with CI/CD to ensure consistent, secure, and automated deployments. This is where env0 comes in, enhancing your Terraform workflows by integrating with your source code repository and automating the entire process.
Integrating Terraform Count with env0
When Terraform count is used with env0’s environment variables feature, it removes redundancy in your code. You can control the number of similar resources that you want to deploy in your cloud provider based on the count value, improving the efficiency of your Infrastructure as Code (IaC) workflows.
Environment Variables with Terraform Count
First, let’s write the Terraform code to automate the deployment of the aws_s3_bucket using count in your main.tf file. We will use a conditional expression in our Terraform configuration, which will decide the value for the count meta-argument and push it to the Github repository with the following code:
region = “us-west-2”
}
variable “environment” {
description = “The environment in which to deploy”
type = string
default = “development”
}
variable “prod_count” {
type = number
default = 4
}
variable “dev_count” {
type = number
default = 2
}
locals {
bucket_count = var.environment == “production” ? var.prod_count : var.dev_count
}
resource “aws_s3_bucket” “env0” {
count = local.bucket_count
bucket = “env0-bucket-${count.index}”
tags = {
Name = “env0-bucket-${count.index}”
}
}
Next, log in to env0 and click on ‘Create New Project’:
A pop-up will appear, prompting you to give the project a unique name. Now, click on ‘Create Project.’
In the Project Environment, click ‘Create New Environment.’
Select ‘VCS’ integration to create a new environment:
Select your Github repository, branch, and Terraform folder and press ‘NEXT’:
Select ‘Terraform’ as your IaC Type:
Click on ‘Load Variables From Code’ in the top right corner to get all the variables declared in your manifest files with their default values:
Change the ‘environment’ value to ‘production’ in the ‘Environment Variables’:
Make sure you have AWS Access Credentials added in the ‘Environment Variables’:
Save the variables, and on the next screen, ‘Environment Details’, give an environment and workspace name. Click on ‘Done’:
After setting up the environment details, the CI/CD should run by itself, and all the deployment logs should be successful.
Check the AWS console to verify the bucket creation:
By following these steps, we have efficiently deployed four S3 buckets for prod using the count meta-argument to AWS using the environment variables in env0.
The process is seamless, and you can smoothly integrate our Git repository into our environment, provide the values for the Terraform environment variables using a simple user interface, and automate the CI/CD pipeline.
This approach not only streamlines your workflow but also ensures consistent, reliable deployments, saving time and effort.
Conclusion
The terraform count argument efficiently manages multiple resources with a single configuration block. It simplifies resource management, enhances scalability, and reduces redundancy in your code.
You can achieve dynamic and well-organized infrastructure deployments by understanding and utilizing count with tools like env0. Whether you are managing a few instances or hundreds, the count argument provides the flexibility and control needed to optimize your infrastructure setup.
Frequently Asked Questions
Q: What is Terraform Count?
The count meta-argument in Terraform allows you to create multiple instances of a resource using a single configuration block. By setting the count value to a number, you tell Terraform how many copies of the resource to create, making it easier to manage and scale your infrastructure.
Q: Can you use Count in the Data Block in Terraform?
No, you cannot use count meta-argument in a data block in Terraform. The count meta-argument is only supported in resource blocks. If you need to fetch multiple data items, you typically use a workaround like a for_each loop or handle it outside of Terraform.
Q: What is the difference between Count and For_each in Terraform?
In Terraform, both count and for_each are meta-arguments used to manage multiple instances of resources or modules, but they have different use cases and offer different capabilities. Here’s a detailed difference:
count – Use count when you want to create multiple identical resources. It takes an integer and creates that number of instances. All instances will be similar, except for differences which you manage using count.index.
for_each – Use for_each when creating multiple instances of a resource that may differ in configuration. It takes a set or map and creates an instance for each item. You can customize each instance based on the key-value pairs.
Q: What is the use of the Count Index in Terraform?
In Terraform, count.index is an automatic variable available within a resource block that uses the count meta-argument.
This represents the index number of the current instance, starting from 0 and helps you differentiate between multiple instances of the same resource, allowing you to customize each instance.