Skip to main content

Using Dynamic Blocks

1. Creating Dynamic Blocks

Dynamic blocks in Terraform allow you to generate repetitive configuration blocks dynamically, based on a list or map of values. This is particularly useful when you need to create multiple resources with similar configurations but different values.

Property Description Example
dynamic Used to define a dynamic block inside a resource or data source. dynamic "subnet" { ... }
for_each Iterates over a map or list to create multiple instances of the block. for_each = var.subnets
iterator Defines the variable name used to reference each item in the loop. iterator = subnet
content Defines the configuration inside the dynamic block. content { ... }

Example: Creating Dynamic Subnets

resource "aws_subnet" "main" {
  for_each = var.subnets

  vpc_id = aws_vpc.main.id
  cidr_block = each.value.cidr
  availability_zone = each.value.availability_zone

  tags = {
    Name = "${each.key}-subnet"
  }
}

Example: Using Dynamic Blocks with Maps

resource "aws_security_group_rule" "ingress" {
  dynamic "ingress" {
    for_each = var.security_rules
    content {
      from_port   = ingress.value.port
      to_port     = ingress.value.port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}

2. Iterating with for_each

Property Description Example
for_each Creates multiple instances of a resource or module based on a map or set for_each = { env1 = "prod", env2 = "staging" }

Example: Using for_each with a resource

resource "aws_instance" "web" {
  for_each = {
    dev  = "t2.micro"
    prod = "t3.large"
  }

  instance_type = each.value
  ami           = "ami-12345678"
}

Example: Using for_each with a module

module "web_server" {
  for_each = {
    us_west = { region = "us-west-2", count = 2 }
    us_east = { region = "us-east-1", count = 1 }
  }

  source   = "./web_server_module"
  region   = each.value.region
  instance_count = each.value.count
}
Note: for_each creates a separate instance for each key-value pair in the map or set. The each.key and each.value variables are available inside the resource or module block.
Warning: Changing the keys in a for_each map will cause Terraform to destroy and recreate resources. Ensure that keys are stable across runs.

3. Using Iterator Object

Property Description Values Notes
iterator Name of the iterator object string Used to reference the current item in a dynamic block
for_each Collection to iterate over map or set Must be a collection of strings, numbers, or objects

Example: Using iterator object in a dynamic block

resource "aws_instance" "example" {
  for_each = {
    web = "t2.micro"
    db  = "t3.small"
  }

  instance_type = each.value
  ami           = "ami-12345678"

  tags = {
    Name = "${each.key}-instance"
  }
}
Note: The each.key and each.value are accessible within the dynamic block, where each is the default iterator name. You can customize this with the iterator argument.

Example: Custom iterator name

resource "aws_security_group" "example" {
  for_each = {
    "allow_http"  = "80"
    "allow_https" = "443"
  }

  name        = each.key
  description = "Allow ${each.key}"

  dynamic "ingress" {
    for_each = each.value
    iterator = sg

    content {
      from_port   = sg.value
      to_port     = sg.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
}

4. Accessing Content Block

Property Description Usage
for_each Creates a block for each item in a map or list
resource "aws_instance" "web" {
  for_each = var.instances
  ami      = data.aws_ami.ubuntu.id
  instance_type = each.value.type
}
count Creates a block for each item in a list or map
resource "aws_instance" "web" {
  count = length(var.instances)
  ami      = data.aws_ami.ubuntu.id
  instance_type = var.instances[count.index].type
}
dynamic Allows for conditional creation of blocks
resource "aws_security_group" "sg" {
  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.port
      to_port     = ingress.value.port
      protocol    = "tcp"
      cidr_blocks = [ingress.value.cidr]
    }
  }
}

Example: Using for_each to create multiple resources

variable "instances" {
  type = map(object({
    type = string
    count = number
  }))
  default = {
    web = {
      type = "t2.micro"
      count = 2
    }
    db = {
      type = "t2.small"
      count = 1
    }
  }
}

Example: Using dynamic blocks with content

variable "ingress_rules" {
  type = list(object({
    port = number
    cidr = string
  }))
  default = [
    {
      port = 80
      cidr = "0.0.0.0/0"
    },
    {
      port = 443
      cidr = "0.0.0.0/0"
    }
  ]
}

5. Nesting Dynamic Blocks

Block Type Description Usage
dynamic Allows for dynamic generation of nested blocks within a resource or module dynamic "block_name" { for_each = list }

Example: Nesting Dynamic Blocks

resource "aws_security_group" "example" {
  name = "example-sg"

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

  dynamic "egress" {
    for_each = var.egress_rules
    content {
      from_port   = egress.value.port
      to_port     = egress.value.port
      protocol    = egress.value.protocol
      cidr_blocks = egress.value.cidr_blocks
    }
  }
}
Note: The for_each attribute in nested dynamic blocks must be a map or list of objects. The content block defines the structure of each generated block.

Nesting Dynamic Blocks - Key Points

  • Dynamic blocks can be nested inside other dynamic blocks.
  • Each nested block must define its own for_each and content.
  • Useful for generating complex configurations with variable structures.

6. Using Conditional Dynamic Blocks

Property Description Example
count Controls how many instances of a block to create. Can be used with dynamic blocks. count = var.instance_count
for_each Creates a block for each item in a map or set. Works well with dynamic blocks. for_each = var.tags
condition Used inside a dynamic block to conditionally include or exclude the block. condition = var.enable_logging

Example: Conditional Dynamic Block with condition

resource "aws_cloudwatch_log_group" "example" {
  count = var.enable_logging ? 1 : 0

  name = "/aws/lambda/${var.function_name}"
}

Example: Using for_each with Dynamic Blocks

resource "aws_security_group_rule" "ingress" {
  for_each = var.ingress_rules

  type        = "ingress"
  from_port   = each.value.port
  to_port     = each.value.port
  protocol    = each.value.protocol
  cidr_blocks = each.value.cidr_blocks
}

7. Combining Dynamic and Static Blocks

Dynamic blocks can be combined with static blocks within the same resource or module to provide more flexible and maintainable configurations.

Block Type Description Usage Example
dynamic Allows for the creation of repeatable blocks with dynamic content based on a list or map
dynamic "port" {
  for_each = var.ports
  content {
    internal = port.value.internal
    external = port.value.external
  }
}
static Fixed, predefined block that does not change during configuration evaluation
resource "aws_security_group" "example" {
  name = "example"
  description = "Example security group"
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Example: Combining Dynamic and Static Blocks

resource "aws_security_group" "example" {
  name        = "example"
  description = "Example security group with dynamic and static blocks"

  # Static block
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # Dynamic block
  dynamic "ingress" {
    for_each = var.ports
    content {
      from_port   = ingress.value.internal
      to_port     = ingress.value.external
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
}
Note: When combining dynamic and static blocks, the static block is evaluated first, followed by the dynamic blocks. This ensures that all static configurations are applied before any dynamic ones.

8. Using Dynamic Blocks for Complex Objects

Dynamic blocks in Terraform allow you to generate nested blocks dynamically, which is particularly useful for complex objects like aws_security_group_rule or google_compute_firewall_rule. This feature is especially powerful when you need to create multiple similar blocks with varying configurations.

Property Description Example Value
dynamic Used to define a dynamic block inside a resource or data source dynamic "rule" { ... }
for_each Iterates over a map or list to generate multiple blocks for_each = var.rules
iterator Name of the iterator variable used inside the dynamic block iterator = rule
content Defines the content of each generated block content { ... }

Example: Using Dynamic Blocks for Security Group Rules

resource "aws_security_group" "example" {
  name        = "example-sg"
  description = "Example security group"

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

  dynamic "egress" {
    for_each = var.egress_rules
    content {
      from_port   = egress.value.port
      to_port     = egress.value.port
      protocol    = egress.value.protocol
      cidr_blocks = egress.value.cidr_blocks
    }
  }
}

Example: Complex Nested Object with Multiple Attributes

resource "google_compute_firewall" "example" {
  name        = "example-firewall"
  network     = google_compute_network.example.self_link

  dynamic "rule" {
    for_each = var.firewall_rules
    content {
      direction = rule.value.direction
      priority  = rule.value.priority

      dynamic "source_ranges" {
        for_each = rule.value.source_ranges
        content {
          value = source_ranges.value
        }
      }

      dynamic "target_tags" {
        for_each = rule.value.target_tags
        content {
          value = target_tags.value
        }
      }

      dynamic "allowed" {
        for_each = rule.value.allowed
        content {
          protocol = allowed.value.protocol
          ports    = allowed.value.ports
        }
      }
    }
  }
}
Note: When using for_each with dynamic blocks, ensure that the input map or list contains valid keys and values. Invalid keys or missing required attributes will cause Terraform to fail during validation.
Warning: Be cautious when nesting dynamic blocks deeply, as it can lead to complex configurations that are hard to debug and maintain. Always validate your configuration with terraform plan.

9. Setting Custom Iterator Names

When using dynamic blocks in Terraform, you can customize the iterator name to control how the loop variable is referenced within the block. By default, Terraform uses each as the iterator name, but you can override this using the iterator argument.

Argument Description Default Value
iterator Specifies the name of the iterator variable used in the dynamic block. each

Example: Custom Iterator Name

resource "aws_instance" "web" {
  count = length(var.instances)

  dynamic "tags" {
    for_each = var.instances
    iterator = "instance"
    content {
      Name = instance.value.name
      Environment = instance.value.environment
    }
  }
}
Note: The iterator argument allows you to use a custom variable name inside the dynamic block, which can improve readability when working with complex data structures.

10. Using Dynamic Labels

Property Description Example
dynamic Used to create dynamic blocks in Terraform configurations
dynamic "labels" {
  for_each = var.labels
  content {
    key   = labels.key
    value = labels.value
  }
}

Example: Defining Dynamic Labels

resource "aws_instance" "example" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"

  dynamic "tags" {
    for_each = var.tags
    content {
      key   = tags.key
      value = tags.value
    }
  }
}
Note: The dynamic block allows you to create multiple blocks of the same type with different values, useful for labels or tags that vary by environment or deployment.

Was this article helpful?