Building a Multi-Region AWS VPC Terraform Module with TGW
A multi-region AWS network needs more than a collection of VPC resources. Each region has its own availability zones, route tables, provider configuration, service endpoints, and failure boundaries. A reusable Terraform module should hide that complexity while still exposing the controls operators need for routing, inspection, application tiers, and future expansion.
Transit Gateway (TGW) provides a central attachment point for VPCs and hybrid connections, but it does not automatically create cross-region connectivity. Inter-Region TGW peering, explicit routes, and careful CIDR planning are required. This pattern suits Australian environments that keep primary workloads in Sydney and use Melbourne for resilience, disaster recovery, or data locality.
Define The Network Contract
Start by deciding what the module owns. A practical design creates one VPC per AWS region, public and private subnets across selected Availability Zones, NAT gateways, route tables, a regional Transit Gateway, VPC attachments, and optional peering between regional TGWs. DNS support and DNS hostnames should normally be enabled at the VPC level.
Avoid hard-coding assumptions such as three Availability Zones in every region. Sydney (ap-southeast-2) and Melbourne (ap-southeast-4) offer different capacity and service characteristics over time. Let the caller provide availability_zones, subnet CIDR lists, enabled regions, and whether the deployment needs isolated subnets for databases or inspection appliances.
CIDR allocation is the foundation of the design. For example, Sydney might use 10.20.0.0/16 and Melbourne 10.21.0.0/16, with subnet ranges carved consistently by tier. The module should validate that VPC ranges do not overlap, because TGW routing cannot resolve ambiguous destinations. Include validation for subnet count, CIDR length, and region-to-CIDR map completeness.
Structure Providers And Module Inputs
Terraform provider aliases are essential when a root configuration manages more than one AWS region. The root module should configure providers such as aws.sydney and aws.melbourne, then pass the correct alias into each regional child module. This keeps region selection visible and prevents resources from accidentally landing in the default provider region.
A useful input model includes a map of region objects rather than a long list of unrelated variables. Each object can contain a region name, VPC CIDR, Availability Zones, subnet definitions, NAT strategy, and TGW settings. Use for_each where possible so adding a third region does not require copying an entire block of resources.
The module should expose outputs that are useful to other stacks: VPC IDs, private route table IDs, TGW IDs, attachment IDs, peering attachment IDs, and route table associations. Consumers can then create application-specific routes without reaching into internal resource names. Keep ownership boundaries clear; a network module should not silently create every workload route in an organisation.
For engineers building broader AWS capability, a focused AWS certification path can help connect Terraform networking concepts with IAM, routing, and regional architecture fundamentals.
Build TGW Connectivity And Routing
Create one TGW in each region, then attach the local VPC to its regional TGW. The default TGW route table can work for a small lab, but production environments benefit from explicit route table associations and propagations. This allows shared-services, production, development, and inspection networks to follow different trust boundaries.
Cross-region connectivity uses a TGW peering attachment. The peering request is created in one region and accepted in the other, with each side using the appropriate provider alias. Terraform dependencies should make the acceptance wait for the peering request, while routes must depend on the peering attachment becoming available.
| Component | Sydney Example | Melbourne Example | Design Consideration |
|---|---|---|---|
| VPC CIDR | 10.20.0.0/16 |
10.21.0.0/16 |
Must never overlap |
| Regional TGW | tgw-syd |
tgw-mel |
Separate control planes |
| Local attachment | Sydney VPC | Melbourne VPC | Associate with chosen TGW table |
| Inter-region link | TGW peering | TGW peering | Add routes on both sides |
| Private subnet route | 10.21.0.0/16 via TGW |
10.20.0.0/16 via TGW |
Explicit and symmetric |
| NAT placement | One per AZ or centralised | One per AZ or centralised | Balance cost and resilience |
A common mistake is creating the peering attachment and assuming traffic will flow. The Sydney private route tables need a route for the Melbourne CIDR pointing to the Sydney TGW, and the Melbourne private route tables need the reciprocal route. The TGW route tables also require the correct static routes or propagated routes. Return paths must be checked before testing an application connection.
Control Costs Security And Failure Modes
NAT gateways are often the largest recurring cost in a small multi-region environment. One NAT gateway per Availability Zone improves resilience and avoids cross-AZ data charges, while a single gateway per region lowers the bill for a lab. Make the strategy configurable rather than embedding a cost decision in the module.
Security groups remain stateful at the ENI level, but they do not replace network routing or inspection controls. Restrict cross-region traffic to required ports and destinations. Network ACLs should be kept understandable, especially when troubleshooting ephemeral return ports. If traffic must pass through a firewall, route tables and TGW appliance mode need to be designed together.
For Australian organisations, data residency can influence which services and backups are enabled in each region. A Canberra-based government supplier may require stricter controls than a startup serving customers around Parramatta or Perth. Document whether Melbourne is an active production region, a warm standby, or merely a recovery target; those roles produce different routing and replication requirements.
Test The Module Before Production
Use terraform validate, formatting checks, and a plan in every supported region. Automated tests should confirm that all intended VPC CIDRs are unique, every private subnet has a valid route table, TGW attachments associate with the expected TGW route table, and cross-region routes exist in both directions.
A disposable AWS account or home lab can validate module behaviour without risking a live landing zone. Test regional provider aliases, peering acceptance, targeted destroys, and changes to the Availability Zone list. Pay particular attention to Terraform state: a single state file may simplify dependency handling, while separate regional states can reduce blast radius and support independent change windows.
Operational testing should include failure scenarios. Remove or disable a route, inspect TGW route tables, verify flow logs, and test application access from each region. In Australia, teams often schedule disruptive work outside customer peaks and may refer to a quick afternoon maintenance window as an “arvo change”; record the exact UTC offset because daylight saving differs between Sydney and Melbourne.
Operational Recommendations
A dependable module should make the safe path the easy path. Use clear variable descriptions, meaningful resource tags, documented defaults, and outputs that downstream Terraform configurations can consume without parsing resource addresses.
Apply these recommendations when turning the design into a reusable module:
- Use non-overlapping CIDR ranges reserved for present and future AWS regions.
- Pass aliased AWS providers explicitly to every regional child module.
- Create explicit TGW route table associations and propagations for each trust zone.
- Add reciprocal routes for every cross-region network that must communicate.
- Make NAT gateway count, flow logs, DNS settings, and inspection paths configurable.
- Test plans, peering, routing, and destroy behaviour in a disposable account.
- Document regional roles, data residency requirements, and ownership of application routes.
Keep the module focused on shared network infrastructure, while workload stacks own security groups, service endpoints, and application-specific routes. That separation makes reviews easier, limits accidental changes, and gives operators a clear path from a two-region Sydney–Melbourne design to a broader Australian or global AWS footprint.