DEV Community

Cover image for Mastering Loops and Conditionals in Terraform
Patrick Odhiambo
Patrick Odhiambo

Posted on

Mastering Loops and Conditionals in Terraform

Terraform, a powerful Infrastructure as Code (IaC) tool, enables engineers to provision and manage cloud resources in an automated and declarative way. Among its many features, loops and conditionals stand out as indispensable tools for handling dynamic and repetitive resource configurations. Whether you're building infrastructure in AWS, GCP, or Azure, mastering these features helps optimize your code, making it more flexible, reusable, and maintainable.

In this post, we will dive deep into loops and conditionals in Terraform, covering their syntax, use cases, and advanced tips. By the end of this guide, you'll have the knowledge to wield these constructs effectively, streamlining your Terraform code and reducing redundancy.

Why Use Loops and Conditionals in Terraform?

When writing Terraform configurations, you often encounter scenarios where you need to create multiple resources with similar configurations or make decisions based on certain criteria (e.g., environment, region, or resource type). Without loops and conditionals, this could lead to a lot of repeated or hard-coded configurations.

For example:

  • Loops allow you to create multiple instances, network subnets, or storage resources in a DRY (Don't Repeat Yourself) manner.
  • Conditionals enable decision-making within the Terraform code based on dynamic inputs or configurations, making your code more adaptable to changing environments.

Loops in Terraform

Terraform offers several ways to loop over data structures like lists and maps, simplifying the creation of multiple resources without duplicating code.

1. count Parameter

The simplest form of looping in Terraform is using the count meta-argument. This allows you to specify the number of instances of a resource to create. For example:

resource "aws_instance" "web" {
  count         = 3
  ami           = "ami-123456"
  instance_type = "t2.micro"
  tags = {
    Name = "WebServer-${count.index}"
  }
}
Enter fullscreen mode Exit fullscreen mode

In this case, Terraform will create three AWS EC2 instances, each with a unique name tag like WebServer-0, WebServer-1, and WebServer-2. The count.index keeps track of the iteration, starting at 0.

Key Points:

  • count.index is available within the resource to differentiate between each instance.
  • Useful when you need to create multiple identical resources.

2. for_each Loop

The for_each meta-argument offers more control compared to count, allowing you to loop over complex data structures like maps or sets. Here's how it works:

resource "aws_security_group" "allow_tls" {
  for_each = {
    "frontend" = "sg-123456"
    "backend"  = "sg-789012"
  }

  name        = "${each.key}_sg"
  description = "Allow TLS inbound traffic"

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [each.value]
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the for_each loop iterates over a map with two keys (frontend and backend), creating two security groups, each with a unique CIDR block.

Key Points:

  • each.key and each.value give you access to the key-value pairs in the loop.
  • for_each is ideal when you need more flexibility or want to loop over maps and sets.

3. Dynamic Blocks with for_each

Another powerful way to use loops in Terraform is within dynamic blocks. Dynamic blocks enable you to conditionally create sub-resources like ingress rules, tags, etc.

resource "aws_security_group" "example" {
  name = "example_sg"

  dynamic "ingress" {
    for_each = var.ingress_rules

    content {
      from_port   = ingress.value.from
      to_port     = ingress.value.to
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Here, the dynamic block creates multiple ingress blocks based on the contents of the var.ingress_rules variable.

Conditionals in Terraform

Terraform conditionals allow you to add logic to your configurations, which can make your code more dynamic and responsive to different environments or input variables.

1. The condition ? true_value : false_value Syntax

Terraform uses a simple ternary conditional expression similar to many programming languages:

variable "environment" {
  type    = string
  default = "production"
}

resource "aws_instance" "web" {
  instance_type = var.environment == "production" ? "t2.large" : "t2.micro"
  ami           = "ami-123456"
}
Enter fullscreen mode Exit fullscreen mode

In this example, the instance_type is set to t2.large if the environment is production, otherwise it defaults to t2.micro.

Key Points:

  • This is a ternary operator, where the first condition is evaluated, and the result is either the second or third value depending on whether the condition is true or false.
  • Ideal for simple decisions.

2. Conditional Resource Creation

Terraform conditionals can also be used to create or skip resources altogether. However, since Terraform is declarative, it doesn't "skip" a resource; instead, it relies on conditions to determine whether or not to execute a resource block.

resource "aws_instance" "web" {
  count = var.create_instance ? 1 : 0
  ami   = "ami-123456"
  instance_type = "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

In this case, if var.create_instance is true, the instance is created. If false, Terraform sets the count to 0, meaning no resources will be created.

Key Points:

  • Use the count meta-argument with a conditional to create resources conditionally.
  • This is useful for dev/test environments where you may not need certain resources all the time.

Advanced Terraform Patterns: Combining Loops and Conditionals

In complex environments, you might need to combine loops and conditionals for more advanced use cases. Let's explore a few scenarios:

1. Creating Resources Conditionally for Multiple Environments

In multi-environment scenarios (e.g., dev, staging, production), you might want to selectively create resources using loops and conditionals together:

variable "environment" {
  type = string
}

variable "instance_count" {
  default = {
    dev     = 1
    staging = 2
    prod    = 3
  }
}

resource "aws_instance" "web" {
  count         = var.instance_count[var.environment]
  ami           = "ami-123456"
  instance_type = var.environment == "prod" ? "t2.large" : "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

This example creates different numbers of instances based on the environment (dev, staging, or prod), while also conditionally adjusting the instance_type.

2. Combining for_each and Conditional Logic

Consider a scenario where you need to create subnets in multiple availability zones but only in certain environments:

variable "availability_zones" {
  default = ["us-west-1a", "us-west-1b"]
}

variable "create_subnets" {
  type = bool
}

resource "aws_subnet" "example" {
  for_each = var.create_subnets ? toset(var.availability_zones) : []

  cidr_block = cidrsubnet(var.vpc_cidr_block, 8, each.key)
  availability_zone = each.value
}
Enter fullscreen mode Exit fullscreen mode

In this case, subnets are only created in specified availability zones if var.create_subnets is set to true.

Best Practices

best

  1. Avoid Over-Complicating Code: While loops and conditionals are powerful, overuse can lead to hard-to-read configurations. Always aim for clarity.
  2. Use for_each over count: When possible, prefer for_each as it offers more flexibility and works well with complex data structures like maps and sets.
  3. Test with Multiple Environments: Make sure to test loops and conditionals across different environments (e.g., dev, staging, prod) to ensure your infrastructure is consistent and reliable.

Parting Shot

Loops and conditionals are essential for writing efficient and dynamic Terraform configurations. By mastering these features, you can reduce code duplication, automate resource creation, and manage complex infrastructure setups with ease. From simple count loops to advanced for_each with conditionals, these tools enable you to handle everything from small-scale projects to enterprise-level infrastructure with confidence.

Happy Terraforming !!

Top comments (0)