Enterprise-grade multi-VPC networking using AWS Transit Gateway with hub-and-spoke topology. Demonstrates transitive routing, automatic route propagation, and multi-AZ high availability.
This project implements a production-ready AWS Transit Gateway architecture connecting multiple VPCs in a hub-and-spoke model. Unlike VPC Peering (which is non-transitive), Transit Gateway enables transitive routing, allowing all connected VPCs to communicate through a central hub.
- β Hub-and-Spoke Architecture - Centralized routing through Transit Gateway
- β Transitive Routing - VPC-A can reach VPC-C through the Transit Gateway
- β Automatic Route Propagation - VPC CIDRs automatically added to TGW route table
- β Multi-AZ High Availability - Dedicated TGW subnets in multiple availability zones
- β Scalable Design - Linear scaling (O(N) vs O(NΒ²) for VPC Peering)
- β LocalStack Testing - Free local testing before AWS deployment
- β Production Best Practices - Dedicated TGW subnets, proper tagging, security groups
βββββββββββββββββββββββ βββββββββββββββββββββββ
β Lab VPC β β Dev VPC β
β 10.200.0.0/16 β β 10.201.0.0/16 β
β β β β
β ββββββββββββββββ β β ββββββββββββββββ β
β β Test Instanceβ β β β Test Instanceβ β
β β 10.200.10.x β β β β 10.201.10.x β β
β ββββββββββββββββ β β ββββββββββββββββ β
β β² β β β² β
β β β β β β
β ββββββββΌβββββββ β β ββββββββΌβββββββ β
β β TGW Subnet β β β β TGW Subnet β β
β β (AZ-A, AZ-B)β β β β (AZ-A, AZ-B)β β
β ββββββββ¬βββββββ β β ββββββββ¬βββββββ β
βββββββββββΌββββββββββββ βββββββββββΌββββββββββββ
β β
β βββββββββββββ β
ββββββββββΆβ Transit βββββββββββ
β Gateway β
β (Hub) β
βββββββββββββ
Lab VPC (10.200.0.0/16):
- Workload Subnet:
10.200.10.0/24(us-east-1a) - For test instances - TGW Subnet A:
10.200.255.0/24(us-east-1a) - Transit Gateway ENI - TGW Subnet B:
10.200.254.0/24(us-east-1b) - Transit Gateway ENI
Dev VPC (10.201.0.0/16):
- Workload Subnet:
10.201.10.0/24(us-east-1a) - For test instances - TGW Subnet A:
10.201.255.0/24(us-east-1a) - Transit Gateway ENI - TGW Subnet B:
10.201.254.0/24(us-east-1b) - Transit Gateway ENI
Transit Gateway:
- BGP ASN: 64512 (configurable)
- Default route table: Enabled (automatic propagation)
- Multi-AZ: ENIs placed in us-east-1a and us-east-1b
| Feature | VPC Peering | Transit Gateway |
|---|---|---|
| Transitive Routing | β No | β Yes |
| Scalability | O(NΒ²) connections | O(N) attachments |
| 10 VPCs | 45 connections | 10 attachments |
| Cost | Free | $0.05/hour per attachment |
| VPN Support | No | Yes |
| Best For | 2-5 VPCs | 6+ VPCs, enterprise |
Scalability Example:
- 10 VPCs with Peering: 45 connections required
- 10 VPCs with Transit Gateway: 10 attachments required
AWS Best Practice: Create separate, dedicated subnets for Transit Gateway ENIs.
Why?
- Transit Gateway places ENIs in each availability zone
- Separate subnets provide clear organization
- Allows specific NACLs for TGW traffic
- Conserves IP addresses (use /28 or /29 in production)
This project: Uses /24 subnets for simplicity (production should use /28)
When enabled (default):
- VPC CIDRs automatically added to Transit Gateway route table
- No manual route management needed
- Simplifies configuration
Example:
Transit Gateway Route Table (automatic):
βββ 10.200.0.0/16 β lab-vpc-attachment
βββ 10.201.0.0/16 β dev-vpc-attachment
Note: VPC route tables still need manual routes pointing to Transit Gateway!
VPC Peering (Non-Transitive):
VPC-A β VPC-B, VPC-B β VPC-C
VPC-A β VPC-C (cannot communicate)
Transit Gateway (Transitive):
VPC-A β TGW β VPC-C β
(works!)
All VPCs connected to the Transit Gateway can communicate with each other.
Design:
- TGW places ENI in each specified availability zone
- Traffic automatically fails over if one AZ fails
- No configuration needed - AWS handles it automatically
This Project:
- 2 TGW subnets per VPC (us-east-1a, us-east-1b)
- 4 total ENIs created by AWS (2 per VPC)
- Python 3.12+
- Pulumi CLI
- AWS credentials (for AWS deployment)
- Docker (for LocalStack testing)
# Clone repository
git clone https://github.com/YOUR_USERNAME/aws-transit-gateway-hub.git
cd aws-transit-gateway-hub
# Install dependencies
pip install -r requirements.txtTest infrastructure locally before deploying to AWS!
# Start LocalStack container
docker run -d \
--name localstack \
-p 4566:4566 \
localstack/localstack
# Verify it's running
docker ps | grep localstack# Initialize Pulumi stack for LocalStack
pulumi stack init tgw-local
# Set region
pulumi config set aws:region us-east-1
# LocalStack configuration (already in Pulumi.tgw-local.yaml)
pulumi config set aws:accessKey test
pulumi config set aws:secretKey test
pulumi config set aws:skipCredentialsValidation true# Set LocalStack endpoint
export AWS_ENDPOINT_URL=http://localhost:4566
# Deploy infrastructure
pulumi up
# Review changes and confirm# Check Transit Gateway
awslocal ec2 describe-transit-gateways
# Check VPC attachments
awslocal ec2 describe-transit-gateway-vpc-attachments
# Check route tables
awslocal ec2 describe-transit-gateway-route-tables
# Get Pulumi outputs
pulumi stack output# Destroy infrastructure
pulumi destroy
# Stop LocalStack
docker stop localstack
docker rm localstackCost: $0.00 β
Transit Gateway costs $0.05/hour per attachment. For this project:
- 2 VPC attachments = $0.10/hour
- Monthly cost: ~$73 if left running
- Test deployment (30 min): ~$0.05
Always destroy resources after testing!
# Initialize AWS stack
pulumi stack init tgw-aws
# Set region
pulumi config set aws:region us-east-1
# Optional: Set SSH key for instance access
pulumi config set key_name your-ssh-key-name
# Optional: Customize VPC CIDRs
pulumi config set lab_vpc_cidr 10.200.0.0/16
pulumi config set dev_vpc_cidr 10.201.0.0/16# Deploy (takes ~5-7 minutes)
pulumi up
# Review changes
# Confirm: yes# Get instance IPs
export LAB_PUBLIC=$(pulumi stack output lab_instance_public_ip)
export DEV_PRIVATE=$(pulumi stack output dev_instance_private_ip)
# SSH to Lab instance
ssh -i ~/.ssh/your-key.pem ec2-user@$LAB_PUBLIC
# Inside Lab instance, ping Dev instance
ping $DEV_PRIVATE
# Expected: Success! Traffic flows through Transit GatewayAWS Console:
- Navigate to VPC β Transit Gateways
- Verify status: Available
- Check Attachments tab - both VPCs should be attached
- View Route Tables β Routes should show both VPC CIDRs
CLI:
# Check Transit Gateway status
aws ec2 describe-transit-gateways \
--query 'TransitGateways[0].State'
# Check attachments
aws ec2 describe-transit-gateway-vpc-attachments \
--query 'TransitGatewayVpcAttachments[*].[VpcId,State]'
# Check TGW route table
aws ec2 describe-transit-gateway-route-tablesCRITICAL: Destroy immediately after testing to avoid costs!
# Destroy all resources
pulumi destroy
# Confirm: yes
# Verify destruction
aws ec2 describe-transit-gateways
# Should return empty or "deleted" stateTotal: ~24 AWS resources
- 1 VPC
- 3 Subnets (1 workload + 2 TGW)
- 1 Internet Gateway
- 1 Route Table
- 1 Security Group
- 1 EC2 Instance
- 1 Transit Gateway
- 2 VPC Attachments
- 4 ENIs (2 per VPC, placed by AWS)
- 2 VPC routes (pointing to TGW)
# VPC CIDRs
lab_vpc_cidr: "10.200.0.0/16" # Lab VPC CIDR
dev_vpc_cidr: "10.201.0.0/16" # Dev VPC CIDR
# Subnet CIDRs
lab_subnet_cidr: "10.200.10.0/24" # Lab workload subnet
dev_subnet_cidr: "10.201.10.0/24" # Dev workload subnet
# Transit Gateway subnets
lab_tgw_subnet_a_cidr: "10.200.255.0/24" # Lab TGW AZ-A
lab_tgw_subnet_b_cidr: "10.200.254.0/24" # Lab TGW AZ-B
dev_tgw_subnet_a_cidr: "10.201.255.0/24" # Dev TGW AZ-A
dev_tgw_subnet_b_cidr: "10.201.254.0/24" # Dev TGW AZ-B
# Instance configuration
instance_type: "t3.micro" # EC2 instance type
key_name: "your-ssh-key" # SSH key name (optional)
# Transit Gateway configuration
tgw_asn: 64512 # BGP ASN
tgw_use_default_rt: true # Use default route table# Set any configuration value
pulumi config set <key> <value>
# Example: Change instance type
pulumi config set instance_type t3.small
# Example: Set SSH key
pulumi config set key_name my-key-pairTest 1: Ping between VPCs
# From Lab instance to Dev instance
ping 10.201.10.x
# From Dev instance to Lab instance
ping 10.200.10.xTest 2: Verify TGW Status
# Check Transit Gateway state
aws ec2 describe-transit-gateways \
--filters "Name=state,Values=available"Test 3: Verify Route Propagation
# Check TGW route table
aws ec2 describe-transit-gateway-route-tables
# Routes should show both VPC CIDRs automatically propagated| Test | LocalStack | AWS |
|---|---|---|
| Infrastructure validation | β Yes | β Yes |
| Resource creation | β Yes | β Yes |
| Connectivity testing | β No | β Yes |
| Cost | Free | ~$0.05 for 30 min |
Recommendation: Validate infrastructure on LocalStack first, then briefly test connectivity on AWS.
aws_transit_gateway_hub/
βββ __main__.py # Main Pulumi program
βββ tgw.py # Transit Gateway module
βββ vpc.py # VPC creation module
βββ networking.py # Networking utilities
βββ Pulumi.yaml # Project configuration
βββ Pulumi.tgw-local.yaml # LocalStack stack configuration
βββ requirements.txt # Python dependencies
βββ README.md # This file
βββ .gitignore # Git ignore rules
β
βββ docs/ # Documentation
βββ ARCHITECTURE.md # Architecture deep dive
βββ DEPLOYMENT_GUIDE.md # Step-by-step deployment
βββ QUICK_REF.md # Quick reference
- β Hub-and-spoke network architecture
- β Transit Gateway operations and configuration
- β Transitive routing concepts
- β Multi-AZ high availability design
- β Automatic route propagation
- β AWS networking best practices
- β Infrastructure as Code with Pulumi
- β AWS Transit Gateway
- β VPC networking
- β Route tables and routing
- β Security groups
- β Elastic Network Interfaces (ENIs)
- β EC2 instances
- "Designed and deployed AWS Transit Gateway hub-and-spoke architecture connecting multiple VPCs with automatic route propagation"
- "Reduced multi-VPC connectivity complexity from O(NΒ²) to O(N) using centralized Transit Gateway routing"
- "Implemented multi-AZ high availability with dedicated Transit Gateway subnets across availability zones"
On Scalability:
"Transit Gateway solves the scalability problem of VPC Peering. For 10 VPCs, VPC Peering requires 45 connections (NΓ(N-1)/2), but Transit Gateway only needs 10 attachments. This linear scaling is critical for enterprise networks."
On Transitive Routing:
"Unlike VPC Peering which is non-transitive, Transit Gateway enables transitive routing. If VPC-A connects to the TGW and VPC-B connects to the TGW, then VPC-A can reach VPC-B through the Transit Gateway automatically."
On High Availability:
"We deploy dedicated TGW subnets in multiple availability zones. AWS automatically places ENIs in each AZ, providing automatic failover if one AZ becomes unavailable."
Check:
- Transit Gateway status is "available"
- VPC attachments status is "available"
- Routes exist in VPC route tables pointing to TGW
- Security groups allow ICMP from peer VPC CIDR
- ENIs exist in TGW subnets
Solution:
# Verify TGW status
aws ec2 describe-transit-gateways
# Verify attachments
aws ec2 describe-transit-gateway-vpc-attachments
# Check VPC route tables
aws ec2 describe-route-tablesCheck:
# Verify LocalStack is running
docker ps | grep localstack
# Check endpoint configuration
cat Pulumi.tgw-local.yaml
# Verify AWS_ENDPOINT_URL is set
echo $AWS_ENDPOINT_URLSolution:
# Destroy resources immediately
pulumi destroy --yes
# Verify destruction
aws ec2 describe-transit-gateways- ARCHITECTURE.md - Technical architecture deep dive
- DEPLOYMENT_GUIDE.md - Step-by-step deployment instructions
- QUICK_REF.md - Quick reference guide
This is a portfolio/learning project. Feel free to:
- Report issues
- Suggest improvements
- Submit pull requests
- Use as a learning resource
MIT License - See LICENSE file for details
- Tutorial based on PacketSwitch.co.uk AWS Transit Gateway Introduction
- Built with Pulumi
- Tested with LocalStack
For questions or discussions about this project:
- Check the documentation
- Review AWS Transit Gateway official docs
- See Pulumi AWS examples
Remember: Always destroy AWS resources after testing to avoid unnecessary costs!