|
| 1 | +# terraform-aws-transit-gateway |
| 2 | + |
| 3 | +Enterprise-grade AWS Transit Gateway Terraform module with VPC attachments, custom route tables, RAM sharing, and inter-region peering support. |
| 4 | + |
| 5 | +## Architecture |
| 6 | + |
| 7 | +``` |
| 8 | + AWS Cloud |
| 9 | + +----------------------------------------------------------------+ |
| 10 | + | | |
| 11 | + | Account A (Hub) Account B (Spoke) | |
| 12 | + | +----------------------------+ +-------------------------+ | |
| 13 | + | | | | | | |
| 14 | + | | +--------+ +--------+ | | +--------+ | | |
| 15 | + | | | VPC A | | VPC B | | | | VPC C | | | |
| 16 | + | | |10.1/16 | |10.2/16 | | | |10.3/16 | | | |
| 17 | + | | +---+----+ +---+----+ | | +---+----+ | | |
| 18 | + | | | | | | | | | |
| 19 | + | | +---+-----------+----+ | | +---+----+ | | |
| 20 | + | | | Transit Gateway |<--RAM--->| TGW | | | |
| 21 | + | | | | | | | Attach | | | |
| 22 | + | | | +------+ +------+ | | | +--------+ | | |
| 23 | + | | | |Prod | |Dev | | | | | | |
| 24 | + | | | |RT | |RT | | | | | | |
| 25 | + | | | +------+ +------+ | | | | | |
| 26 | + | | +--------------------+ | | | | |
| 27 | + | +----------------------------+ +-------------------------+ | |
| 28 | + | | |
| 29 | + | Region us-east-1 Region eu-west-1 | |
| 30 | + | +------------------------+ +------------------------+ | |
| 31 | + | | Transit Gateway A |<--->| Transit Gateway B | | |
| 32 | + | | (Inter-Region Peering) | | (Inter-Region Peering) | | |
| 33 | + | +------------------------+ +------------------------+ | |
| 34 | + +----------------------------------------------------------------+ |
| 35 | +``` |
| 36 | + |
| 37 | +## Features |
| 38 | + |
| 39 | +- **Transit Gateway**: Fully configurable with ASN, DNS, ECMP, and multicast support |
| 40 | +- **VPC Attachments**: Dynamic VPC attachments with per-attachment configuration |
| 41 | +- **Custom Route Tables**: Network segmentation with custom route tables, associations, and propagations |
| 42 | +- **Static Routes**: Support for both forwarding routes and blackhole routes |
| 43 | +- **RAM Sharing**: Cross-account Transit Gateway sharing via AWS Resource Access Manager |
| 44 | +- **Sub-Modules**: Reusable spoke attachment and inter-region peering modules |
| 45 | +- **Production Ready**: Input validation, comprehensive tagging, and full documentation |
| 46 | + |
| 47 | +## Usage |
| 48 | + |
| 49 | +### Basic - Two VPC Connectivity |
| 50 | + |
| 51 | +```hcl |
| 52 | +module "transit_gateway" { |
| 53 | + source = "kogunlowo123/transit-gateway/aws" |
| 54 | +
|
| 55 | + name = "my-tgw" |
| 56 | + description = "Transit Gateway for VPC connectivity" |
| 57 | +
|
| 58 | + vpc_attachments = { |
| 59 | + vpc_a = { |
| 60 | + vpc_id = "vpc-aaa" |
| 61 | + subnet_ids = ["subnet-aaa1", "subnet-aaa2"] |
| 62 | + } |
| 63 | + vpc_b = { |
| 64 | + vpc_id = "vpc-bbb" |
| 65 | + subnet_ids = ["subnet-bbb1", "subnet-bbb2"] |
| 66 | + } |
| 67 | + } |
| 68 | +
|
| 69 | + tags = { |
| 70 | + Environment = "production" |
| 71 | + } |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +### Advanced - Network Segmentation with Custom Route Tables |
| 76 | + |
| 77 | +```hcl |
| 78 | +module "transit_gateway" { |
| 79 | + source = "kogunlowo123/transit-gateway/aws" |
| 80 | +
|
| 81 | + name = "segmented-tgw" |
| 82 | +
|
| 83 | + enable_default_route_table_association = false |
| 84 | + enable_default_route_table_propagation = false |
| 85 | +
|
| 86 | + vpc_attachments = { |
| 87 | + production = { |
| 88 | + vpc_id = "vpc-prod" |
| 89 | + subnet_ids = ["subnet-prod-1a", "subnet-prod-1b"] |
| 90 | + transit_gateway_default_route_table_association = false |
| 91 | + transit_gateway_default_route_table_propagation = false |
| 92 | + } |
| 93 | + development = { |
| 94 | + vpc_id = "vpc-dev" |
| 95 | + subnet_ids = ["subnet-dev-1a", "subnet-dev-1b"] |
| 96 | + transit_gateway_default_route_table_association = false |
| 97 | + transit_gateway_default_route_table_propagation = false |
| 98 | + } |
| 99 | + } |
| 100 | +
|
| 101 | + route_tables = { |
| 102 | + production = { name = "production-rt" } |
| 103 | + development = { name = "development-rt" } |
| 104 | + } |
| 105 | +
|
| 106 | + route_table_associations = { |
| 107 | + prod_assoc = { route_table_key = "production", attachment_key = "production" } |
| 108 | + dev_assoc = { route_table_key = "development", attachment_key = "development" } |
| 109 | + } |
| 110 | +
|
| 111 | + route_table_propagations = { |
| 112 | + prod_to_dev = { route_table_key = "development", attachment_key = "production" } |
| 113 | + dev_to_prod = { route_table_key = "production", attachment_key = "development" } |
| 114 | + } |
| 115 | +
|
| 116 | + # Blackhole route to drop traffic to unused address space |
| 117 | + routes = [ |
| 118 | + { |
| 119 | + destination_cidr = "172.16.0.0/12" |
| 120 | + route_table_key = "production" |
| 121 | + blackhole = true |
| 122 | + } |
| 123 | + ] |
| 124 | +
|
| 125 | + tags = { |
| 126 | + Environment = "multi-env" |
| 127 | + } |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +### Cross-Account Sharing with RAM |
| 132 | + |
| 133 | +```hcl |
| 134 | +module "transit_gateway" { |
| 135 | + source = "kogunlowo123/transit-gateway/aws" |
| 136 | +
|
| 137 | + name = "shared-tgw" |
| 138 | +
|
| 139 | + enable_auto_accept_shared_attachments = true |
| 140 | +
|
| 141 | + vpc_attachments = { |
| 142 | + hub = { |
| 143 | + vpc_id = "vpc-hub" |
| 144 | + subnet_ids = ["subnet-hub-1a", "subnet-hub-1b"] |
| 145 | + } |
| 146 | + } |
| 147 | +
|
| 148 | + ram_principals = [ |
| 149 | + "123456789012", # Spoke account |
| 150 | + "arn:aws:organizations::111111111111:organization/o-abc123", # Entire org |
| 151 | + ] |
| 152 | +
|
| 153 | + tags = { |
| 154 | + Environment = "hub" |
| 155 | + } |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +## Sub-Modules |
| 160 | + |
| 161 | +### spoke-attachment |
| 162 | + |
| 163 | +Reusable module for attaching spoke VPCs with optional route table association, propagation, and VPC route creation. |
| 164 | + |
| 165 | +```hcl |
| 166 | +module "spoke" { |
| 167 | + source = "kogunlowo123/transit-gateway/aws//modules/spoke-attachment" |
| 168 | +
|
| 169 | + name = "spoke-vpc" |
| 170 | + transit_gateway_id = module.transit_gateway.transit_gateway_id |
| 171 | + vpc_id = "vpc-spoke" |
| 172 | + subnet_ids = ["subnet-spoke-1a", "subnet-spoke-1b"] |
| 173 | +
|
| 174 | + route_table_id = module.transit_gateway.route_table_ids["production"] |
| 175 | + propagation_route_table_ids = [module.transit_gateway.route_table_ids["shared"]] |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +### inter-region-peering |
| 180 | + |
| 181 | +Creates Transit Gateway peering between two regions with automatic acceptance, route table associations, and static routes. |
| 182 | + |
| 183 | +```hcl |
| 184 | +module "peering" { |
| 185 | + source = "kogunlowo123/transit-gateway/aws//modules/inter-region-peering" |
| 186 | +
|
| 187 | + providers = { |
| 188 | + aws = aws |
| 189 | + aws.peer = aws.eu_west |
| 190 | + } |
| 191 | +
|
| 192 | + name = "us-to-eu" |
| 193 | + transit_gateway_id = module.tgw_us.transit_gateway_id |
| 194 | + peer_transit_gateway_id = module.tgw_eu.transit_gateway_id |
| 195 | +
|
| 196 | + requester_route_table_id = module.tgw_us.route_table_ids["main"] |
| 197 | + accepter_route_table_id = module.tgw_eu.route_table_ids["main"] |
| 198 | +
|
| 199 | + requester_routes = ["10.100.0.0/16"] |
| 200 | + accepter_routes = ["10.0.0.0/16"] |
| 201 | +} |
| 202 | +``` |
| 203 | + |
| 204 | +## Requirements |
| 205 | + |
| 206 | +| Name | Version | |
| 207 | +|------|---------| |
| 208 | +| [terraform](#requirement\_terraform) | >= 1.5.0 | |
| 209 | +| [aws](#requirement\_aws) | >= 5.20.0 | |
| 210 | + |
| 211 | +## Inputs |
| 212 | + |
| 213 | +| Name | Description | Type | Default | Required | |
| 214 | +|------|-------------|------|---------|----------| |
| 215 | +| `name` | Name for the Transit Gateway and associated resources | `string` | n/a | yes | |
| 216 | +| `description` | Description of the Transit Gateway | `string` | `""` | no | |
| 217 | +| `amazon_side_asn` | Private ASN for the Amazon side of BGP session | `number` | `64512` | no | |
| 218 | +| `enable_auto_accept_shared_attachments` | Auto-accept cross-account attachment requests | `bool` | `false` | no | |
| 219 | +| `enable_default_route_table_association` | Auto-associate attachments with default route table | `bool` | `true` | no | |
| 220 | +| `enable_default_route_table_propagation` | Auto-propagate routes to default route table | `bool` | `true` | no | |
| 221 | +| `enable_dns_support` | Enable DNS resolution across attached VPCs | `bool` | `true` | no | |
| 222 | +| `enable_vpn_ecmp_support` | Enable ECMP for VPN connections | `bool` | `true` | no | |
| 223 | +| `enable_multicast_support` | Enable multicast on the Transit Gateway | `bool` | `false` | no | |
| 224 | +| `vpc_attachments` | Map of VPC attachment configurations | `map(object({...}))` | `{}` | no | |
| 225 | +| `route_tables` | Map of custom route table configurations | `map(object({name=string}))` | `{}` | no | |
| 226 | +| `routes` | List of static routes (forwarding and blackhole) | `list(object({...}))` | `[]` | no | |
| 227 | +| `route_table_associations` | Map of route table associations | `map(object({...}))` | `{}` | no | |
| 228 | +| `route_table_propagations` | Map of route table propagations | `map(object({...}))` | `{}` | no | |
| 229 | +| `ram_principals` | AWS account IDs or organization ARNs for RAM sharing | `list(string)` | `[]` | no | |
| 230 | +| `tags` | Tags to apply to all resources | `map(string)` | `{}` | no | |
| 231 | + |
| 232 | +## Outputs |
| 233 | + |
| 234 | +| Name | Description | |
| 235 | +|------|-------------| |
| 236 | +| `transit_gateway_id` | The ID of the Transit Gateway | |
| 237 | +| `transit_gateway_arn` | The ARN of the Transit Gateway | |
| 238 | +| `transit_gateway_owner_id` | The AWS account ID of the Transit Gateway owner | |
| 239 | +| `transit_gateway_association_default_route_table_id` | The default association route table ID | |
| 240 | +| `transit_gateway_propagation_default_route_table_id` | The default propagation route table ID | |
| 241 | +| `vpc_attachment_ids` | Map of VPC attachment IDs keyed by attachment name | |
| 242 | +| `vpc_attachment_details` | Map of VPC attachment details (ID, VPC ID, subnet IDs) | |
| 243 | +| `route_table_ids` | Map of custom route table IDs | |
| 244 | +| `ram_resource_share_id` | RAM resource share ID (null if not enabled) | |
| 245 | +| `ram_resource_share_arn` | RAM resource share ARN (null if not enabled) | |
| 246 | +| `aws_region` | AWS region of the Transit Gateway | |
| 247 | +| `aws_account_id` | AWS account ID owning the Transit Gateway | |
| 248 | + |
| 249 | +## Security Considerations |
| 250 | + |
| 251 | +- **Network Segmentation**: Use custom route tables to isolate environments. Never rely solely on default route tables in production. |
| 252 | +- **Blackhole Routes**: Use blackhole routes to explicitly drop traffic to unused or restricted CIDR ranges. |
| 253 | +- **Appliance Mode**: Enable appliance mode for VPC attachments hosting stateful network appliances (firewalls, IDS/IPS) to ensure symmetric routing. |
| 254 | +- **RAM Sharing**: When sharing via RAM, use organization-level sharing with SCPs for governance. Avoid sharing with individual accounts when possible. |
| 255 | +- **Auto-Accept**: Only enable `auto_accept_shared_attachments` when you have strong governance (SCPs) in place. Otherwise, manually approve each attachment. |
| 256 | +- **DNS Support**: Be aware that enabling DNS support allows cross-VPC DNS resolution, which may expose internal service names. |
| 257 | +- **Least Privilege**: Use route table associations and propagations to implement least-privilege network access between VPCs. |
| 258 | +- **Monitoring**: Enable VPC Flow Logs on all attached VPCs and Transit Gateway Flow Logs for network visibility. |
| 259 | + |
| 260 | +## Cost Estimation |
| 261 | + |
| 262 | +| Component | Unit | Price (us-east-1) | |
| 263 | +|-----------|------|-------------------| |
| 264 | +| Transit Gateway | Per hour | $0.05 | |
| 265 | +| VPC Attachment | Per hour per attachment | $0.05 | |
| 266 | +| Data Processing | Per GB | $0.02 | |
| 267 | +| Peering Attachment | Per hour per attachment | $0.05 | |
| 268 | +| Cross-region data | Per GB | Standard data transfer rates | |
| 269 | + |
| 270 | +**Example**: A Transit Gateway with 5 VPC attachments processing 1 TB/month: |
| 271 | + |
| 272 | +- TGW: $0.05 x 730 hours = $36.50 |
| 273 | +- Attachments: 5 x $0.05 x 730 = $182.50 |
| 274 | +- Data: 1,000 GB x $0.02 = $20.00 |
| 275 | +- **Total: ~$239/month** |
| 276 | + |
| 277 | +## References |
| 278 | + |
| 279 | +- [AWS Transit Gateway Documentation](https://docs.aws.amazon.com/vpc/latest/tgw/what-is-transit-gateway.html) |
| 280 | +- [Transit Gateway Design Best Practices](https://docs.aws.amazon.com/vpc/latest/tgw/tgw-best-design-practices.html) |
| 281 | +- [Transit Gateway Network Manager](https://docs.aws.amazon.com/vpc/latest/tgw/what-is-network-manager.html) |
| 282 | +- [AWS RAM User Guide](https://docs.aws.amazon.com/ram/latest/userguide/what-is.html) |
| 283 | +- [Terraform AWS Provider - Transit Gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway) |
| 284 | +- [AWS Transit Gateway Pricing](https://aws.amazon.com/transit-gateway/pricing/) |
| 285 | +- [Centralized Inspection Architecture](https://docs.aws.amazon.com/prescriptive-guidance/latest/inline-traffic-inspection-third-party-appliances/architecture.html) |
| 286 | + |
| 287 | +## Examples |
| 288 | + |
| 289 | +- [Basic](examples/basic/) - Simple 2-VPC Transit Gateway |
| 290 | +- [Advanced](examples/advanced/) - Multi-VPC with custom route tables and network segmentation |
| 291 | +- [Complete](examples/complete/) - Full enterprise with RAM sharing, inspection VPC, and blackhole routes |
| 292 | + |
| 293 | +## License |
| 294 | + |
| 295 | +MIT Licensed. See [LICENSE](LICENSE) for full details. |
0 commit comments