Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAT Instance #8

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module "label" {
}

module "example1" {
source = "github.com/obytes/terraform-aws-vpc.git?ref=v1.0.9"
source = "github.com/obytes/terraform-aws-vpc.git?ref=v1.0.10"
enabled = true
prefix = module.label.id
name = "vpc"
Expand Down
6 changes: 5 additions & 1 deletion examples/example.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ module "example1" {
additional_tags = module.label.tags
cidr_block = "172.16.0.0/18"
enable_dns_hostnames = true
enable_nat_gateway = true
enable_nat_gateway = false
nat_instance_enabled = true
enable_internet_gateway = true
create_public_subnets = true
max_subnet_count = 3
single_nat_gateway = true
private_subnets_enabled = true
ipv4_enabled = true

additional_default_route_table_tags = {
Managed = "Terraform"
Default = "Yes"
Expand Down
2 changes: 1 addition & 1 deletion nat-gw.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ locals {


resource "aws_eip" "_" {
count = local.enabled ? local.nat_gateway_eip_count : 0
count = local.enabled && var.enable_nat_gateway ? 1 : local.enabled && local.nat_instance_enabled ? 1 : 0
vpc = true
tags = merge(var.additional_tags, tomap({ "Name" = join(local.delimiter, [local.name, count.index]) }))
}
Expand Down
118 changes: 118 additions & 0 deletions nat_instance.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
locals {
private_enabled = local.enabled && var.private_subnets_enabled
private4_enabled = local.private_enabled && local.ipv4_enabled
ipv4_enabled = local.enabled && var.ipv4_enabled
nat_gateway_setting = var.nat_instance_enabled == true ? var.enable_nat_gateway == true : !(
var.enable_nat_gateway == false # not true or null
)
nat_instance_setting = local.nat_gateway_setting ? false : var.nat_instance_enabled == true # not false or null
nat_instance_useful = local.private4_enabled
nat_instance_enabled = local.nat_instance_useful && local.nat_instance_setting
need_nat_ami_id = local.nat_instance_enabled && length(var.nat_instance_ami_id) == 0
nat_instance_ami_id = local.need_nat_ami_id ? data.aws_ami.nat_instance[0].id : try(var.nat_instance_ami_id[0], "")
}

resource "aws_security_group" "nat_instance" {
count = local.nat_instance_enabled ? 1 : 0
description = "Security Group for NAT Instance"
vpc_id = join("", aws_vpc._.*.id)
tags = merge(var.additional_tags, var.additional_private_subnet_tags, tomap({ "VPC" = join("", aws_vpc._.*.id),
"Availability Zone" = length(var.azs_list_names) > 0 ? element(var.azs_list_names, count.index) : element(data.aws_availability_zones.azs.names, count.index),
"Name" = join(local.delimiter, [local.name, local.az_map_list_short[local.availability_zones[count.index]]]) }
))
}

resource "aws_security_group_rule" "nat_instance_egress" {
count = local.nat_instance_enabled ? 1 : 0

description = "Allow all egress traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = join("", aws_security_group.nat_instance.*.id)
type = "egress"
}

resource "aws_security_group_rule" "nat_instance_ingress" {
count = local.nat_instance_enabled ? 1 : 0

description = "Allow ingress traffic from the VPC CIDR block"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.cidr_block]
security_group_id = join("", aws_security_group.nat_instance.*.id)
type = "ingress"
}

# aws --region us-west-2 ec2 describe-images --owners amazon --filters Name="name",Values="amzn-ami-vpc-nat*" Name="virtualization-type",Values="hvm"
data "aws_ami" "nat_instance" {
count = local.need_nat_ami_id ? 1 : 0

most_recent = true

filter {
name = "name"
values = ["amzn-ami-vpc-nat*"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}

owners = ["amazon"]
}

# https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-comparison.html
# https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html
# https://dzone.com/articles/nat-instance-vs-nat-gateway
resource "aws_instance" "nat_instance" {
count = local.nat_instance_enabled ? 1 : 0

ami = local.nat_instance_ami_id
instance_type = var.nat_instance_type
subnet_id = aws_subnet.public[count.index].id
vpc_security_group_ids = [aws_security_group.nat_instance[0].id]

tags = merge(
var.additional_tags,
{
"Name" = join(local.delimiter, [local.name, count.index]),
"VPC" = join("", aws_vpc._.*.id)
}
)

# Required by NAT
# https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html#EIP_Disable_SrcDestCheck
source_dest_check = false
associate_public_ip_address = true

ebs_optimized = true
key_name = join("-", [local.name, "key"])
}

resource "aws_eip_association" "nat_instance" {
count = local.nat_instance_enabled ? 1 : 0

instance_id = aws_instance.nat_instance[count.index].id
allocation_id = aws_eip._[count.index].id
}

# If private IPv4 subnets and NAT Instance are both enabled, create a
# default route from private subnet to NAT Instance in each subnet

resource "aws_route" "nat_instance" {
count = local.enabled && var.nat_instance_enabled ? 1 : 0

route_table_id = element(aws_route_table.private.*.id, count.index)
network_interface_id = element(aws_instance.nat_instance.*.primary_network_interface_id, count.index)
destination_cidr_block = "0.0.0.0/0"
depends_on = [aws_route_table.private]

timeouts {
create = var.route_create_timeout
delete = var.route_delete_timeout
}
}
2 changes: 1 addition & 1 deletion private-subnets.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ resource "aws_subnet" "private" {

# There are as many route_table as local.nat_gateway_count
resource "aws_route_table" "private" {
count = local.enabled && local.private_subnet_count > 0 ? local.nat_gateway_count : 0
count = local.enabled && local.private_subnet_count > 0 ? 1 : 0
vpc_id = aws_vpc._[count.index].id

tags = merge(var.additional_tags, tomap({ "Name" = join(local.delimiter, [local.name, "prv-route", count.index]) }),
Expand Down
48 changes: 47 additions & 1 deletion variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,54 @@ variable "azs_list_names" {

variable "enable_nat_gateway" {
type = bool
description = <<-EOT
Set `true` to create NAT Gateways to perform IPv4 NAT and NAT64 as needed.
Defaults to `true` unless `nat_instance_enabled` is `true`.
EOT
default = null
}

############## NAT instance configuration ###################
variable "nat_instance_type" {
type = string
description = "NAT Instance type"
default = "t3.micro"
}

variable "nat_instance_enabled" {
type = bool
description = <<-EOT
Set `true` to create NAT Instances to perform IPv4 NAT.
Defaults to `false`.
EOT
default = null
}

variable "private_subnets_enabled" {
type = bool
description = "If false, do not create private subnets (or NAT gateways or instances)"
default = true
description = "Should be true if you want to provision NAT Gateways for each of your private networks"
}

variable "ipv4_enabled" {
type = bool
description = "Set `true` to enable IPv4 addresses in the subnets"
default = true
}

variable "nat_instance_ami_id" {
type = list(string)
description = <<-EOT
A list optionally containing the ID of the AMI to use for the NAT instance.
If the list is empty (the default), the latest official AWS NAT instance AMI
will be used. NOTE: The Official NAT instance AMI is being phased out and
does not support NAT64. Use of a NAT gateway is recommended instead.
EOT
default = []
validation {
condition = length(var.nat_instance_ami_id) < 2
error_message = "Only 1 NAT Instance AMI ID can be provided."
}
}

variable "single_nat_gateway" {
Expand Down