Using Terraform for_each statements and dynamic nested blocks to simply AWS security group Ingress statements


Over the past few months, I’ve been updating various Terraform modules to utilize the new features in 0.12. Among these, is the ability to iterate over dynamic blocks with for_each. Utilizing this new feature has allowed me to reduce the size of my security groups, while making them more readable. To show this feature in action, I will create a new map variable with the port as a key, and a list of CIDR blocks to allow in as the value:

variable "ec2_ingress_ports_default" {
  description = "Allowed Ec2 ports"
  type        = list
  default     = {
    "22"  = ["192.168.1.0/24", "10.1.1.0/24" ]
    "443" = ["0.0.0.0/0"]
}

To populate the Ingress statements, you can define a dynamic block, and then use for_each to iterate through the map and populate each ingress stanza:

resource "aws_security_group" "ec2_default_rules" {

  name = "ecs_default_sg_rules"

  dynamic ingress {
    for_each = var.ec2_ingress_ports_default
    content {
      from_port   = ingress.key
      to_port     = ingress.key
      cidr_blocks = ingress.value
      protocol    = "tcp"
    }
  }
}

The final result will be one or more Ingress statements, each defining the CIDR block source IPs that are allowed to connect to the port:

+ {
    + cidr_blocks      = [
        + "192.168.1.0/24",
        + "10.1.1.0/24",
      ]
    + description      = ""
    + from_port        = 22
    + ipv6_cidr_blocks = []
    + prefix_list_ids  = []
    + protocol         = "tcp"
    + security_groups  = []
    + self             = false
    + to_port          = 22
  },

I’ve been able to drastically reduce the amount of HCL in my custom modules, which is always a good thing. There are tons of uses for this, and I will continue to refine things as I clean up old Terraform .tf files.

This article was posted by on 2020-04-29 01:00:00 -0500 -0500