1
votes

I have both IPv4 and IPv6, I'm trying to manage a Routing for private subnet.

Once NAT Gateway is attached to Route Table, it does not allow me to attach Egress Gateway to the same route table, and giving me an error:

An interface that is part of a NAT gateway cannot be the next hop for an IPv6 destination CIDR block or IPv6 prefix list

However if I'm attaching manually thought AWS Console, there is no problem

Manual Attachment worked as expected

Maybe I'm missing some info? I know that NAT only for IPv4 and Egress only for IPv6, can someone guide me on this? Why if NAT not compatible with Egress Only Gateway, it allows me to attach via aws console, but not with terraform?

Here is my simple terraform

resource "aws_eip" "neip"  {
   count = length(var.private_subnet) 
   vpc   = true
}

resource "aws_nat_gateway" "nat" {
   count = length(var.private_subnet) 
   subnet_id     = element(var.public_subnet, count.index)
   allocation_id = element(aws_eip.neip.*.id, count.index)
}
resource "aws_egress_only_internet_gateway"  "egw"  {
   count  = length(var.zones) > 0 ? 1 : 0
   vpc_id = var.vpc_id
}
resource "aws_route_table" "route" {
   count = length(var.private_subnet) 
   vpc_id = var.vpc_id
}

resource "aws_route" "ipv4" {
   count           = length(aws_route_table.route) 
   depends_on      = [ aws_route_table.route ]
   route_table_id  = aws_route_table.route[count.index].id
   nat_gateway_id  = element(aws_nat_gateway.nat.*.id, count.index)
   destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route" "ipv6"  {
   count                   = length(aws_route_table.route) 
   depends_on              = [ aws_route_table.route ]
   route_table_id          = aws_route_table.route[count.index].id
   egress_only_gateway_id  = element(aws_egress_only_internet_gateway.egw.*.id, count.index)
   destination_ipv6_cidr_block = "::/0"
}

resource "aws_route_table_association" "route" {
   count          = length(aws_route_table.route) 
   subnet_id      = var.private_subnet[count.index]
   route_table_id = aws_route_table.route[count.index].id
}

No issue with terraform script

enter image description here

1
Can you provide actuall example TF code which can demonstrate the issue and allow for its reproduction?Marcin
Hello, @Marcin, I've added the terraform fileBow Bow
Interesting. Theoretically this should not be a problem. An issue with Terraform, maybe?Dennis Traub
Can you check the sequence of API calls in AWS CloudTrail to e see which specific action fails?Dennis Traub
Does this code even work? Because you are using count to define aws_nat_gateway, but then you are referencing it using regular aws_nat_gateway.nat.id? Can you provide what are these var.private_subnet and the rest of variables?Marcin

1 Answers

0
votes

I tried to reproduce your issue, but for me it works as expected. Maybe you still have some "typos" in your code presented here, thus its difficult to see why it woudn't work for you.

Anyway, here is the code I used in order to mimic your setup, though large chunks I had to create myself, as they are not shown in your code (e.g. VPC setup all missing, internet gateway, public subnets).

The code below works and I couldn't replicate your issue. Route tables work as expected:


data "aws_availability_zones" "available" {}

resource "aws_vpc" "vpc" {
    cidr_block = "10.0.0.0/16"
    enable_dns_hostnames = true
    assign_generated_ipv6_cidr_block = true
    tags = {
        Name = "testvpc"
    }
}

variable "private_cidrs" {
    default = ["10.0.2.0/24", "10.0.3.0/24"]
}

variable "public_cidrs" {
    default = ["10.0.0.0/24", "10.0.1.0/24"]
}

resource "aws_subnet" "public_subnet" {

    count = length(var.public_cidrs)

    cidr_block = var.public_cidrs[count.index]
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[count.index]
    tags = {
        Name = "public${count.index}"
    }
}

resource "aws_subnet" "private_subnet" {

    count = length(var.private_cidrs)

    cidr_block = var.private_cidrs[count.index]
    vpc_id = aws_vpc.vpc.id
    availability_zone = data.aws_availability_zones.available.names[count.index]
    tags = {
        Name = "private${count.index}"
    }
}

resource "aws_eip" "neip"  {
   count = length(var.private_cidrs) 
   vpc   = true
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "main"
  }
}

resource "aws_nat_gateway" "nat" {
   count         = length(var.private_cidrs) 
   subnet_id     = element(aws_subnet.public_subnet.*.id, count.index)
   allocation_id = element(aws_eip.neip.*.id, count.index)
   
   depends_on    = [aws_internet_gateway.igw]
}

resource "aws_egress_only_internet_gateway"  "egw"  {
   #count  = length(var.private_cidrs) 
   vpc_id = aws_vpc.vpc.id
}

# routes for public subnets

resource "aws_route_table" "public_route" {
   count = length(var.public_cidrs) 
   vpc_id = aws_vpc.vpc.id
}

resource "aws_route" "public_ipv4" {
   count           = length(aws_route_table.public_route) 
   route_table_id  = aws_route_table.public_route[count.index].id
   gateway_id  = aws_internet_gateway.igw.id
   destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route" "ipv6_public"  {
   count                   = length(aws_route_table.public_route) 
   route_table_id          = aws_route_table.public_route[count.index].id
   egress_only_gateway_id  = aws_egress_only_internet_gateway.egw.id
   destination_ipv6_cidr_block = "::/0"
}

resource "aws_route_table_association" "public_route" {
   count          = length(aws_route_table.public_route) 
   subnet_id      = aws_subnet.public_subnet[count.index].id
   route_table_id = aws_route_table.public_route[count.index].id
}

# routes for private subnets

resource "aws_route_table" "route" {
   count = length(var.private_cidrs) 
   vpc_id = aws_vpc.vpc.id
}

resource "aws_route" "ipv4" {
   count           = length(aws_route_table.route) 
   route_table_id  = aws_route_table.route[count.index].id
   nat_gateway_id  = aws_nat_gateway.nat[count.index].id
   #nat_gateway_id  = aws_nat_gateway.nat.id
   destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route" "ipv6"  {
   count                   = length(aws_route_table.route) 
   route_table_id          = aws_route_table.route[count.index].id
   egress_only_gateway_id  = aws_egress_only_internet_gateway.egw.id
   destination_ipv6_cidr_block = "::/0"
}

resource "aws_route_table_association" "route" {
   count          = length(aws_route_table.route) 
   subnet_id      = aws_subnet.private_subnet[count.index].id
   route_table_id = aws_route_table.route[count.index].id
}