This is a Terraform module which provisions a NAT instance.
Features:
- Providing NAT for private subnet(s)
- Auto healing using an auto scaling group
- Saving cost using a spot instance (from $1/month)
- Fixed source IP address by reattaching ENI
- Supporting Systems Manager Session Manager
- Compatible with workspaces
Terraform 0.12 or later is required.
Warning: Generally you should use a NAT gateway. This module provides a very low cost solution for testing purpose.
You can use this module with terraform-aws-modules/vpc/aws module as follows:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "main"
cidr = "172.18.0.0/16"
azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
private_subnets = ["172.18.64.0/20", "172.18.80.0/20", "172.18.96.0/20"]
public_subnets = ["172.18.128.0/20", "172.18.144.0/20", "172.18.160.0/20"]
enable_dns_hostnames = true
}
module "nat" {
source = "int128/nat-instance/aws"
name = "main"
vpc_id = module.vpc.vpc_id
public_subnet = module.vpc.public_subnets[0]
private_subnets_cidr_blocks = module.vpc.private_subnets_cidr_blocks
private_route_table_ids = module.vpc.private_route_table_ids
}
resource "aws_eip" "nat" {
network_interface = module.nat.eni_id
tags = {
"Name" = "nat-instance-main"
}
}
Now create an EC2 instance in the private subnet to verify the NAT configuration. Open the AWS Systems Manager Session Manager, log in to the instance and make sure you have external access from the instance.
See also the example.
This module provisions the following resources:
- Auto Scaling Group with mixed instances policy
- Launch Template
- Elastic Network Interface
- Security Group
- IAM Role for SSM and ENI attachment
- VPC Route (optional)
You need to attach your elastic IP to the ENI.
Take a look at the diagram:
By default the latest Amazon Linux 2 image is used.
You can set image_id
for a custom image.
The instance will execute runonce.sh
and snat.sh
to enable NAT as follows:
- Attach the ENI to
eth1
. - Set the kernel parameters for IP forwarding and masquerade.
- Switch the default route to
eth1
.
You can set additional write_files
and runcmd
section. For example,
module "nat" {
user_data_write_files = [
{
path : "/opt/nat/run.sh",
content : file("./run.sh"),
permissions : "0755",
},
]
user_data_runcmd = [
["/opt/nat/run.sh"],
]
}
See also cloud-init modules and the example for more.
You can enable SSH access by setting key_name
option and opening the security group. For example,
module "nat" {
key_name = "YOUR_KEY_PAIR"
}
resource "aws_security_group_rule" "nat_ssh" {
security_group_id = module.nat.sg_id
type = "ingress"
cidr_blocks = ["0.0.0.0/0"]
from_port = 22
to_port = 22
protocol = "tcp"
}
This module no longer creates an EIP since v2.
To keep your EIP when you migrate to module v2, rename the EIP in the state as follows:
% terraform state mv -dry-run module.nat.aws_eip.this aws_eip.nat
Would move "module.nat.aws_eip.this" to "aws_eip.nat"
% terraform state mv module.nat.aws_eip.this aws_eip.nat
Move "module.nat.aws_eip.this" to "aws_eip.nat"
Successfully moved 1 object(s).
This is an open source software. Feel free to open issues and pull requests.
Name | Version |
---|---|
terraform | >= 0.12.0 |
Name | Version |
---|---|
aws | n/a |
No modules.
Name | Type |
---|---|
aws_autoscaling_group.this | resource |
aws_iam_instance_profile.this | resource |
aws_iam_role.this | resource |
aws_iam_role_policy.eni | resource |
aws_iam_role_policy_attachment.ssm | resource |
aws_launch_template.this | resource |
aws_network_interface.this | resource |
aws_route.this | resource |
aws_security_group.this | resource |
aws_security_group_rule.egress | resource |
aws_security_group_rule.ingress_any | resource |
aws_ami.this | data source |
Name | Description | Type | Default | Required |
---|---|---|---|---|
enabled | Enable or not costly resources | bool |
true |
no |
image_id | AMI of the NAT instance. Default to the latest Amazon Linux 2 | string |
"" |
no |
instance_types | Candidates of spot instance type for the NAT instance. This is used in the mixed instances policy | list(string) |
[ |
no |
key_name | Name of the key pair for the NAT instance. You can set this to assign the key pair to the NAT instance | string |
"" |
no |
name | Name for all the resources as identifier | string |
n/a | yes |
private_route_table_ids | List of ID of the route tables for the private subnets. You can set this to assign the each default route to the NAT instance | list(string) |
[] |
no |
private_subnets_cidr_blocks | List of CIDR blocks of the private subnets. The NAT instance accepts connections from this subnets | list(string) |
n/a | yes |
public_subnet | ID of the public subnet to place the NAT instance | string |
n/a | yes |
ssm_policy_arn | SSM Policy to be attached to instance profile | string |
"arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" |
no |
tags | Tags applied to resources created with this module | map(string) |
{} |
no |
use_spot_instance | Whether to use spot or on-demand EC2 instance | bool |
true |
no |
user_data_runcmd | Additional runcmd section of cloud-init | list(list(string)) |
[] |
no |
user_data_write_files | Additional write_files section of cloud-init | list(any) |
[] |
no |
vpc_id | ID of the VPC | string |
n/a | yes |
Name | Description |
---|---|
eni_id | ID of the ENI for the NAT instance |
eni_private_ip | Private IP of the ENI for the NAT instance |
iam_role_name | Name of the IAM role for the NAT instance |
sg_id | ID of the security group of the NAT instance |