Terraform for_each Loops w/ If Statements

Posted by Miguel Lopez on Wed 01 December 2021 in terraform

Technical Stack: Terraform 1.6.0+, AWS

Read: 5 minutes

Introduction

The for_each meta-argument in Terraform allows you to dynamically create resources or modules based on a map or set of values. Starting with Terraform v0.12.6, this feature became widely available, and by Terraform v1.6.0, additional enhancements made it even more powerful for conditional logic.

This guide demonstrates how to use for_each with conditional logic to control resource creation dynamically. We'll explore practical examples and best practices for 2025.


Basic for_each Example

A simple for_each loop looks like this:

resource "aws_s3_bucket" "example" {
  for_each = {
    group_a = "bucket-1"
    group_b = "bucket-2"
  }
  bucket = each.value
  tags = {
    Name  = each.value
    Group = each.key
  }
}

Here, Terraform creates two S3 buckets, one for each key-value pair in the map. The each.key and each.value expressions allow you to reference the current iteration's key and value.


Building Conditional If Statements

Conditional logic inside for_each enables you to filter which resources are created. Let's explore an updated example for 2025.

Example: VPC Link for Network Load Balancers

Suppose you want to create a VPC link only for network load balancers. Here's how you can achieve this:

resource "aws_api_gateway_vpc_link" "vpc_link" {
  for_each = { for key, value in var.load_balancers : key => value if value.type == "network" }
  name     = each.key
  target_arns = [aws_lb_target_group.example[each.key].arn]
}

Input Example

variable "load_balancers" {
  default = {
    app_lb = {
      type = "application"
    }
    net_lb = {
      type = "network"
    }
  }
}

Explanation

  1. The for_each expression iterates over the load_balancers map.
  2. The if condition filters only those entries where type equals "network".
  3. Terraform creates a VPC link for each matching entry.

Advanced Example: Autoscaling Group with Optional Load Balancers

Imagine you're building a microservice architecture where some services require a load balancer, and others do not. Here's how you can conditionally create load balancers:

Module Example

module "load_balancer" {
  for_each = { for key, value in var.services : key => value if lookup(value, "enable_lb", false) }
  source   = "./modules/load-balancer"

  name        = each.key
  internal    = lookup(each.value.lb_config, "internal", true)
  lb_type     = lookup(each.value.lb_config, "type", "application")
  tags        = merge(local.common_tags, lookup(each.value, "tags", {}))
}

Input Example

variable "services" {
  default = {
    api = {
      enable_lb = true
      lb_config = {
        internal = true
        type     = "application"
      }
    }
    worker = {
      enable_lb = false
    }
  }
}

Explanation

  1. The for_each expression filters services where enable_lb is true.
  2. The lookup function safely retrieves values from nested maps, providing defaults if keys are missing.
  3. Terraform creates a load balancer module only for services with enable_lb = true.

Best Practices for 2025

  1. Use lookup for Safe Access: Always use lookup to avoid errors when accessing optional keys in maps.
  2. Combine for_each with dynamic Blocks: Use dynamic blocks for conditional nested configurations.
  3. Leverage Local Variables: Simplify complex expressions by breaking them into reusable local variables.

Conclusion

The for_each meta-argument, combined with conditional logic, is a powerful tool for creating dynamic and reusable Terraform configurations. By leveraging the latest features in Terraform 1.6.0, you can build more flexible and maintainable infrastructure as code.

Start using these patterns in your projects today to streamline resource management and improve scalability!