Skip to content

Commit 3681299

Browse files
committed
Fix NAT routing when max_nats limits NATs to fewer AZs
**Problem:** When max_nats < number of AZs, NAT Gateways are only created in the first max_nats AZs, but the route table mapping formulas assumed NATs exist in all AZs, causing "Invalid index" errors. **Example scenario that failed:** - 2 AZs, max_nats=1 → Only 1 NAT created (in AZ0) - 2 private route tables (1 per AZ) - Mapping produced [0, 1] but only NAT[0] exists - Error: aws_nat_gateway.default[1] - invalid index **Root cause:** The private_route_table_to_nat_map and public_route_table_to_nat_map formulas calculated: az_index * nats_per_az + subnet_within_az This works when NATs exist in all AZs, but fails when max_nats limits NATs to fewer AZs. The formula could generate indices >= nat_count. **Fix:** Added modulo operation to clamp results to available NAT indices: (floor(i / subnets_per_az_count) * nats_per_az + (i % subnets_per_az_count) % nats_per_az) % nat_count Now route tables in AZs without NATs will wrap around to use NATs from other AZs (typically AZ0). **Why wasn't this caught by tests?** None of the module's examples use the max_nats feature. All examples either omit max_nats (defaulting to unlimited) or use max_nats >= number of AZs, so the bug was never triggered. **Testing:** This bug was discovered by the aws-vpc component test suite when using: - 2 AZs with max_nats: 1 - public_subnets_enabled: true - NAT Gateway enabled Fixes: #226
1 parent fec1189 commit 3681299

File tree

1 file changed

+16
-8
lines changed

1 file changed

+16
-8
lines changed

main.tf

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ locals {
240240
nats_per_az = local.nat_count > 0 ? length(local.nat_gateway_resolved_indices) : 0
241241

242242
# For each private route table, calculate which NAT device it should route to
243-
# This ensures each private subnet routes to a NAT in its own AZ
243+
# This ensures each private subnet routes to a NAT in its own AZ when possible
244244
# Used by both NAT Gateways and NAT Instances
245245
#
246246
# Example 1: 3 AZs, 3 private subnets per AZ, 1 NAT per AZ
@@ -255,22 +255,30 @@ locals {
255255
# Route table 4 (AZ1, app1) → NAT 3
256256
# Route tables 6,8 (AZ2, database & app2) → NAT 4
257257
# Route table 7 (AZ2, app1) → NAT 5
258+
#
259+
# Example 3: 2 AZs, 1 private subnet per AZ, max_nats=1 (only 1 NAT total in AZ0)
260+
# Route table 0 (AZ0) → NAT 0
261+
# Route table 1 (AZ1) → NAT 0 (wraps to AZ0's NAT because AZ1 has no NAT)
258262
private_route_table_to_nat_map = local.nat_enabled && local.private4_enabled ? [
259263
for i in range(local.private_route_table_count) :
260264
# Calculate AZ index for this route table
261-
floor(i / local.private_subnets_per_az_count) * local.nats_per_az +
262-
# Distribute private subnets within the AZ across available NATs
263-
(i % local.private_subnets_per_az_count) % local.nats_per_az
265+
(floor(i / local.private_subnets_per_az_count) * local.nats_per_az +
266+
# Distribute private subnets within the AZ across available NATs
267+
(i % local.private_subnets_per_az_count) % local.nats_per_az
268+
# Clamp to available NAT indices in case max_nats limits NATs to fewer AZs
269+
) % local.nat_count
264270
] : []
265271

266272
# For each public route table, calculate which NAT gateway it should route to (for NAT64)
267-
# This ensures each public subnet routes to a NAT in its own AZ
273+
# This ensures each public subnet routes to a NAT in its own AZ when possible
268274
public_route_table_to_nat_map = local.nat_gateway_enabled && local.public_dns64_enabled ? [
269275
for i in range(local.public_route_table_count) :
270276
# Calculate AZ index for this route table
271-
floor(i / local.public_subnets_per_az_count) * local.nats_per_az +
272-
# Distribute public subnets within the AZ across available NATs
273-
(i % local.public_subnets_per_az_count) % local.nats_per_az
277+
(floor(i / local.public_subnets_per_az_count) * local.nats_per_az +
278+
# Distribute public subnets within the AZ across available NATs
279+
(i % local.public_subnets_per_az_count) % local.nats_per_az
280+
# Clamp to available NAT indices in case max_nats limits NATs to fewer AZs
281+
) % local.nat_count
274282
] : []
275283

276284
# It does not make sense to create both a NAT Gateway and a NAT instance, since they perform the same function

0 commit comments

Comments
 (0)