Day 10— Terraform Conditional Expressions, Dynamic Blocks and Splat Expressions

Published: (December 3, 2025 at 11:56 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

Conditional Expressions

Conditional expressions evaluate a condition and return one of two values based on whether the condition is true or false.

Syntax

condition ? true_value : false_value

Use Cases

  • Choose instance types based on environment (dev vs prod)
  • Enable/disable monitoring based on configuration
  • Select different AMIs based on region
  • Set different resource counts for environments
  • Apply environment‑specific tags

Benefits

  • Single configuration for multiple environments
  • Reduces code duplication
  • Makes environment differences explicit
  • Simplifies configuration management
  • Easy to understand and maintain

When to Use

  • Environment‑specific configurations
  • Feature flags (enable/disable features)
  • Conditional resource creation
  • Region‑specific settings
  • Cost optimization (smaller resources in dev)

When Not to Use

  • Complex logic with many conditions (use locals instead)
  • When separate environment files are clearer
  • When all environments should be identical

Example

resource "aws_instance" "conditional_example" {
  ami           = "ami-28765345876"
  instance_type = var.environment == "prod" ? "t3.large" : "t2.micro"

  tags = {
    Name = "conditional-instance-${var.environment}"
  }
}

Running terraform plan shows the selected instance type based on the environment variable:

terraform plan
data.aws_ami.amazon_linux: Reading...
data.aws_ami.amazon_linux: Read complete after 1s [id=ami-02610f36df0c59544]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  + create

# aws_instance.conditional_example will be created
+ resource "aws_instance" "conditional_example" {
    + ami           = "ami-02610f36df0c59544"
    + instance_type = "t2.micro"
    # ... other attributes omitted for brevity
}

In this example, if var.environment is set to "prod" the instance type will be t3.large; otherwise it defaults to t2.micro.

Dynamic Block

Dynamic blocks generate multiple nested blocks within a resource based on a collection (list or map), eliminating the need to repeat similar block configurations.

Syntax

dynamic "block_name" {
  for_each = var.collection
  content {
    # Block configuration using each.key and each.value
  }
}
  • for_each iterates over a list or map.
  • content defines what each generated block should contain.
  • Access values with each.key and each.value.

Use Cases

  • Security group ingress/egress rules
  • Multiple EBS volumes on EC2 instances
  • IAM policy statements
  • Load balancer listeners
  • Route table routes
  • Any repeating nested block structure

Benefits

  • Eliminates repetitive code
  • Easy to add or remove items
  • Configuration driven by variables
  • Cleaner, more maintainable code
  • Supports complex data structures

When to Use

  • Multiple similar nested blocks
  • Variable number of configurations
  • Security group rules
  • Inline policies
  • Any repeating block pattern

When Not to Use

  • Single or few static blocks (overhead not worth it)
  • When it makes the code harder to read
  • For top‑level resources (use count or for_each instead)

Example

resource "aws_security_group" "dynamic_sg" {
  name        = "dynamic-sg-${var.environment}"
  description = "Security group with dynamic rules"

  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
      description = ingress.value.description
    }
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "dynamic-sg-${var.environment}"
  }
}
variable "ingress_rules" {
  description = "List of ingress rules for security group"
  type = list(object({
    from_port   = number
    to_port     = number
    protocol    = string
    cidr_blocks = list(string)
    description = string
  }))
  default = [
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
      description = "HTTP"
    },
    {
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
      description = "HTTPS"
    }
  ]
}

Running terraform plan shows the dynamically created ingress rules:

# aws_security_group.dynamic_sg will be created
+ resource "aws_security_group" "dynamic_sg" {
    + arn         = (known after apply)
    + description = "Security group with dynamic rules"
    + egress      = [
        {
          + cidr_blocks = ["0.0.0.0/0"]
          + from_port   = 0
          + protocol    = "-1"
          + to_port     = 0
        }
      ]
    + ingress = [
        {
          + from_port   = 80
          + to_port     = 80
          + protocol    = "tcp"
          + cidr_blocks = ["0.0.0.0/0"]
          + description = "HTTP"
        },
        {
          + from_port   = 443
          + to_port     = 443
          + protocol    = "tcp"
          + cidr_blocks = ["0.0.0.0/0"]
          + description = "HTTPS"
        }
      ]
    # ... other attributes omitted for brevity
}

The dynamic block iterates over var.ingress_rules, creating an ingress block for each rule without manual duplication.

Terraform Dynamic Block Illustration

Back to Blog

Related posts

Read more »