The Cloud Computing domain's networking lesson covers what a VPC, subnet, and security group conceptually are. This one builds an actual one — a specific, complete, production-shaped network, in Terraform, with the exact resources and wiring a real application needs.
4 min read
This lesson assumes the concepts from the Cloud Computing domain's networking basics lesson — VPCs, public vs private subnets, security groups, NAT gateways — and goes straight to actually building one. A real VPC for a typical web application needs, at minimum: the VPC itself, subnets spread across multiple AZs, an internet gateway, route tables, and a NAT gateway — six distinct resources wired together correctly, not just one.
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = { Name = "main-vpc" }
}
resource "aws_subnet" "public_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
map_public_ip_on_launch = true
tags = { Name = "public-a" }
}
resource "aws_subnet" "private_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.11.0/24"
availability_zone = "us-east-1a"
tags = { Name = "private-a" }
}
# (public_b / private_b in us-east-1b, same shape, different CIDR ranges)map_public_ip_on_launch = true on the public subnet is what actually makes it "public" in practice — combined with the internet gateway and route table below, not a property of the subnet alone.
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
}
resource "aws_route_table_association" "public_a" {
subnet_id = aws_subnet.public_a.id
route_table_id = aws_route_table.public.id
}This is the part that's easy to get subtly wrong: creating an internet gateway and attaching it to the VPC does nothing by itself — a subnet is only "public" once its route table has an explicit route sending 0.0.0.0/0 traffic to that gateway, and that route table is associated with the subnet. All three pieces (gateway, route, association) have to be correct together; a subnet with map_public_ip_on_launch = true but no route to an internet gateway is a subnet whose resources have public IPs that still can't actually reach the internet — a genuinely common, confusing first mistake.
resource "aws_eip" "nat" {
domain = "vpc"
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public_a.id # NAT gateway lives in a PUBLIC subnet
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
}
resource "aws_route_table_association" "private_a" {
subnet_id = aws_subnet.private_a.id
route_table_id = aws_route_table.private.id
}The NAT gateway itself lives in a public subnet (it needs its own route to the internet gateway), while the private subnet's route table points at the NAT gateway rather than the internet gateway directly — this asymmetry (private subnets route through NAT, which routes through the IGW) is exactly what gives private-subnet resources outbound-only internet access.
VPC peering connects two VPCs (in the same or different AWS accounts) so resources in each can communicate using private IP addresses, as if they were on the same network — commonly used to connect a shared-services VPC to multiple application VPCs, or to connect environments that need to talk to each other without routing through the public internet.
resource "aws_vpc_peering_connection" "main" {
vpc_id = aws_vpc.main.id
peer_vpc_id = aws_vpc.other.id
auto_accept = true
}A peering connection alone doesn't route any traffic — like the internet gateway above, each VPC's route tables need explicit routes pointing traffic destined for the peer's CIDR range at the peering connection, on both sides, or traffic silently doesn't flow in one or both directions.
The fastest real check that a VPC is wired correctly: launch one EC2 instance in the public subnet (should get a public IP and reach the internet directly) and one in the private subnet (should have no public IP, but should still be able to reach the internet outbound via the NAT gateway — verifiable with curl to any external URL from inside the instance, and separately confirm it's genuinely unreachable from outside). A VPC that "looks right" in a diagram but hasn't been verified this way is exactly how the missing-route mistake above goes unnoticed until it matters.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. Why doesn't attaching an internet gateway to a VPC alone make any subnet 'public'?
2. Why does a NAT gateway need to live in a public subnet, even though it serves private subnets?
3. What does a VPC peering connection require, beyond just creating the connection itself, for traffic to actually flow?
4. What is the fastest way to actually verify a newly-built VPC is wired correctly?
AWS