SA Pro Lab 02 — Route between VPCs with a Transit Gateway
Scaffold: 3/5. The Transit Gateway, both VPCs and subnets, both VPC attachments, and a custom TGW route table are all built. You write the wiring that makes routing happen: associate and propagate each attachment to the route table, and add the return route in each VPC.
The scenario
You have two VPCs that need to talk to each other, and more coming. The naive answer is VPC peering: fine for one pair, but peerings do not transit, so every new VPC has to peer with every existing one. Ten VPCs is forty-five peerings and forty-five route entries per table to maintain. That is an N-squared mesh, and it does not scale.
A Transit Gateway is the hub. Each VPC attaches once, and the gateway routes between the spokes, so adding the eleventh VPC is one attachment, not ten new peerings. In this lab the gateway is created with default association and default propagation disabled, which means nothing routes automatically. That is deliberate: on a Professional paper the interesting Transit Gateway questions are about explicit routing, where you choose which attachments share a route table and what each one learns.
The requirement
VPC A (10.10.0.0/16) and VPC B (10.20.0.0/16) must be able to reach each other through the Transit Gateway. The gateway’s custom route table must learn both VPC CIDRs, and each VPC’s own route table must send the other VPC’s traffic to the gateway.
What’s provided
src/template.yaml— the Transit Gateway (with default association and propagation disabled), VPC A and VPC B each with a subnet and its own route table, a VPC attachment for each VPC, a custom TGW route table, and the stack Outputs. There is a clearly-markedTODOblock where the routing wiring goes.scripts/— deploy, test, and teardown.solution/template.yaml— the complete, correct template.
Deploying src/template.yaml as shipped succeeds, but the two VPCs cannot reach
each other: nothing is associated or propagated to the custom route table, and
neither VPC has a route pointing at the gateway.
Your task
Open src/template.yaml and fill in the TODO block with six resources:
- Two
AWS::EC2::TransitGatewayRouteTableAssociation— associate each attachment with the custom TGW route table. Association decides which route table an attachment consults when it sends traffic into the gateway. - Two
AWS::EC2::TransitGatewayRouteTablePropagation— propagate each attachment into the custom route table, so the gateway learns 10.10.0.0/16 from VPC A’s attachment and 10.20.0.0/16 from VPC B’s. - Two
AWS::EC2::Route— the return routes. In VPC A’s route table, a route to 10.20.0.0/16 withTransitGatewayId; in VPC B’s route table, a route to 10.10.0.0/16 withTransitGatewayId. Give each aDependsOnon both attachments.
If you want the mechanics first, read the walk-through post linked at the bottom.
Run it
# Defaults: stack sa-pro-lab-02, region ap-southeast-2.
./scripts/deploy.sh # deploys src/template.yaml
./scripts/test.sh # checks the TGW and VPC route tables
./scripts/teardown.sh # deletes everything
With the unedited src/template.yaml, test.sh reports the custom route table
holding no VPC routes and neither VPC routing to the other: the hub exists, but
nothing routes. Fill in the TODO, redeploy, and run the test again.
What success looks like
./scripts/test.sh prints:
ok: TGW route table has an active route for 10.10.0.0/16
ok: TGW route table has an active route for 10.20.0.0/16
ok: rtb-... routes 10.20.0.0/16 via the Transit Gateway
ok: rtb-... routes 10.10.0.0/16 via the Transit Gateway
PASS: both spokes are associated and propagated, and each VPC routes to the other via the Transit Gateway.
The test never launches an instance, so it stays cheap: it reads the Transit Gateway route table and both VPC route tables and asserts the routes are in place.
If it fails
- No active routes in the TGW route table. Propagation is missing. The gateway only learns a VPC’s CIDR when that attachment is propagated to the route table.
- The TGW route table has routes, but the VPCs still cannot reach each other. The return routes in the VPC route tables are missing. Propagation teaches the gateway; each VPC still needs its own route sending the other CIDR to the gateway.
- A route resource fails to create. Check the
DependsOn: the route to a CIDR reachable via the gateway cannot be created until the attachments exist.
Reveal the solution
Deploy the complete reference template without editing anything:
SRC=solution ./scripts/deploy.sh && ./scripts/test.sh
What you just learned
- A Transit Gateway replaces an N-squared mesh of VPC peerings with a hub. Each VPC attaches once, and adding a spoke is one attachment, not one peering per existing VPC.
- Association and propagation are two separate decisions. Association picks which route table an attachment uses for its outbound traffic; propagation installs an attachment’s routes into a route table so other attachments can reach it.
- Disabling default association and propagation forces both decisions to be explicit, which is how you build segmentation: put some attachments on one route table and others on another, and they cannot reach each other unless you propagate across.
- Teaching the gateway is only half the job. Each VPC still needs a route in its own route table pointing the other CIDR at the gateway, or the traffic never leaves the VPC.
Next
The rest of the SA Pro lab track is listed in labs/README-sa-pro.md.