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 ofcount
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:
provider "aws" {
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 acount
value instead of manually duplicating resource blocks. For example, multiple AWS VPCs can be created usingcount
. - 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 thecount.index
variable to differentiate between the VPCs created with thecount
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 expressioncount = var.isTrue ? 1 : 0
whereisTrue
is a boolean-type variable. The resource will be created only if the value ofisTrue
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.
provider "aws" {
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:
resource "aws_instance" "env0-instance" {
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:
provider "aws" {
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:
provider "aws" {
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:
provider "aws" {
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.
Top comments (0)