terraform setproduct: combine list of objects to generate all the possible combinations

4 min read | by Jordi Prats

Let's imagine we want to create a security group with the following ingress rules:

ingress_rules = [
    {
        protocol = "tcp"
        cidr_blocks = [ "1.1.1.1/32", "2.2.2.2/32" ]
    },
    {
        protocol = "tcp"
        cidr_blocks = [ "1.2.3.4/32" ]
    }
]

For each of the following ports:

services = ["80", "443", "8080"]

We can use the terraform function setproduct() to calculate all the combinations of elements from the given sets. That's also called the Cartesian product. For this example it's going to be 2x3.

We can use the following code that loops using for_each on the resulting values from using setproduct on both lists:

  dynamic "ingress" {
    for_each = setproduct(var.ingress_rules, var.services)
    content {
      description = "allow ${ingress.value[1]}"
      from_port   = ingress.value[1]
      to_port     = ingress.value[1]
      protocol    = ingress.value[0]["protocol"]
      cidr_blocks = ingress.value[0]["cidr_blocks"]
    }
  }

Its going to create 6 rules, we can test it using terraform plan:

$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_security_group.demo_sg will be created
  + resource "aws_security_group" "demo_sg" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = (known after apply)
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "1.1.1.1/32",
                  + "2.2.2.2/32",
                ]
              + description      = "allow 443"
              + from_port        = 443
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 443
            },
          + {
              + cidr_blocks      = [
                  + "1.1.1.1/32",
                  + "2.2.2.2/32",
                ]
              + description      = "allow 80"
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
          + {
              + cidr_blocks      = [
                  + "1.1.1.1/32",
                  + "2.2.2.2/32",
                ]
              + description      = "allow 8080"
              + from_port        = 8080
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 8080
            },
          + {
              + cidr_blocks      = [
                  + "1.2.3.4/32",
                ]
              + description      = "allow 443"
              + from_port        = 443
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 443
            },
          + {
              + cidr_blocks      = [
                  + "1.2.3.4/32",
                ]
              + description      = "allow 80"
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
            },
          + {
              + cidr_blocks      = [
                  + "1.2.3.4/32",
                ]
              + description      = "allow 8080"
              + from_port        = 8080
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 8080
            },
        ]
      + name                   = "demo_sg"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = "vpc-1a2b3c4d"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

You'll be able to find the the code used on this post on the following github repo:

https://github.com/pet2cattle/terraform-setproduct


Posted on 16/06/2021

Categories