In a three-tier application, a load balancer needs to accept internet traffic, while the API container, database, and cache should not be directly reachable. Put the load balancer in a public subnet and the other services in a private subnet. A public subnet is configured for direct internet connectivity; in AWS, an internet gateway must also be set up before that connectivity works. section 10.2.2 section 10.2.3
A Terraform location module can divide a supplied CIDR block into public and private subnet ranges with `cidrsubnet`. Its public `aws_subnet` enables `map_public_ip_on_launch`; an associated NAT gateway lets resources in the private subnet initiate internet access without making that subnet directly accessible from the internet. The lower-level module does not set up the internet gateway, so it is not a complete internet-connected network on its own. 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 two-segment Terraform module for the CIDR calculation, public subnet settings, and NAT gateway resource. section 10.2.3
For sensitive data, a three-segment topology adds an isolated subnet alongside the public and private ones. For availability, repeat the selected topology across locations. Network segmentation is only one protection layer: also use security groups or firewalls on services. Consider NAT gateway cost when designing private-subnet internet access. section 10.2.2 section 10.2.3

