From 51b7bd7391286c27fc0fe22838bb8728311baac6 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 92fa98d53ae3279ff29ea15cf2026e70dc0d61e5 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 9874357ceb7fb325224851c13dc6c9d9cfc011b9 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 50d21f90c85cc44f02b37c8c39f5f213746b6a9d 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." - } - } } # ==============================================