Problems in creating aws route table using terraform - amazon-web-services

Running terraform v1.0.9 with AWS plugin v3.63.0 on a mac
Following hashicorp instructions for creating a route table (https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table), but getting the following error:
Error: Incorrect attribute value type
...
Inappropriate value for attribute "route": element 0: attributes "carrier_gateway_id", "destination_prefix_list_id", "egress_only_gateway_id",
│ "instance_id", "ipv6_cidr_block", "local_gateway_id", "nat_gateway_id", "network_interface_id", "transit_gateway_id", "vpc_endpoint_id", and
│ "vpc_peering_connection_id" are required.
Here is my main.tf:
provider "aws" {
region = "us-east-1"
}
resource "aws_vpc" "my-test-vpc" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "my-test-vpc"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.my-test-vpc.id
}
resource "aws_route_table" "prod-route-table" {
vpc_id = aws_vpc.my-test-vpc.id
route = [
{
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
]
tags = {
Name = "production"
}
}

I have run into something similar, without getting to the bottom of the root cause, so this is not the best possible answer, but moving the route out into its own explicit resource works for me:
resource "aws_route_table" "prod-route-table" {
vpc_id = aws_vpc.my-test-vpc.id
tags = {
Name = "production"
}
}
resource "aws_route" "prod-route-igw" {
route_table_id = aws_route_table.prod-route-table.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
depends_on = [aws_route_table.prod-route-table]
}

Related

Terraform: Why AWS NAT Gateway conflicts with Egress Only Internet Gateway

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
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
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
}

A managed resource "aws_internet_gateway" "igw_id" has not been declared in module.eip

I'm trying create eip and natgateway using terraform but i'm facing follwing error.
Error: Reference to undeclared resource
on ../../modules/eip/main.tf line 7, in resource "aws_eip" "nat-gateway":
7: depends_on = [aws_internet_gateway.igw_id]
A managed resource "aws_internet_gateway" "igw_id" has not been declared in
module.eip.
Here is my main.tf
variable "igw_id" {}
resource "aws_nat_gateway" "nat-gateway" {
allocation_id = "*********"
subnet_id = "*********"
depends_on = [aws_internet_gateway.igw_id]
tags {
Name = "*********"
Service = "*********"
Environment = terraform.workspace
}
}
Can anyone help me?
Here is my igw.tf
resource "aws_internet_gateway" "igw" {
vpc_id = "********"
tags = {
Name = "*********"
Service = "*********"
Environment = terraform.workspace
}
}
output.tf
output igw_id {
value = aws_internet_gateway.igw.id
}

Terraform how to get dynamic route in route table

Terraform how to get dynamic route in route table based on the workspace
UAT
resource "aws_vpn_gateway" "micorsoft_vpn_gw" {
vpc_id = aws_vpc.default.id
tags = {
Name = "micorsoft"
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.default.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.default.id
}
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_vpn_gateway.micorsoft_vpn_gw.id
}
tags = merge(map("Name", "${var.namespace}-${var.environment}"), var.tags)
}
DEV
resource "aws_route_table" "private" {
vpc_id = aws_vpc.default.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.default.id
}
tags = merge(map("Name", "${var.namespace}-${var.environment}"), var.tags)
}
How to achieve this is dynamic way based on workdspace/environment
You can use dynamic block for that.
resource "aws_route_table" "private" {
vpc_id = aws_vpc.default.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.default.id
}
dynamic "route" {
for_each = var.env == "DEV" ? toset([]) : toset([1])
content {
cidr_block = "0.0.0.0/0"
gateway_id = aws_vpn_gateway.micorsoft_vpn_gw.id
}
}
tags = merge(map("Name", "${var.namespace}-${var.environment}"), var.tags)
}
Basically, when the var.env is DEV, no second route will be created.

assign multiple subnets to route table aws

I think multiple people have asked the same question but my condition is different. I am taking input from the user for the vpc region, cidr value even the public subnet segment too. I have to attach all my public subnet to the default route table and private subnets to the diff route table . can you help me in how to attach them .
provider "aws" {
region = var.region
}
resource "aws_vpc" "app_vpc" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = var.vpc_name
}
}
# create igw
resource "aws_internet_gateway" "app_igw" {
vpc_id = aws_vpc.app_vpc.id
}
data "aws_availability_zones" "available" {
state = "available"
}
#provision public subnet
resource "aws_subnet" "public_subnet_01" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = var.public_subnet_01
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "public_subnet_01"
}
depends_on = [aws_vpc_dhcp_options_association.dns_resolver]
}
resource "aws_subnet" "public_subnet_02" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = var.public_subnet_02
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "public_subnet_02"
}
depends_on = [aws_vpc_dhcp_options_association.dns_resolver]
}
resource "aws_subnet" "public_subnet_03" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = var.public_subnet_03
availability_zone = data.aws_availability_zones.available.names[2]
tags = {
Name = "public_subnet_03"
}
depends_on = [aws_vpc_dhcp_options_association.dns_resolver]
}
#default route table
resource "aws_default_route_table" "default" {
default_route_table_id = aws_vpc.app_vpc.default_route_table_id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.app_igw.id
}
}
resource "aws_route_table_association" "default_association_01" {
subnet_id = [aws_subnet.public_subnet_01.id, aws_subnet.public_subnet_02.id, aws_subnet.public_subnet_03.id]
route_table_id = aws_vpc.app_vpc.default_route_table_id
}
I am getting error in adding multiple subnet so can u please help here :)
aws_route_table_association takes only one subnet as an input, not a list of subnets.
If you want to create the associations using your list, you can use for_each:
resource "aws_route_table_association" "default_association_01" {
for_each = toset([aws_subnet.public_subnet_01.id, aws_subnet.public_subnet_02.id, aws_subnet.public_subnet_03.id])
subnet_id = each.key
route_table_id = aws_vpc.app_vpc.default_route_table_id
}
The above assumes that everything else is correct. There could be still some errors in your code which aren't apparent yet.

Assigning multiple public subnet to route table using for each

I have multiple subnets created with for each that I am trying to get associated with a route table. Below is the code I have, error, and what I have tried.
locals {
az_names = data.aws_availability_zones.azs.names
pub_sub_ids = aws_subnet.public.*.id
}
resource "aws_route_table_association" "main" {
for_each = var.public_sub_cidr
subnet_id = local.pub_sub_ids[each.key]
route_table_id = aws_route_table.main.id
}
resource "aws_subnet" "public" {
for_each = { for index, az_name in local.az_names : index => az_name }
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, each.key + 1)
availability_zone = local.az_names[each.key]
map_public_ip_on_launch = true
tags = {
Name = "${var.vpc_tags}-PubSubnet"
}
}
Error: Unsupported attribute
on vpc.tf line 3, in locals:
3: pub_sub_ids = aws_subnet.public.*.id
This object does not have an attribute named "id".
I believe this should be working. Any advice on this error and getting these public subnets to attach to the route table would be helpful.
UPDATE
I made some changes and removed the local variable 'pub_sub_ids' and also changed 'aws_route_table_association" "main" to
resource "aws_route_table_association" "main" {
for_each = var.public_sub_cidr
subnet_id = each.key
route_table_id = aws_route_table.main.id
}
Now I am getting an error
Error: Error creating route table association: InvalidSubnetID.NotFound: The `subnet ID '' does not exist`
It says the subnet does not exist even though I see it in the Console. Any advice would be appreciated in associating these public subnets to the route table.
subnet_id in aws_route_table_association should be subnet id, not subnet CIDR.
Since aws_route_table is not given, I made my own to verify the setup. Thus you could do the following:
resource "aws_route_table_association" "main" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.main.id
}
And below is the full code I used for verification:
provider "aws" {
# your data
}
data "aws_availability_zones" "azs" {
state = "available"
}
locals {
az_names = data.aws_availability_zones.azs.names
}
variable "vpc_cidr" {
default = "10.0.0.0/16"
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
}
resource "aws_subnet" "public" {
for_each = {for index, az_name in local.az_names: index => az_name}
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, each.key + 1)
availability_zone = local.az_names[each.key]
map_public_ip_on_launch = true
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main"
}
}
resource "aws_route_table" "main" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
}
resource "aws_route_table_association" "main" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.main.id
}