Terraform dynamic blocks

3 min read

When we have a resource that can have multiple nested blocks to be configured we can use dynamic blocks to configure it dynamically. A perfect example is a SecurityGroup that can have multiple ingress and egress rules to be able to allow traffic. Let's use the following aws_security_group resource definition as a starting point:

resource "aws_security_group" "demo_sg" {
  name = "demo_sg"
  description = "pet2cattle demo SG with dynamic blocks"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = [aws_vpc.main.cidr_block]
  }

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

To be able to configure the SegurityGroup rules we are going to use the following variables (ingress_rules and egress_rules) They are going to be a list of objects containing the variables we are going to use for each rule:

variable "ingress_rules" {
  description = "ingress rule"
  type        = list(object({
                              from_port = number
                              to_port   = number
                              protocol  = string
                              cidr_blocks = list(string)
                          }))
}

variable "egress_rules" {
  description = "egress rules with cidr_block"
  type        = list(object({
                              from_port   = number
                              to_port     = number
                              protocol    = string
                              cidr_blocks = list(string)
                          }))
}

Then we will have to transform the ingress nested block into a dynamic block looping thru the var.ingress_rules using the for_each keyword:

  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"]
    }
  }

For the egress we will do exactly the same. For the data that we want to set on the dynamic block we will have to use the content nested block using the block name (on this example egress) for referencing each of the objects on the for_each list:

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

Putting all together it will look like this:

resource "aws_security_group" "demo_sg" {
  name = "demo_sg"
  description = "pet2cattle demo SG with dynamic blocks"
  vpc_id = aws_vpc.main.id

  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"]
    }
  }

  dynamic "egress" {
    for_each = var.egress_rules_cidr
    content {
      from_port   = egress.value["from_port"]
      to_port     = egress.value["to_port"]
      protocol    = egress.value["protocol"]
      cidr_blocks = egress.value["cidr_blocks"]
    }
  }
}

So, we can use the following variables to create the incoming and outgoing rules:

  ingress_rules = [{
                    from_port   = 0,
                    to_port     = 443,
                    protocol    = "tcp",
                    cidr_blocks = ["0.0.0.0/0"]
                  },
                  {
                    from_port   = 0,
                    to_port     = 80,
                    protocol    = "tcp",
                    cidr_blocks = ["0.0.0.0/0"]
                  }]

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

Using these variable we will be able to dynamically configure the rules of this SecurityGroup, but we can do the same for any other resource with nested blocks.


Posted on 27/05/2021