Lab 1 · Build a Custom VPC¶
Security Networking · Est. time: 40–50 min · Cost: ⚠️ NOT fully Free Tier — the NAT Gateway bills while it exists (see below). Tear down at the end.
Goal. Build a VPC from scratch — public and private subnets across two AZs, an internet gateway, a NAT gateway, and route tables — and see the traffic-flow rules the exam tests (Topics 3 & 10).
Money warning — read first
- A NAT Gateway is not Free Tier: it bills \$0.045/hr plus \$0.045/GB processed (us-east rates) the entire time it exists — roughly \$1+/day even idle.
- Since Feb 2024, every public IPv4 address bills \$0.005/hr (in use or not), including the Elastic IP on the NAT Gateway.
- Do the teardown the same day. Set a budget alarm first.
- Exact prices vary by Region and change — confirm on the VPC pricing page.
What you'll build¶
VPC 10.0.0.0/16
├── Public subnet 10.0.1.0/24 (AZ-a) → route 0.0.0.0/0 → Internet Gateway
├── Public subnet 10.0.2.0/24 (AZ-b) → route 0.0.0.0/0 → Internet Gateway
├── Private subnet 10.0.11.0/24 (AZ-a) → route 0.0.0.0/0 → NAT Gateway
└── Private subnet 10.0.12.0/24 (AZ-b) → route 0.0.0.0/0 → NAT Gateway
The defining difference: a public subnet routes 0.0.0.0/0 to an Internet Gateway; a
private subnet routes it to a NAT Gateway (outbound only). That routing — not the subnet's
name — is what makes it "public."
Step 1 — Create the VPC and subnets¶
- VPC console → Create VPC → choose VPC and more (this wizard builds subnets, route tables, and gateways together).
- Name
lab-vpc, IPv4 CIDR10.0.0.0/16. - Set 2 Availability Zones, 2 public and 2 private subnets.
- NAT gateways: choose In 1 AZ (cheaper than one-per-AZ for a lab).
- VPC endpoints: None for now.
- Create VPC. The wizard wires up the IGW, NAT gateway, and route tables.
(Prefer to see each piece? Use the CLI tab, which creates them one at a time.)
# VPC
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
--query Vpc.VpcId --output text)
aws ec2 create-tags --resources $VPC_ID --tags Key=Name,Value=lab-vpc
# Subnets (public a/b, private a/b) — swap AZ names for your Region
PUB_A=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 --availability-zone us-east-1a --query Subnet.SubnetId --output text)
PUB_B=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.2.0/24 --availability-zone us-east-1b --query Subnet.SubnetId --output text)
PRIV_A=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.11.0/24 --availability-zone us-east-1a --query Subnet.SubnetId --output text)
PRIV_B=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.12.0/24 --availability-zone us-east-1b --query Subnet.SubnetId --output text)
Step 2 — Internet Gateway (makes public subnets public)¶
Done by the wizard. To inspect: Internet gateways → confirm one is attached to lab-vpc,
and the public route table has a route 0.0.0.0/0 → igw-....
IGW_ID=$(aws ec2 create-internet-gateway --query InternetGateway.InternetGatewayId --output text)
aws ec2 attach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID
# Public route table → IGW, associate the public subnets
RT_PUB=$(aws ec2 create-route-table --vpc-id $VPC_ID --query RouteTable.RouteTableId --output text)
aws ec2 create-route --route-table-id $RT_PUB --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID
aws ec2 associate-route-table --route-table-id $RT_PUB --subnet-id $PUB_A
aws ec2 associate-route-table --route-table-id $RT_PUB --subnet-id $PUB_B
# Auto-assign public IPs in public subnets
aws ec2 modify-subnet-attribute --subnet-id $PUB_A --map-public-ip-on-launch
aws ec2 modify-subnet-attribute --subnet-id $PUB_B --map-public-ip-on-launch
Step 3 — NAT Gateway (private subnets get outbound-only internet)¶
This is the part that costs money
The NAT Gateway starts billing the moment it's created. Note the time; you'll delete it in teardown.
Created by the wizard in one AZ. Confirm the private route table has 0.0.0.0/0 → nat-....
# Elastic IP + NAT Gateway in a public subnet
EIP_ALLOC=$(aws ec2 allocate-address --domain vpc --query AllocationId --output text)
NAT_ID=$(aws ec2 create-nat-gateway --subnet-id $PUB_A --allocation-id $EIP_ALLOC \
--query NatGateway.NatGatewayId --output text)
aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT_ID
# Private route table → NAT, associate the private subnets
RT_PRIV=$(aws ec2 create-route-table --vpc-id $VPC_ID --query RouteTable.RouteTableId --output text)
aws ec2 create-route --route-table-id $RT_PRIV --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $NAT_ID
aws ec2 associate-route-table --route-table-id $RT_PRIV --subnet-id $PRIV_A
aws ec2 associate-route-table --route-table-id $RT_PRIV --subnet-id $PRIV_B
Step 4 — Observe the traffic-flow rules (optional, Free-Tier EC2)¶
To feel public vs private:
- Launch a
t2.micro/t3.microin a public subnet (auto-assigns a public IP). Connect via EC2 Instance Connect — it works because of the IGW route. - Launch one in a private subnet (no public IP). You can't reach it directly from the
internet, but from inside it
curl https://example.comworks — outbound via the NAT Gateway. - Note: the private instance can reach out, but nothing on the internet can start a connection to it. That's the NAT-vs-IGW distinction the exam tests.
Terminate both instances before teardown.
Teardown (do this today)¶
Order matters (delete the biller first):
- Terminate any EC2 instances you launched.
- Delete the NAT Gateway (VPC → NAT gateways) — this stops the hourly bill. Wait for it to fully delete.
- Release the Elastic IP (VPC → Elastic IPs) so it stops billing.
- Delete the VPC (VPC → Your VPCs → Delete) — this removes subnets, route tables, and the IGW with it.
aws ec2 delete-nat-gateway --nat-gateway-id $NAT_ID
aws ec2 wait nat-gateway-deleted --nat-gateway-ids $NAT_ID
aws ec2 release-address --allocation-id $EIP_ALLOC
# (detach/delete IGW, delete subnets/route tables, then the VPC — or just:)
aws ec2 delete-vpc --vpc-id $VPC_ID # after dependencies are gone
Confirm zero cost
After teardown, check there are no NAT Gateways and no unassociated Elastic IPs in the Region. That's where surprise VPC bills come from.
✅ What you should understand now¶
- A subnet is public because its route table sends
0.0.0.0/0to an Internet Gateway — not because of its name. - A NAT Gateway gives private-subnet resources outbound-only internet; the internet can't initiate connections inward. An IGW allows both directions (for resources with public IPs).
- NAT Gateways and public IPv4 addresses cost money continuously — the #1 source of surprise lab bills. Always tear them down.
- Spreading subnets across two AZs is the foundation of the resilient designs in Topic 4.
- To reach S3/DynamoDB privately without a NAT Gateway, you'd add a VPC Gateway endpoint (free) — cheaper and more secure for that traffic.
Related: Learn — Network Security · Learn — Networking Performance