Skip to content

Commit c1bb063

Browse files
committed
NAT Instance
- Creating an optional nat_instance controlled by `nat_instance_enabled`
1 parent 58e5a7d commit c1bb063

File tree

6 files changed

+171
-4
lines changed

6 files changed

+171
-4
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ module "label" {
2121
}
2222
2323
module "example1" {
24-
source = "github.com/obytes/terraform-aws-vpc.git?ref=v1.0.9"
24+
source = "github.com/obytes/terraform-aws-vpc.git?ref=v1.0.10"
2525
enabled = true
2626
prefix = module.label.id
2727
name = "vpc"

examples/example.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ module "example1" {
1818
cidr_block = "172.16.0.0/18"
1919
enable_dns_hostnames = true
2020
enable_nat_gateway = true
21+
nat_instance_enabled = false
2122
enable_internet_gateway = true
2223
create_public_subnets = true
2324
max_subnet_count = 3
2425
single_nat_gateway = true
26+
private_subnets_enabled = true
27+
ipv4_enabled = true
28+
2529
additional_default_route_table_tags = {
2630
Managed = "Terraform"
2731
Default = "Yes"

nat-gw.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ locals {
77

88

99
resource "aws_eip" "_" {
10-
count = local.enabled ? local.nat_gateway_eip_count : 0
10+
count = local.enabled && var.enable_nat_gateway ? 1 : local.enabled && local.nat_instance_enabled ? 1 : 0
1111
vpc = true
1212
tags = merge(var.additional_tags, tomap({ "Name" = join(local.delimiter, [local.name, count.index]) }))
1313
}

nat_instance.tf

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
locals {
2+
private_enabled = local.enabled && var.private_subnets_enabled
3+
private4_enabled = local.private_enabled && local.ipv4_enabled
4+
ipv4_enabled = local.enabled && var.ipv4_enabled
5+
nat_gateway_setting = var.nat_instance_enabled == true ? var.enable_nat_gateway == true : !(
6+
var.enable_nat_gateway == false # not true or null
7+
)
8+
nat_instance_setting = local.nat_gateway_setting ? false : var.nat_instance_enabled == true # not false or null
9+
nat_instance_useful = local.private4_enabled
10+
nat_instance_enabled = local.nat_instance_useful && local.nat_instance_setting
11+
need_nat_ami_id = local.nat_instance_enabled && length(var.nat_instance_ami_id) == 0
12+
nat_instance_ami_id = local.need_nat_ami_id ? data.aws_ami.nat_instance[0].id : try(var.nat_instance_ami_id[0], "")
13+
}
14+
15+
resource "aws_security_group" "nat_instance" {
16+
count = local.nat_instance_enabled ? 1 : 0
17+
description = "Security Group for NAT Instance"
18+
vpc_id = join("", aws_vpc._.*.id)
19+
tags = merge(var.additional_tags, var.additional_private_subnet_tags, tomap({ "VPC" = join("", aws_vpc._.*.id),
20+
"Availability Zone" = length(var.azs_list_names) > 0 ? element(var.azs_list_names, count.index) : element(data.aws_availability_zones.azs.names, count.index),
21+
"Name" = join(local.delimiter, [local.name, local.az_map_list_short[local.availability_zones[count.index]]]) }
22+
))
23+
}
24+
25+
resource "aws_security_group_rule" "nat_instance_egress" {
26+
count = local.nat_instance_enabled ? 1 : 0
27+
28+
description = "Allow all egress traffic"
29+
from_port = 0
30+
to_port = 0
31+
protocol = "-1"
32+
cidr_blocks = ["0.0.0.0/0"]
33+
security_group_id = join("", aws_security_group.nat_instance.*.id)
34+
type = "egress"
35+
}
36+
37+
resource "aws_security_group_rule" "nat_instance_ingress" {
38+
count = local.nat_instance_enabled ? 1 : 0
39+
40+
description = "Allow ingress traffic from the VPC CIDR block"
41+
from_port = 0
42+
to_port = 0
43+
protocol = "-1"
44+
cidr_blocks = [var.cidr_block]
45+
security_group_id = join("", aws_security_group.nat_instance.*.id)
46+
type = "ingress"
47+
}
48+
49+
# aws --region us-west-2 ec2 describe-images --owners amazon --filters Name="name",Values="amzn-ami-vpc-nat*" Name="virtualization-type",Values="hvm"
50+
data "aws_ami" "nat_instance" {
51+
count = local.need_nat_ami_id ? 1 : 0
52+
53+
most_recent = true
54+
55+
filter {
56+
name = "name"
57+
values = ["amzn-ami-vpc-nat*"]
58+
}
59+
60+
filter {
61+
name = "virtualization-type"
62+
values = ["hvm"]
63+
}
64+
65+
owners = ["amazon"]
66+
}
67+
68+
# https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-comparison.html
69+
# https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html
70+
# https://dzone.com/articles/nat-instance-vs-nat-gateway
71+
resource "aws_instance" "nat_instance" {
72+
count = local.nat_instance_enabled ? 1 : 0
73+
74+
ami = local.nat_instance_ami_id
75+
instance_type = var.nat_instance_type
76+
subnet_id = aws_subnet.public[count.index].id
77+
vpc_security_group_ids = [aws_security_group.nat_instance[0].id]
78+
79+
tags = merge(
80+
var.additional_tags,
81+
{
82+
"Name" = join(local.delimiter, [local.name, count.index]),
83+
"VPC" = join("", aws_vpc._.*.id)
84+
}
85+
)
86+
87+
# Required by NAT
88+
# https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html#EIP_Disable_SrcDestCheck
89+
source_dest_check = false
90+
associate_public_ip_address = true
91+
92+
ebs_optimized = true
93+
}
94+
95+
resource "aws_eip_association" "nat_instance" {
96+
count = local.nat_instance_enabled ? 1 : 0
97+
98+
instance_id = aws_instance.nat_instance[count.index].id
99+
allocation_id = aws_eip._[count.index].id
100+
}
101+
102+
# If private IPv4 subnets and NAT Instance are both enabled, create a
103+
# default route from private subnet to NAT Instance in each subnet
104+
105+
resource "aws_route" "nat_instance" {
106+
count = local.enabled && var.nat_instance_enabled ? 1 : 0
107+
108+
route_table_id = element(aws_route_table.private.*.id, count.index)
109+
network_interface_id = element(aws_instance.nat_instance.*.primary_network_interface_id, count.index)
110+
destination_cidr_block = "0.0.0.0/0"
111+
depends_on = [aws_route_table.private]
112+
113+
timeouts {
114+
create = var.route_create_timeout
115+
delete = var.route_delete_timeout
116+
}
117+
}

private-subnets.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ resource "aws_subnet" "private" {
1818

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

2424
tags = merge(var.additional_tags, tomap({ "Name" = join(local.delimiter, [local.name, "prv-route", count.index]) }),

variables.tf

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,54 @@ variable "azs_list_names" {
5959

6060
variable "enable_nat_gateway" {
6161
type = bool
62+
description = <<-EOT
63+
Set `true` to create NAT Gateways to perform IPv4 NAT and NAT64 as needed.
64+
Defaults to `true` unless `nat_instance_enabled` is `true`.
65+
EOT
66+
default = null
67+
}
68+
69+
############## NAT instance configuration ###################
70+
variable "nat_instance_type" {
71+
type = string
72+
description = "NAT Instance type"
73+
default = "t3.micro"
74+
}
75+
76+
variable "nat_instance_enabled" {
77+
type = bool
78+
description = <<-EOT
79+
Set `true` to create NAT Instances to perform IPv4 NAT.
80+
Defaults to `false`.
81+
EOT
82+
default = null
83+
}
84+
85+
variable "private_subnets_enabled" {
86+
type = bool
87+
description = "If false, do not create private subnets (or NAT gateways or instances)"
6288
default = true
63-
description = "Should be true if you want to provision NAT Gateways for each of your private networks"
89+
}
90+
91+
variable "ipv4_enabled" {
92+
type = bool
93+
description = "Set `true` to enable IPv4 addresses in the subnets"
94+
default = true
95+
}
96+
97+
variable "nat_instance_ami_id" {
98+
type = list(string)
99+
description = <<-EOT
100+
A list optionally containing the ID of the AMI to use for the NAT instance.
101+
If the list is empty (the default), the latest official AWS NAT instance AMI
102+
will be used. NOTE: The Official NAT instance AMI is being phased out and
103+
does not support NAT64. Use of a NAT gateway is recommended instead.
104+
EOT
105+
default = []
106+
validation {
107+
condition = length(var.nat_instance_ami_id) < 2
108+
error_message = "Only 1 NAT Instance AMI ID can be provided."
109+
}
64110
}
65111

66112
variable "single_nat_gateway" {

0 commit comments

Comments
 (0)