2

I have public and private subnets established in a VPC created with for each. I am now trying to create route tables for the subnets and nat gateways specifically for access for private instances. My subnets, route tables, and public subnet associations are working properly. I am having trouble getting my private subnets to attach to the route table connecting it to the NAT gateway. I believe my logic correct. My NAT gateways are sitting in my public subnets. The only issue is private subnets being attached to the route table that connects to the NAT gateway. Below is my code, any advice is appreciated.

resource "aws_route_table" "public" {
  for_each = var.pub_subnet
  vpc_id   = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = var.rt_tags
  }
}

resource "aws_route_table_association" "public" {
  for_each       = aws_subnet.public
  route_table_id = aws_route_table.public[each.key].id
  subnet_id      = each.value.id
}

resource "aws_route_table_association" "nat" {
  for_each       = aws_subnet.private
  route_table_id = aws_route_table.nat[each.key].id
  subnet_id      = each.value.id
}

resource "aws_route_table" "nat" {
  for_each = var.pub_subnet
  vpc_id   = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_nat_gateway.main[each.key].id
  }

  tags = {
    Name = var.rt_tags_private
  }
}

resource "aws_subnet" "public" {
  for_each                = var.pub_subnet
  vpc_id                  = aws_vpc.main.id
  cidr_block              = each.value.cidr_block
  availability_zone       = each.value.availability_zone
  map_public_ip_on_launch = true
  tags = {
    Name = each.key
  }
}

resource "aws_subnet" "private" {
  for_each                = var.priv_subnet
  vpc_id                  = aws_vpc.main.id
  cidr_block              = each.value.cidr_block
  availability_zone       = each.value.availability_zone
  map_public_ip_on_launch = false
  tags = {
    Name = each.key
  }
}

Variables

variable "pub_subnet" {
  type = map(object({
    cidr_block        = string
    availability_zone = string
  }))
  default = {
    "PubSub1" = {
      cidr_block        = "10.0.1.0/24"
      availability_zone = "us-west-1a"
    }
  }
}

variable "priv_subnet" {
  type = map(object({
    cidr_block        = string
    availability_zone = string
  }))
  default = {
    "PrivSub1" = {
      cidr_block        = "10.0.2.0/24"
      availability_zone = "us-west-1c"
    }
  }
}

Error

Error: Invalid index

  on vpc.tf line 61, in resource "aws_route_table_association" "nat":
  61:   route_table_id = aws_route_table.nat[each.key].id
    |----------------
    | aws_route_table.nat is object with 1 attribute "PubSub1"
    | each.key is "PrivSub1"

The given key does not identify an element in this collection value.

NAT Gateway

resource "aws_nat_gateway" "main" {
  for_each      = aws_subnet.public
  subnet_id     = each.value.id
  allocation_id = aws_eip.main[each.key].id
}

EIP

resource "aws_eip" "main" {
  for_each = aws_subnet.public
  vpc      = true

  lifecycle {
    create_before_destroy = true
  }
}
Dave Michaels
  • 847
  • 1
  • 19
  • 51

1 Answers1

2

You are defining your route table for nat using var.pub_subnet which has the form of:

  "PubSub1" = {
      cidr_block        = "10.0.1.0/24"
      availability_zone = "us-west-1a"
    }

Thus to refer to aws_route_table you have to use PubSub1 key.

However, in your aws_route_table_association you are iterating over aws_subnet.private which has key of PrivSub1.

update

The issue can be overcome by creating a local mapping for private=>public subnets names, e.g.:

locals {
   private_public_mapping = zipmap(keys(var.priv_subnet), keys(var.pub_subnet))
}

resource "aws_route_table_association" "nat" {
  for_each       = aws_subnet.private
  route_table_id = aws_route_table.nat[local.private_public_mapping[each.key]].id
  subnet_id      = each.value.id
}
Marcin
  • 215,873
  • 14
  • 235
  • 294
  • Right, I need to iterate over the private subnets because I am trying to give resources in the private subnet a connection to the NAT gateway. If I iterate over aws_subnet.public, it will then connect public subnets to the NAT gateway, which I don't need. – Dave Michaels Aug 12 '20 at 22:57
  • @DaveMichaels Hi. Can you update your question with definition of `aws_nat_gateway.main`? I will try to replicate the issue and see what can be done. – Marcin Aug 12 '20 at 23:30
  • 1
    Hey, I added it! – Dave Michaels Aug 12 '20 at 23:36
  • Hi @Marcin, I am not sure if Terraform has this limitation when it comes to NAT gateways and private subnets. I believe my logic is correct. Do you have an idea how to approach this? – Dave Michaels Aug 13 '20 at 02:28
  • @DaveMichaels Can you also provide `aws_eip.main`. I'm trying to run your code, but all the time something is missing. – Marcin Aug 13 '20 at 03:25
  • Hey, my apologies. Overlooked that one. – Dave Michaels Aug 13 '20 at 03:31
  • @DaveMichaels Ok. Now I can replicate the issue. I will check how it can be addressed. – Marcin Aug 13 '20 at 03:37
  • Hmm, I actually don't have a resource for that – Dave Michaels Aug 13 '20 at 03:37
  • @DaveMichaels Yes, it was my mistake :-( – Marcin Aug 13 '20 at 03:38
  • @DaveMichaels You can check updated answer. My aim was to have minim number of modifications to your code. Its an unusual design. – Marcin Aug 13 '20 at 04:11