0

My VPC which I've spinned up using the terraform aws vpc module has spinned up 3 /24 public subnets, 3 /24 private subnets and 2 spare subnets.

Our InfoSec team has mandated that if traffic is to flow from either the public subnet to the private subnet, or vice-versa, the traffic must be routed via a GWLB firewall service endpoint that we must deploy into one of the spare subnets (aka the inspection subnet).

I have already setup the GWLB endpoint and also updated the 1 public route-table that the terraform module setup with route to the GWLB device when traffic needs to pass from the public subnets to the private subnets (this 1 route table is attached to all 3 public subnets)

For whatever reason, the vpc module has setup 3 separate route tables, one for each AZ, and associated one to each of the private subnets. I am trying to figure out how to programmatically add the GWLB route to each of the private route tables for all the three public subnet cidr blocks.

From the module:

  • module.vpc.private_route_table_ids is a list of 3 route tables
  • module.vpc.public_subnets_cidr_blocks is a list of 3 '/24' CIDR blocks

This appears to be a use-case of needing 2 nested counts or for_each loops, but as I am yet very well versed with terraform, trying to reach out for help from more experienced folks here.

Here's the code block I wrote for updating the route table associated the public subnets to add the private subnet CIDR blocks:

locals {
  pub_to_private_route_map = setproduct(
    module.vpc.public_route_table_ids,
    module.vpc.private_subnets_cidr_blocks
  )
}

resource "aws_route_table" "pub_to_private_via_firewall" {
  count = local.pub_to_private_route_map > 0 ? local.pub_to_private_route_map : 0

  route_table_id         = local.pub_to_private_route_map[count.index][0]
  destination_cidr_block = local.pub_to_private_route_map[count.index][1]
  vpc_endpoint_id        = aws_vpc_endpoint.firewall_vpc_endpoint[0].id # This one is created elsewhere in the code

  timeouts {
    create = "5m"
  }
}

I know that this code can cause issues if there's ever the case that there are multiple public_route_table_ids, but for now, the module seems to only create one of them.

How do I do a double count or nested for_each to enumerate this:

  • module.vpc.private_route_table_ids which is a list of 3 route tables
  • module.vpc.public_subnets_cidr_blocks which is a list of 3 '/24' CIDR blocks

Thanks for your help.

Marko E
  • 13,362
  • 2
  • 19
  • 28

1 Answers1

0

After rummaging around stackoverflow, I came across this answer, using which I managed to achieve my requirement by creating my own mapping object and then using it in the aws_route code, like so:

locals {
  rtbl_subnetcidr_mappings = merge([
    for rtbl_id in local.pub_rtbl_ids : {
      for subnet_cidr_block in module.vpc.private_subnets_cidr_blocks :
      "${rtbl_id}_${subnet_cidr_block}" => {
        rtblid              = rtbl_id
        subnet_cidr_block = subnet_cidr_block
      }
    }
  ]...) # https://www.terraform.io/language/expressions/function-calls#expanding-function-arguments
}

resource "resource" "aws_route" "private_to_public_via_fw_route" {
  for_each = var.create_firewall_service_endpoint == true ? local.rtbl_subnetcidr_mappings : null

  route_table_id         = each.value.rtbl_id
  destination_cidr_block = each.value.subnet_cidr_block
  vpc_endpoint_id        = aws_vpc_endpoint.firewall_vpc_endpoint[0].id

  timeouts {
    create = "5m"
  }
}

With this, my goal is achieved.