/scalable-nat-instances

Terraform module to provision a NAT Instance using an Auto Scaling Group and Spot Instance from $1/month

Primary LanguageHCLApache License 2.0Apache-2.0

terraform-aws-nat-instance CircleCI

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.

Getting Started

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.

How it works

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:

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:

  1. Attach the ENI to eth1.
  2. Set the kernel parameters for IP forwarding and masquerade.
  3. Switch the default route to eth1.

Configuration

User data

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.

SSH access

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

Migration guide

Upgrade to v2 from v1

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).

Contributions

This is an open source software. Feel free to open issues and pull requests.

Requirements

No requirements.

Providers

Name Version
aws n/a

Inputs

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
[
"t3.nano",
"t3a.nano"
]
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 [] no
private_subnets_cidr_blocks List of CIDR blocks of the private subnets. The NAT instance accepts connections from this subnets list n/a yes
public_subnet ID of the public subnet to place the NAT instance string n/a yes
tags Tags applied to resources created with this module map {} 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 [] no
user_data_write_files Additional write_files section of cloud-init list [] no
vpc_id ID of the VPC string n/a yes

Outputs

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