From 398180e6fd9dc03972c3f3670ec778713daca700 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 2 Jun 2026 14:17:48 +0200 Subject: [PATCH 1/4] chore(iac/aws/networking): remove redundant Secrets Manager VPC endpoint The interface endpoint was unconditional and redundant: every env runs fck-NAT (enable_nat_gateway = true), so in-VPC Secrets Manager consumers (main Lambda, cleanup Lambda, Fargate) will reach SM over NAT regardless. Removes: - aws_vpc_endpoint.secretsmanager (~$14/mo across 2 AZs) - aws_security_group.vpc_endpoints (the endpoint was its sole consumer) - the three matching outputs (vpc_endpoints_security_group_id, secretsmanager_endpoint_id, secretsmanager_endpoint_dns -- grep confirmed no consumers) Header comment updated to reflect actual topology (NAT present, per-service interface endpoints removed). Precondition for safety: enable_nat_gateway must remain true in every environment. If a future change turns NAT off, this endpoint (and likely the ECR ones too) needs to be reintroduced first. --- terraform/modules/networking/aws/main.tf | 74 +-------------------- terraform/modules/networking/aws/outputs.tf | 15 ----- 2 files changed, 3 insertions(+), 86 deletions(-) diff --git a/terraform/modules/networking/aws/main.tf b/terraform/modules/networking/aws/main.tf index fd46b3d27..61e89bd75 100644 --- a/terraform/modules/networking/aws/main.tf +++ b/terraform/modules/networking/aws/main.tf @@ -1,6 +1,7 @@ # AWS VPC Module with IPv6 -# Creates VPC with IPv6 support - no NAT Gateway or VPC Endpoints needed -# Cost savings: ~$54/month (NAT Gateway + VPC Endpoints eliminated) +# Creates VPC with IPv6 support. NAT (fck-nat) handles egress for ECR pulls +# and any AWS APIs that lack IPv6, so we no longer need per-service VPC +# interface endpoints. # Supports: new VPC, existing VPC, or default VPC terraform { @@ -684,74 +685,5 @@ resource "aws_iam_role_policy" "flow_logs" { }) } -# ============================================== -# VPC Endpoints (for services without IPv6 support) -# ============================================== - -# Security group for VPC endpoints -resource "aws_security_group" "vpc_endpoints" { - name_prefix = "${var.stack_name}-vpc-endpoints-" - description = "Security group for VPC endpoints" - vpc_id = local.vpc_id - - # HTTPS from VPC (IPv4) - ingress { - description = "HTTPS from VPC (IPv4)" - from_port = 443 - to_port = 443 - protocol = "tcp" - cidr_blocks = [local.vpc_cidr] - } - - # HTTPS from VPC (IPv6) - ingress { - description = "HTTPS from VPC (IPv6)" - from_port = 443 - to_port = 443 - protocol = "tcp" - ipv6_cidr_blocks = var.enable_ipv6 ? [local.vpc_ipv6_cidr] : [] - } - - # Allow all outbound (IPv4) - egress { - description = "Allow all outbound (IPv4)" - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - - # Allow all outbound (IPv6) - egress { - description = "Allow all outbound (IPv6)" - from_port = 0 - to_port = 0 - protocol = "-1" - ipv6_cidr_blocks = ["::/0"] - } - - tags = merge(var.tags, { - Name = "${var.stack_name}-vpc-endpoints-sg" - }) - - lifecycle { - create_before_destroy = true - } -} - -# Secrets Manager VPC Endpoint (required - no IPv6 support) -resource "aws_vpc_endpoint" "secretsmanager" { - vpc_id = local.vpc_id - service_name = "com.amazonaws.${data.aws_region.current.name}.secretsmanager" - vpc_endpoint_type = "Interface" - subnet_ids = local.private_subnet_ids - security_group_ids = [aws_security_group.vpc_endpoints.id] - private_dns_enabled = true - - tags = merge(var.tags, { - Name = "${var.stack_name}-secretsmanager-endpoint" - }) -} - # Data source for current region data "aws_region" "current" {} diff --git a/terraform/modules/networking/aws/outputs.tf b/terraform/modules/networking/aws/outputs.tf index 72e198a44..520ad4425 100644 --- a/terraform/modules/networking/aws/outputs.tf +++ b/terraform/modules/networking/aws/outputs.tf @@ -99,18 +99,3 @@ output "database_vpc_config" { } } -# VPC Endpoints -output "vpc_endpoints_security_group_id" { - description = "Security group ID for VPC endpoints" - value = aws_security_group.vpc_endpoints.id -} - -output "secretsmanager_endpoint_id" { - description = "Secrets Manager VPC endpoint ID" - value = aws_vpc_endpoint.secretsmanager.id -} - -output "secretsmanager_endpoint_dns" { - description = "Secrets Manager VPC endpoint DNS names" - value = aws_vpc_endpoint.secretsmanager.dns_entry -} From 03ab02947d58ff5f83549268f5f0499a6a579c1f Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 2 Jun 2026 16:02:51 +0200 Subject: [PATCH 2/4] fix(iac/networking): enforce NAT-or-IPv4-endpoint precondition (CR on #915) Add lifecycle.precondition to aws_subnet.private that fails at plan time when enable_nat_gateway is false. Without NAT, module-created private subnets have no IPv4 path to AWS services (Secrets Manager, ECR, etc.) and the module previously planned successfully in this broken configuration. Addresses CodeRabbit Major finding on PR #915. --- terraform/modules/networking/aws/main.tf | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/terraform/modules/networking/aws/main.tf b/terraform/modules/networking/aws/main.tf index 61e89bd75..d40be6f3d 100644 --- a/terraform/modules/networking/aws/main.tf +++ b/terraform/modules/networking/aws/main.tf @@ -406,6 +406,13 @@ resource "aws_subnet" "private" { Name = "${var.stack_name}-private-${data.aws_availability_zones.available.names[count.index]}" Type = "private" }) + + lifecycle { + precondition { + condition = var.enable_nat_gateway + error_message = "enable_nat_gateway must be true when creating private subnets: without it, module-created private subnets have no IPv4 path to AWS services (Secrets Manager, ECR, etc.). Enable the fck-nat instance or use an existing VPC with its own egress." + } + } } # ============================================== From 8d431959fff2e02f16b11852e548806b2a53f25b Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 2 Jun 2026 16:32:37 +0200 Subject: [PATCH 3/4] fix(iac/aws/networking): address CR + pre-commit on SM endpoint removal (#915) - Remove unused data "aws_region" "current" (the SM endpoint was its sole consumer; tflint flagged it as unused). - Restore trailing newline on outputs.tf. - Add lifecycle.precondition on aws_vpc.main that fails the plan when enable_nat_gateway is false: in-VPC workloads (Lambda, Fargate) need NAT for AWS API egress now that per-service interface endpoints are gone. Fails fast instead of relying on doc-only safety as flagged by CodeRabbit. --- terraform/modules/networking/aws/main.tf | 15 ++++++++++++--- terraform/modules/networking/aws/outputs.tf | 1 - 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/terraform/modules/networking/aws/main.tf b/terraform/modules/networking/aws/main.tf index d40be6f3d..9dd3508b1 100644 --- a/terraform/modules/networking/aws/main.tf +++ b/terraform/modules/networking/aws/main.tf @@ -83,6 +83,18 @@ resource "aws_vpc" "main" { tags = merge(var.tags, { Name = "${var.stack_name}-vpc" }) + + # In-VPC workloads (Lambda, Fargate) need egress to AWS APIs that lack + # IPv6 (Secrets Manager, ECR, etc). This module no longer provisions + # per-service interface endpoints, so NAT is the only egress path. + # Plan fails fast instead of producing a VPC where workloads can't + # reach AWS APIs. + lifecycle { + precondition { + condition = var.enable_nat_gateway + error_message = "enable_nat_gateway must be true: this module no longer provisions per-service VPC interface endpoints, so in-VPC workloads need NAT for AWS API egress. Set enable_nat_gateway = true, or reintroduce the relevant aws_vpc_endpoint resources first." + } + } } # ============================================== @@ -691,6 +703,3 @@ resource "aws_iam_role_policy" "flow_logs" { ] }) } - -# Data source for current region -data "aws_region" "current" {} diff --git a/terraform/modules/networking/aws/outputs.tf b/terraform/modules/networking/aws/outputs.tf index 520ad4425..4258a5ea5 100644 --- a/terraform/modules/networking/aws/outputs.tf +++ b/terraform/modules/networking/aws/outputs.tf @@ -98,4 +98,3 @@ output "database_vpc_config" { security_group_id = aws_security_group.database.id } } - From d234b86c1d4af3977b6d6627c9d1b210507477f3 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Tue, 2 Jun 2026 16:33:49 +0200 Subject: [PATCH 4/4] fix(iac/aws/networking): drop duplicate NAT precondition on aws_vpc.main Commit e395c035 already adds the same precondition on aws_subnet.private, which is the more accurate trigger (it fires only when private subnets are actually being created). Keeping both checked the same variable twice and emitted redundant errors. Drop the one on aws_vpc.main. --- terraform/modules/networking/aws/main.tf | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/terraform/modules/networking/aws/main.tf b/terraform/modules/networking/aws/main.tf index 9dd3508b1..0ddc78e3d 100644 --- a/terraform/modules/networking/aws/main.tf +++ b/terraform/modules/networking/aws/main.tf @@ -83,18 +83,6 @@ resource "aws_vpc" "main" { tags = merge(var.tags, { Name = "${var.stack_name}-vpc" }) - - # In-VPC workloads (Lambda, Fargate) need egress to AWS APIs that lack - # IPv6 (Secrets Manager, ECR, etc). This module no longer provisions - # per-service interface endpoints, so NAT is the only egress path. - # Plan fails fast instead of producing a VPC where workloads can't - # reach AWS APIs. - lifecycle { - precondition { - condition = var.enable_nat_gateway - error_message = "enable_nat_gateway must be true: this module no longer provisions per-service VPC interface endpoints, so in-VPC workloads need NAT for AWS API egress. Set enable_nat_gateway = true, or reintroduce the relevant aws_vpc_endpoint resources first." - } - } } # ==============================================