Terraform nat gateway AWS - amazon-web-services

I am trying to create nat gateway from terraform by using AWS as provider but subnet_id in resource aws_nat_gateway always gives me error. I am trying to assign public subnet in subnet_id on resource "aws_nat_gateway" "sample_nat_gateway" from variables.tf file but failing in doing so and need support if someone can assist ?
Below is my vpc.tf file of vpc module
resource "aws_subnet" "public-subnet" {
for_each = var.prefix
availability_zone_id = each.value["az"]
cidr_block = each.value["cidr"]
vpc_id = aws_vpc.sample_vpc.id
tags = {
Name = "${var.name}-${each.value["az"]}"
}
}
resource "aws_nat_gateway" "sample_nat_gateway" {
allocation_id = aws_eip.sample_eip.id
subnet_id = ""
tags = {
Name = "${var.name}-sample-nat-gateway"
Environment = var.environment
}
depends_on = [aws_internet_gateway.sample_igw]
}
variables.tf
variable "prefix" {
type = map
default = {
sub-1 = {
az = "use2-az1"
cidr = "10.0.1.0/16"
}
sub-2 = {
az = "use2-az2"
cidr = "10.0.2.0/24"
}
}
}

Subent's can't be empty You have to provide valid subnet id where the NAT is going to be placed. For example:
resource "aws_nat_gateway" "sample_nat_gateway" {
allocation_id = aws_eip.sample_eip.id
subnet_id = aws_subnet.public-subnet["sub-1"].id
tags = {
Name = "${var.name}-sample-nat-gateway"
Environment = var.environment
}
depends_on = [aws_internet_gateway.sample_igw]
}
where aws_subnet.example is one of the public subnets in your VPC.

Related

In Terraform, how can I create an iterative list out of two aws_subnet objects?

New to Terraform. I have two aws_subnet objects which I want to associate with route tables. As I understand it, each AZ will need it's own route table. The easiest thing to do would be just declare two route tables, one for each subnet but would like to know if there is a better way to do it instead of just settling for things thrown together.
I have declared my subnets as a list in variables.tf:
variable "my_public_subnets" {
type = list
description = "public subnet within vpc cidr block"
default = ["10.1.2.0/24", "10.1.1.0/24"]
}
And have two public subnets in main.tf
resource "aws_subnet" "pub_1" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.my_public_subnets[0]
availability_zone = "us-east-1a"
}
resource "aws_subnet" "pub_2" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.my_public_subnets[1]
availability_zone = "us-east-1b"
}
Instead of:
resource "aws_route_table_association" "pub_ra_1" {
subnet_id = aws_subnet.pub_1.id
route_table_id = aws_route_table.bar.id
}
resource "aws_route_table_association" "pub2_ra_2" {
subnet_id = aws_subnet.pub_2.id
route_table_id = aws_route_table.bar.id
}
Is there way to do something like this? Create a list/array/map of those two subnets so I don't have to declare a aws_route_table_association for both of them? Maybe there's a better way to set this up in general?
locals {
my_pub_subnets = [aws_subnet.pub_1, aws_subnet.pub_2]
}
resource "aws_route_table_association" "pub_rt_a" {
for_each = locals.my_pub_subnets
subnet_id = each.value
route_table_id = aws_route_table.some_public_route_table.id
depends_on = [aws_subnet.pub_1]
}
Modules are how you create repeatable procedures in TF.
Something like:
locals{
subnets = {
public = "10.1.2.0/24",
private = "10.1.1.0/24"
}
module "subnets" {
source = "./modules/subnets"
for_each = subnets
name = each.key
cidr = each.value
}
for the AZ names, you could also use data.aws_availability_zones.available.names
I would guess that most of you want is really well done inside the VPC module.
You would have to import the VPC into your state to start, but this is how I do my subnets with it.
locals {
subnets = chunklist(cidrsubnets("10.2.8.0/24", 3, 3, 3, 3, 3, 3), 2)
public_subnets = local.subnets[1]
private_subnets = local.subnets[2]
}
data "aws_availability_zones" "available" {
}
resource "aws_eip" "nat" {
count = length(local.private_subnets)
vpc = true
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.14.0"
name = "foo"
cidr = "10.2.8.0/24"
azs = data.aws_availability_zones.available.names
private_subnets = local.private_subnets
public_subnets = local.public_subnets
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
reuse_nat_ips = true # <= Skip creation of EIPs for the NAT Gateways
external_nat_ip_ids = aws_eip.nat.*.id
public_subnet_tags = {
"Tier" = "Public"
}
private_subnet_tags = {
"Tier" = "Private"
}
}
output "public_subnets" {
value = module.vpc.public_subnets
}
output "public_subnets_cidr" {
value = module.vpc.public_subnets_cidr_blocks
}
output "private_subnets" {
value = module.vpc.private_subnets
}
output "private_subnets_cidr" {
value = module.vpc.private_subnets_cidr_blocks
}

Terraform ENI Mapping error - does not fall within the subnet's address range

I am trying to map my ENI to my subnet and its throwing an error.
Because there is a for_each loop on the subnet the ENI pointing to it must also have a looped key/value added to it hence the problem
main.tf
# VPC
resource "aws_vpc" "main" {
cidr_block = local.json.vpc.cidr
tags = {
Name = "vpc"
}
}
# Subnet
resource "aws_subnet" "public" {
for_each = local.api
vpc_id = aws_vpc.main.id
cidr_block = each.value.subnet_cidr
availability_zone = each.value.subnet_az
}
# ENI
resource "aws_network_interface" "eni" {
for_each = local.api
subnet_id = aws_subnet.public[each.key].id
private_ips = ["172.16.10.100"] # Might need to add another IP
tags = {
Name = "primary_network_interface"
}
}
my locals look like this
locals {
json = jsondecode(file("API.json"))
api = merge([
for vpc in local.json : {
for subnet in vpc.subnets :
"${vpc.name}-${subnet.name}" => {
vpc_name = vpc.name
vpc_cidr = vpc.cidr
subnet_name = subnet.name
subnet_cidr = subnet.cidr
subnet_az = subnet.az
}
}
]...)
}
their output (local.api) from terraform console
{
"vpc-subnet-one" = {
"subnet_az" = "eu-central-1a"
"subnet_cidr" = "192.168.1.0/24"
"subnet_name" = "subnet-one"
"vpc_cidr" = "192.168.0.0/16"
"vpc_name" = "vpc"
}
"vpc-subnet-two" = {
"subnet_az" = "eu-central-1b"
"subnet_cidr" = "192.168.4.0/24"
"subnet_name" = "subnet-two"
"vpc_cidr" = "192.168.0.0/16"
"vpc_name" = "vpc"
}
}
error message
status code: 400, request id: 64e031e5-11ea-4f6d-a03c-9a36a1ff56af
with aws_network_interface.eni["vpc-subnet-one"],
on main.tf line 20, in resource "aws_network_interface" "eni":
20: resource "aws_network_interface" "eni" {
Error: creating EC2 Network Interface: InvalidParameterValue: Address does not fall within the subnet's address range
status code: 400, request id: 7842f089-08b4-4042-b928-7830a37ffe28
with aws_network_interface.eni["vpc-subnet-two"],
on main.tf line 20, in resource "aws_network_interface" "eni":
20: resource "aws_network_interface" "eni" {
I've followed this documenation and validated everything else is correct. I still can't seem to figure out what value should be set on subnet_id
Bonus cheeky points - I am trying to configure 2 EC2's, Should i give our eni a secondary private_ips?
The error means that your IP 172.16.10.100 is invalid for your subnet CIDR range 192.168.1.0/24 and 192.168.4.0/24. Obviously this is correct because your IP should be in the correct range. For example:
private_ips = ["192.168.1.100"] # for the first subnet
private_ips = ["192.168.4.100"] # for the second subnet

Terraform using output from module

I just started with Terraform infrastructure. Trying to create a vpc module that will contain code for vpc, subnets, internet gateway, rout table. Also creating a separate tf file for rds , which will refer to the vpc module and utilize the private subnets declared in vpc module.
Created a vpc module that has vpc.tf with following
provider "aws" {
region = var.region
}
terraform {
backend "s3" {}
}
resource "aws_vpc" "production-vpc" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
tags = {
Name = "Dev-VPC"
}
}
// Private Subnets
resource "aws_subnet" "private-subnet-1" {
cidr_block = var.private_subnet_1_cidr
vpc_id = aws_vpc.production-vpc.id
availability_zone = "us-east-1a"
tags = {
Name = "Private-Subnet-1"
}
}
resource "aws_subnet" "private-subnet-2" {
cidr_block = var.private_subnet_2_cidr
vpc_id = aws_vpc.production-vpc.id
availability_zone = "us-east-1b"
tags = {
Name = "Private-Subnet-2"
}
}
The output.tf has following
output "private-subnet1-id" {
description = "Private Subnet1 Id"
value = aws_subnet.private-subnet-1.*.id
}
output "private-subnet2-id" {
description = "Private Subnet2 Id"
value = aws_subnet.private-subnet-2.*.id
}
The file is saved in folder \module\vpc folder
Created rds.tf as follows in folder \rds
provider "aws" {
region = var.region
}
terraform {
backend "s3" {}
}
module "vpc" {
source = "../module/vpc"
}
resource "aws_db_subnet_group" "subnetgrp" {
name = "dbsubnetgrp"
subnet_ids = [module.vpc.private-subnet1-id.id, module.vpc.private-subnet2-id.id]
}
When I run terraform plan , I get following error
Error: Unsupported attribute
on rds.tf line 16, in resource "aws_db_subnet_group" "subnetgrp":
16: subnet_ids = [module.vpc.private-subnet1-id.id, module.vpc.private-subnet2-id.id]
|----------------
| module.vpc.private-subnet1-id is tuple with 1 element
This value does not have any attributes.
Error: Unsupported attribute
on rds.tf line 16, in resource "aws_db_subnet_group" "subnetgrp":
16: subnet_ids = [module.vpc.private-subnet1-id.id, module.vpc.private-subnet2-id.id]
|----------------
| module.vpc.private-subnet2-id is tuple with 1 element
This value does not have any attributes.
You don't need the splat expression in the output.tf. Try the following,
output "private-subnet1-id" {
description = "Private Subnet1 Id"
value = aws_subnet.private-subnet-1.id
}
output "private-subnet2-id" {
description = "Private Subnet2 Id"
value = aws_subnet.private-subnet-2.id
}

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.

Attach multiple private subnet to route table for each terraform

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