Dynamic blocks in Terraform allow you to generate multiple nested blocks within a resource or module dynamically. This feature is particularly useful when you need to create a variable number of nested blocks based on input variables or other dynamic data.

Key Concepts

  1. Dynamic Block Structure:

    • A dynamic block is defined using the dynamic keyword.
    • It contains a for_each argument to iterate over a collection.
    • The content block defines the structure of the nested blocks to be generated.
  2. Use Cases:

    • Creating multiple sub-resources within a resource.
    • Generating configuration blocks based on variable input.

Syntax

The basic syntax for a dynamic block is as follows:

resource "resource_type" "resource_name" {
  # Other resource arguments

  dynamic "block_name" {
    for_each = var.collection
    content {
      # Nested block arguments
      argument1 = block_name.value.argument1
      argument2 = block_name.value.argument2
    }
  }
}
  • block_name: The name of the nested block to be generated.
  • for_each: The collection to iterate over.
  • content: The structure of the nested block.

Practical Example

Let's consider an example where we need to create multiple security group rules for an AWS security group. The rules are defined in a variable.

Variable Definition

First, define a variable to hold the security group rules:

variable "security_group_rules" {
  description = "List of security group rules"
  type = list(object({
    type        = string
    from_port   = number
    to_port     = number
    protocol    = string
    cidr_blocks = list(string)
  }))
  default = []
}

Resource Definition with Dynamic Block

Next, use a dynamic block to create the security group rules:

resource "aws_security_group" "example" {
  name        = "example"
  description = "Example security group"
  vpc_id      = var.vpc_id

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

Example Variable Values

Provide values for the security_group_rules variable:

variable "security_group_rules" {
  default = [
    {
      type        = "ingress"
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    },
    {
      type        = "ingress"
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  ]
}

Practical Exercise

Exercise

  1. Define a variable firewall_rules to hold a list of firewall rules.
  2. Create a Google Cloud Platform (GCP) firewall resource using a dynamic block to generate the rules.

Solution

Variable Definition

variable "firewall_rules" {
  description = "List of firewall rules"
  type = list(object({
    name        = string
    direction   = string
    priority    = number
    ranges      = list(string)
    allow_ports = list(object({
      protocol = string
      ports    = list(string)
    }))
  }))
  default = []
}

Resource Definition with Dynamic Block

resource "google_compute_firewall" "example" {
  name    = "example-firewall"
  network = var.network

  dynamic "allow" {
    for_each = var.firewall_rules
    content {
      protocol = allow.value.allow_ports[0].protocol
      ports    = allow.value.allow_ports[0].ports
    }
  }

  source_ranges = var.firewall_rules[0].ranges
  direction     = var.firewall_rules[0].direction
  priority      = var.firewall_rules[0].priority
}

Example Variable Values

variable "firewall_rules" {
  default = [
    {
      name        = "allow-http"
      direction   = "INGRESS"
      priority    = 1000
      ranges      = ["0.0.0.0/0"]
      allow_ports = [
        {
          protocol = "tcp"
          ports    = ["80"]
        }
      ]
    },
    {
      name        = "allow-https"
      direction   = "INGRESS"
      priority    = 1000
      ranges      = ["0.0.0.0/0"]
      allow_ports = [
        {
          protocol = "tcp"
          ports    = ["443"]
        }
      ]
    }
  ]
}

Common Mistakes and Tips

  • Incorrect for_each Type: Ensure that the for_each argument is a collection (list, map, or set).
  • Referencing Values: Use block_name.value to reference the current item in the collection.
  • Nested Blocks: Dynamic blocks can be nested within other dynamic blocks if needed.

Conclusion

Dynamic blocks in Terraform provide a powerful way to generate multiple nested blocks based on dynamic data. By understanding and utilizing dynamic blocks, you can create more flexible and reusable Terraform configurations. In the next section, we will explore Terraform Import, which allows you to import existing infrastructure into your Terraform state.

© Copyright 2024. All rights reserved