2

Using terraform-0.7.7. I'm following, more or less, the Best Practices repo, the AWS part:

https://github.com/hashicorp/best-practices/tree/master/terraform

I am trying to come up with a general template for building VPCs. The idea is to have a network module with several sub-modules (vpc, private_subnet, etc), and just plug into it different variables from terraform.tfvars files, in order to build different environments.

Let's say in the .tfvars file for one environment I have a list of availability zones, and another list with the IP blocks for the private subnets:

azs             = "us-west-2a,us-west-2b,us-west-2c"
private_subnets = "10.XXX.1.0/24,10.XXX.2.0/24,10.XXX.3.0/24"

The network/private_subnets module will happily create subnets, route tables, and associations based on those lists:

resource "aws_subnet" "private" {
  vpc_id            = "${var.vpc_id}"
  cidr_block        = "${element(split(",", var.cidrs), count.index)}"
  availability_zone = "${element(split(",", var.azs), count.index)}"
  count             = "${length(split(",", var.cidrs))}"

  tags      { Name = "${var.name}-${element(split(",", var.azs), count.index)}-private" }
  lifecycle { create_before_destroy = true }
}

resource "aws_route_table" "private" {
  vpc_id = "${var.vpc_id}"
  count  = "${length(split(",", var.cidrs))}"

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = "${element(split(",", var.nat_gateway_ids), count.index)}"
  }

  tags      { Name = "${var.name}-${element(split(",", var.azs), count.index)}-private" }
  lifecycle { create_before_destroy = true }
}

resource "aws_route_table_association" "private" {
  count          = "${length(split(",", var.cidrs))}"
  subnet_id      = "${element(aws_subnet.private.*.id, count.index)}"
  route_table_id = "${element(aws_route_table.private.*.id, count.index)}"

  lifecycle { create_before_destroy = true }
}

That works well. For each environment, I have different azs and private_subnets lists, and the VPC is created correctly. The NAT gateways are created prior to that in a different module. I have one NAT gateway, one private subnet, and one private routing table, per AZ.

But now I'm trying to create VPC peering connections in the same way.

peer_vpc_ids    = "vpc-XXXXXXXX, vpc-YYYYYYYY"
peer_vpc_blocks = "10.XXX.0.0/16, 10.YYY.0.0/16"

So now I need to write Terraform code that goes into the private route tables and adds routes for each VPC peering connection.

The problem is, I need to iterate by two variables: the list of AZs, and the list of peering connections, and Terraform does not seem to allow that. AFAICT, you cannot do nested loops in Terraform.

Am I missing something? Is there a better way to solve this problem?

Of course I could write by hand some custom spaghetti code that would build the VPC no matter what, but the goal here is to keep the code composable and maintainable, and separate the logic from the attributes.

Florin Andrei
  • 1,067
  • 3
  • 11
  • 33
  • Having a similar requirement :) – Hafiz Nov 29 '16 at 14:30
  • In these situations I typically just have a resource that is count `x * y` and then in one of my `element` calls in interpolation, i will do something like `element(var.list, floor(count.index / x))`. Since `element` loops over the list, this accomplishes most of the need (albeit not as cleanly as a nested list). – Paul Tyng Dec 12 '16 at 16:22

1 Answers1

2

You can potentially achieve this with some math on the element interpolation method:

resource "aws_vpc_peering_connection" {
  peer_vpc_ids = "${element(var.vpc_ids, count.index)}"
  peer_vpc_blocks = "${element(var.vpc_blocks,floor(count.index / length(var.vpc_ids))}"
  count = "${length(var.vpc_ids) * length(var.vpc_blocks)}"
}

Specifically the element(var.vpc_blocks,floor(count.index / length(var.vpc_ids)) call will make VPC blocks increment 1 for each of the var.vpc_ids values.

Paul Tyng
  • 7,924
  • 1
  • 33
  • 57