Utilizing Loops in Terraform

Rashad Ansari
5 min readJun 19, 2023

Summary

This article explores how loops in Terraform can simplify the management of resources and configurations. The article explains loop constructs like “for_each” and “count”, providing practical examples and best practices. By leveraging loops effectively, Terraform users can streamline infrastructure provisioning and scale deployments easily.

Loop Constructs in Terraform

Terraform provides two main loop constructs: “for_each” and “count”. These constructs allow developers to iterate over a collection of values or repeat a resource block a specific number of times.

for_each

This loop construct allows you to iterate over a map or a set of values. It’s especially useful when you have multiple instances of a resource that share similar properties but require unique configurations.

Example 1: Suppose you need to create multiple AWS S3 buckets with different configurations. Instead of writing separate resource blocks for each bucket, you can leverage the “for_each” loop to simplify the process.

variable "buckets" {
type = map
default = {
"bucket1" = { region = "us-west-1", acl = "private" },
"bucket2" = { region = "us-east-1", acl = "public-read" }
}
}

resource "aws_s3_bucket" "bucket" {
for_each = var.buckets

bucket = each.key
region = each.value.region
acl = each.value.acl
}

In this example, we define a variable called “buckets” as a map that contains two buckets, “bucket1” and “bucket2”. Each bucket has its own configuration, including the AWS region and access control list (ACL) settings.

The “aws_s3_bucket” resource block uses the “for_each” loop with the variable “buckets”. The loop creates separate S3 buckets for each key-value pair in the “buckets” map. Inside the loop, we access the key and values of each iteration using “each.key” and “each.value” respectively. This way, each bucket gets created with its unique name, region, and ACL settings.

Example 2: In scenarios where you have to manage multiple instances within an Auto Scaling Group (ASG), the “for_each” loop can come in handy.

variable "instances" {
type = map
default = {
"instance1" = { instance_type = "t2.micro", subnet = "subnet-12345" },
"instance2" = { instance_type = "t2.nano", subnet = "subnet-67890" }
}
}

resource "aws_launch_configuration" "launch_config" {
for_each = var.instances

name = each.key
instance_type = each.value.instance_type
image_id = "ami-12345678"
}

resource "aws_autoscaling_group" "asg" {
for_each = aws_launch_configuration.launch_config

launch_configuration = each.value.name
min_size = 1
max_size = 3
desired_capacity = 2
}

In this example, we define a variable called “instances” as a map that contains two instances, “instance1” and “instance2”. Each instance has its own configuration, including the instance type and subnet.

The “aws_launch_configuration” resource block uses the “for_each” loop with the variable “instances”. This loop creates separate launch configurations for each instance. Inside the loop, we access the key and values of each iteration to set the name and instance type.

The “aws_autoscaling_group” resource block uses the “for_each” loop in conjunction with the previously created launch configurations. This loop ensures that an Auto Scaling Group is created for each launch configuration. Each Auto Scaling Group can have different instance configurations, such as minimum and maximum sizes and desired capacity.

count

This loop construct allows you to repeat a resource block a specific number of times. It’s useful when you need to create a fixed number of identical resources, such as multiple load balancers or virtual machines.

Example 1: Suppose you need to create a set of AWS S3 buckets with a sequential naming scheme. The “count” loop can help you achieve this easily.

resource "aws_s3_bucket" "bucket" {
count = 5

bucket = "my-bucket-${count.index}"
acl = "private"
}

In this example, we use the “count” loop with a count value of 5. It means that Terraform will create five S3 buckets with a sequential naming scheme: “my-bucket-0”, “my-bucket-1”, “my-bucket-2”, and so on. Each bucket will have the same access control list (ACL) configuration set to “private”.

Example 2: Suppose you need to provision multiple AWS EC2 instances, each with a unique set of tags. The “count” loop can be used to accomplish this task.

variable "tags" {
type = list(string)
default = ["web", "database", "cache"]
}

resource "aws_instance" "ec2" {
count = length(var.tags)

instance_type = "t2.micro"
ami = "ami-12345678"
subnet_id = "subnet-12345"

tags = {
Name = "my-instance-${count.index}"
Type = var.tags[count.index]
}
}

In this example, we define a variable called “tags” as a list of strings. We assume that each string represents a different tag category, such as “web”, “database”, and “cache”.

The “aws_instance” resource block uses the “count” loop with a count value equal to the length of the “tags” list. This means that Terraform will create three EC2 instances, one for each tag in the “tags” list.

Inside the loop, we assign a unique name to each instance using the count index, and set the “Type” tag based on the corresponding value from the “tags” list.

This way, Terraform will provision three EC2 instances with different tags: “my-instance-0” with the tag “Type” set to “web”, “my-instance-1” with the tag “Type” set to “database”, and “my-instance-2” with the tag “Type” set to “cache”.

Choosing Between “for_each” and “count” in Terraform

When deciding between “for_each” and “count” in Terraform, consider the following factors:

for_each

  • Use when you need different configurations or unique names for each resource instance.
  • Ideal for working with maps or sets to create resources based on key-value pairs.

count

  • Use when you want to create multiple identical resources, such as replicas or instances in a cluster.
  • Suitable for scenarios where resources have the same configuration and properties.

Using “count” as a Control Variable in Terraform

In Terraform, the “count” loop construct not only allows for the repetition of resources but also serves as a powerful control variable for determining whether a resource should be created or omitted based on specific conditions. This capability provides flexibility and adaptability to your infrastructure configurations. Let’s explore how “count” can be used as a control variable in Terraform.

Consider the following example:

variable "create_instance" {
type = bool
default = true
}

resource "aws_instance" "example" {
count = var.create_instance ? 1 : 0

instance_type = "t2.micro"
subnet_id = "subnet-12345"
}

In this example, we introduce a boolean variable called “create_instance” with a default value of true. The value of this variable will determine whether the AWS instance resource should be created.

The “count” argument in the resource block is set based on the condition: if “var.create_instance” is true, the count is set to 1, resulting in the creation of a single instance. However, if “create_instance” is false, the count is set to 0, meaning that no instances will be created.

Conclusion

In conclusion, the effective use of loop constructs in Terraform, such as “for_each” and “count,” enhances the flexibility and efficiency of infrastructure provisioning. “for_each” allows for dynamic resource creation with individual configurations, while “count” simplifies the replication of identical resources. Additionally, “count” can be employed as a control variable to conditionally create or omit resources based on specific conditions, offering flexibility and resource efficiency. By understanding the distinctions and applications of these loop constructs, Terraform users can make informed decisions to suit their infrastructure requirements, optimizing deployments and ensuring efficient resource utilization.

--

--

Rashad Ansari

Curious and continuously learning software engineer, driven by crafting innovative solutions with passion. Let’s collaborate to shape a better future!