Suppose an application has an API container, database, cache, and internet-facing load balancer. Put the load balancer in a public subnet, but keep the API, database, and cache in a private subnet: those services should not be directly reachable from the internet. section 10.2.2
A private subnet can still give its resources outbound internet access. In a two-segment network, a NAT gateway sits in the public subnet, and a route sends internet-bound traffic from the private subnet through it. This does not make the private subnet directly reachable from the internet. section 10.2.2 section 10.2.3
Define the two subnets with Terraform
Pass a CIDR block, VPC ID, and availability zone to the network module. Use `cidrsubnet(var.cidr_block, 1, 0)` for the private subnet and `cidrsubnet(var.cidr_block, 1, 1)` for the public subnet. Configure the private subnet without public IP addresses on launch; place the NAT gateway in the public subnet. 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
}
The two-segment module listing shows how one supplied CIDR block becomes separate public and private subnet ranges. section 10.2.3
Choose an isolated subnet instead when a network must have no internet access; unlike the private subnet, it needs no NAT route. For availability across locations, create a network with the chosen topology in each location. NAT gateways are straightforward to configure but can be expensive. section 10.2.2 section 10.2.3

