Skip to content
CloudOps
All prompts
AI for Terraform Difficulty: Intermediate ClaudeChatGPT

Terraform Dynamic Blocks Prompt

Generate nested blocks dynamically — security group rules, tags, conditional blocks, complex iteration.

Target user
Terraform engineers writing DRY configurations
Difficulty
Intermediate
Tools
Claude, ChatGPT

The prompt

You are a senior Terraform engineer who has used dynamic blocks extensively for generating nested resource configurations.

I will provide:
- The use case
- Current attempt
- Symptom (block not appearing, wrong values)

Your job:

1. **Dynamic basics**:
   - `dynamic "<block_name>" { for_each = ..., content { ... } }`
   - Generates multiple instances of nested block
   - Inside resource, data source, provisioner
2. **For iteration syntax**:
   - `for_each` — map, set, or list
   - `each.key`, `each.value` inside content
   - `iterator` for renaming default
3. **For conditional**:
   - Empty list → no blocks generated
   - `for_each = condition ? [{...}] : []`
4. **For complex inputs**:
   - Map of objects
   - Each provides config to block
5. **For combining with static**:
   - Static blocks + dynamic
   - All evaluated; combined
6. **For nested dynamic**:
   - Dynamic inside dynamic
   - Use iterators for clarity
7. **For common pitfalls**:
   - Forgetting `content {}`
   - Iterator name collision
   - Wrong attribute access
8. **For when NOT to use**:
   - Static blocks simpler when no iteration
   - Don't dynamic everything

Mark DESTRUCTIVE: dynamic block generating wrong configs, missing required attributes, accidentally producing thousands of blocks.

---

Use case: [DESCRIBE]
Current attempt: [PASTE]
Symptom: [DESCRIBE]

Why this prompt works

Dynamic blocks are powerful but tricky. This prompt walks them.

How to use it

  1. Use when iteration needed.
  2. for_each appropriate.
  3. Static for simple.
  4. Test with small inputs first.

Patterns

Security group with dynamic rules

variable "ingress_rules" {
  description = "Map of ingress rules"
  type = map(object({
    from_port   = number
    to_port     = number
    protocol    = string
    cidr_blocks = list(string)
    description = optional(string)
  }))
  default = {
    https = {
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
      description = "HTTPS from anywhere"
    }
    ssh = {
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      cidr_blocks = ["10.0.0.0/8"]
      description = "SSH from internal"
    }
  }
}

resource "aws_security_group" "web" {
  name = "web"

  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 = lookup(ingress.value, "description", ingress.key)
    }
  }

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

Conditional dynamic

resource "aws_s3_bucket" "data" {
  bucket = "myorg-data"

  dynamic "lifecycle_rule" {
    for_each = var.enable_lifecycle ? [1] : []
    content {
      id      = "archive"
      enabled = true

      transition {
        days          = 90
        storage_class = "GLACIER"
      }
    }
  }

  dynamic "logging" {
    for_each = var.enable_logging ? [1] : []
    content {
      target_bucket = aws_s3_bucket.logs.id
      target_prefix = "data-access/"
    }
  }
}

Nested dynamic with iterator

resource "aws_iam_policy" "complex" {
  name = "complex"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      for k, v in var.permissions : {
        Effect   = v.effect
        Action   = v.actions
        Resource = v.resources
      }
    ]
  })
}

# Alternative: dynamic block inside resource
resource "aws_appautoscaling_policy" "ecs" {
  name = "policy"

  target_tracking_scaling_policy_configuration {
    target_value       = var.target_value

    dynamic "customized_metric_specification" {
      for_each = var.use_custom_metric ? [var.custom_metric] : []
      content {
        metric_name = customized_metric_specification.value.metric_name
        namespace   = customized_metric_specification.value.namespace
        statistic   = customized_metric_specification.value.statistic

        dynamic "dimensions" {
          for_each = customized_metric_specification.value.dimensions
          iterator = dim                  # rename to avoid confusion
          content {
            name  = dim.key
            value = dim.value
          }
        }
      }
    }
  }
}

Dynamic tags

locals {
  base_tags = {
    Environment = var.environment
    ManagedBy   = "Terraform"
  }

  resource_tags = merge(local.base_tags, var.additional_tags)
}

resource "aws_instance" "web" {
  ami           = "ami-..."
  instance_type = "t3.medium"

  tags = merge(
    local.resource_tags,
    {
      Name = "web-${var.environment}"
    }
  )
}

Static + dynamic combined

resource "aws_security_group" "mixed" {
  name = "mixed"

  # Static ingress (always present)
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "HTTPS"
  }

  # Dynamic additional ingress (configurable)
  dynamic "ingress" {
    for_each = var.additional_ingress
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}

Common findings this catches

  • Dynamic block missing → for_each empty.
  • Wrong attributeeach.value.x vs each.key.
  • Iterator collision in nested → rename.
  • Required block missing when for_each is empty → use static.
  • Performance with large inputs → profile.
  • Hard to read → static blocks where possible.
  • Conditional dynamic showing in plan even when empty.

When to escalate

  • Complex dynamic logic — refactor.
  • Performance issues at scale — profile.
  • Cross-module dynamic blocks — design.

Related prompts

Newsletter

Get weekly AI workflows for DevOps engineers

Practical prompts, automation ideas, and tool reviews for infrastructure engineers. One email per week. No spam.