Network address translation (NAT) lets resources in a private network access the internet without making that private network directly reachable from the internet. A public subnet hosts the NAT gateway, while private-subnet traffic is routed through it. section 10.2.3 section 10.2.2
A typical segmented network uses NAT as part of a broader routing design:
- Place internet-facing resources such as a load balancer in the public subnet. section 10.2.2
- Keep application containers, databases, and caches in the private subnet when they should not be directly accessible from the internet. section 10.2.2
- Give the public subnet public IP addresses and create a NAT gateway there. section 10.2.3
- Route internet-bound traffic from the private subnet to the NAT gateway, allowing outbound access while blocking direct inbound access. section 10.2.3
- Use a separate isolated subnet for extremely sensitive data when it should have no direct internet access. section 10.2.2
Terraform subnet module
A Terraform module can divide an input CIDR block with cidrsubnet, create public and private aws_subnet resources, enable map_public_ip_on_launch on the public subnet, and provision an aws_nat_gateway with an elastic IP. The private subnet then relies on a route table that sends internet traffic through the gateway. section 10.2.3
Listing 10.6 Public and private subnet module
variable "cidr_block" {
description = "The CIDR block for the network."
type = string
}
variable "vpc_id" {
description = "The ID of the VPC."
type = string
}
variable "availability_zone" {
description = "The availability zone."
type = string
}
locals {
private_subnet_cidr_block = cidrsubnet(var.cidr_block, 1, 0) #1
public_subnet_cidr_block = cidrsubnet(var.cidr_block, 1, 1) #1
}
#
# Public Network
#
resource "aws_subnet" "public" {
vpc_id = var.vpc_id
cidr_block = local.public_subnet_cidr_block #2
map_public_ip_on_launch = true #3
availability_zone = var.availability_zone
tags = {
Network = "Public" #4
}
}
resource "aws_eip" "nat" {
tags = {}
}
resource "aws_nat_gateway" "main" { #5
allocation_id = aws_eip.nat.id #6
subnet_id = aws_subnet.public.id #7
tags = {}
}
#
# Private Network
#
resource "aws_subnet" "private" { #8
vpc_id = var.vpc_id
cidr_block = local.private_subnet_cidr_block #9
availability_zone = var.availability_zone #10
tags = {
Network = "Private"
}
}
resource "aws_route_table" "private" { #11
vpc_id = var.vpc_id
}
resource "aws_route_table_association" "private" { #12
subnet_id = aws_subnet.private.id
route_table_id = aws_route_table.private.id
}
resource "aws_route" "internet_access" { #13
route_table_id = aws_route_table.private.id
nat_gateway_id = aws_nat_gateway.main.id
destination_cidr_block = "0.0.0.0/0" #14
}
output "private_subnet_id" { #15
value = aws_subnet.private.id
}
output "public_subnet_id" { #16
value = aws_subnet.public.id
}
Inspect the Terraform listing showing CIDR subdivision, public and private subnet resources, public IP assignment, and NAT gateway setup. section 10.2.3

