From 7bb5cb73aa9d2e95b42228647646e9f888a48fd0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 22 Sep 2026 00:13:16 +0000 Subject: [PATCH] fix(infra): fail prod apply without the afterhours VPC (DEV-289) Prod never creates the 10.60 fallback VPC. A terraform_data precondition fails plan and apply when existing_vpc_id is empty, instead of a check block that only warns. Co-authored-by: Adam Moussa --- terraform/locals.tf | 2 +- terraform/vpc.tf | 22 ++++++++++++++++++++-- tests/test_terraform_vpc.py | 7 ++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/terraform/locals.tf b/terraform/locals.tf index 6678bdf..a311a55 100644 --- a/terraform/locals.tf +++ b/terraform/locals.tf @@ -9,7 +9,7 @@ locals { # Created only when existing_vpc_id is empty. Prod attaches to afterhours 10.70. # 10.60 is the unused fallback CIDR, not a second prod VPC. - manage_vpc = var.existing_vpc_id == "" + manage_vpc = var.existing_vpc_id == "" && !local.is_prod vpc_cidr = "10.60.0.0/16" public_subnet_cidrs = ["10.60.0.0/24", "10.60.1.0/24"] diff --git a/terraform/vpc.tf b/terraform/vpc.tf index 8c28033..8433c4b 100644 --- a/terraform/vpc.tf +++ b/terraform/vpc.tf @@ -4,7 +4,7 @@ data "aws_availability_zones" "available" { } data "aws_vpc" "existing" { - count = local.manage_vpc ? 0 : 1 + count = var.existing_vpc_id == "" ? 0 : 1 id = var.existing_vpc_id } @@ -13,6 +13,17 @@ data "aws_subnet" "existing_public" { id = each.value } +resource "terraform_data" "prod_requires_afterhours_vpc" { + input = var.existing_vpc_id + + lifecycle { + precondition { + condition = !local.is_prod || var.existing_vpc_id != "" + error_message = "Prod must set existing_vpc_id to the afterhours VPC. Do not mint 10.60." + } + } +} + resource "aws_vpc" "this" { count = local.manage_vpc ? 1 : 0 @@ -24,6 +35,13 @@ resource "aws_vpc" "this" { Name = "${local.project}-vpc" } + lifecycle { + precondition { + condition = !local.is_prod + error_message = "Prod must set existing_vpc_id to the afterhours VPC. Do not mint 10.60." + } + } + # First apply updates the live hcptf apply role before CreateVpc. depends_on = [ aws_iam_role_policy_attachments_exclusive.hcptf_apply, @@ -80,7 +98,7 @@ resource "aws_route_table_association" "public" { } locals { - vpc_id = local.manage_vpc ? aws_vpc.this[0].id : data.aws_vpc.existing[0].id + vpc_id = local.manage_vpc ? aws_vpc.this[0].id : try(data.aws_vpc.existing[0].id, var.existing_vpc_id) public_subnet_ids = local.manage_vpc ? aws_subnet.public[*].id : var.existing_public_subnet_ids } diff --git a/tests/test_terraform_vpc.py b/tests/test_terraform_vpc.py index 97ef1cc..188a8c8 100644 --- a/tests/test_terraform_vpc.py +++ b/tests/test_terraform_vpc.py @@ -21,7 +21,9 @@ def test_meals_owns_a_vpc_instead_of_looking_up_default(): assert "count = local.manage_vpc ? 1 : 0" in vpc assert 'variable "existing_vpc_id"' in variables assert 'variable "existing_public_subnet_ids"' in variables - assert 'manage_vpc = var.existing_vpc_id == ""' in locals_tf + assert ( + 'manage_vpc = var.existing_vpc_id == "" && !local.is_prod' in locals_tf + ) assert 'data "aws_vpc" "default"' not in ecs assert "data.aws_vpc.default" not in ecs assert "data.aws_subnets.default" not in ecs @@ -38,3 +40,6 @@ def test_meals_owns_a_vpc_instead_of_looking_up_default(): assert "value = local.vpc_id" in outputs assert 'output "public_subnet_ids"' in outputs assert 'check "prod_reuses_afterhours_vpc"' in data + assert "prod_requires_afterhours_vpc" in vpc + assert "Do not mint 10.60." in vpc + assert 'count = var.existing_vpc_id == "" ? 0 : 1' in vpc