fix(infra): share the afterhours VPC in prod (PLAT-215) (#203)
Some checks are pending
Deploy API / Resolve target (push) Waiting to run
Deploy API / Deploy API to (push) Blocked by required conditions

* fix(infra): share the afterhours VPC when existing_vpc_id is set

Prod is at the account VPC quota, so the v1.0.0 apply destroyed Lambda/API Gateway then failed on CreateVpc. Skip creating a sixth VPC when the workspace supplies afterhours subnets.

* style(test): format the VPC terraform assertion for ruff
This commit is contained in:
Adam Moussa 2026-09-21 21:15:47 +00:00 • committed by GitHub
parent 697deb94fd
commit 3efff5e784
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 114 additions and 21 deletions

View file

@ -68,3 +68,26 @@ check "dev_has_no_custom_domain" {
error_message = "attach_custom_domain must be false in non-prod; use the CloudFront distribution domain."
}
}
check "existing_vpc_pair" {
assert {
condition = (var.existing_vpc_id == "") == (length(var.existing_public_subnet_ids) == 0)
error_message = "existing_vpc_id and existing_public_subnet_ids must both be set or both be empty."
}
}
check "existing_vpc_two_az" {
assert {
condition = var.existing_vpc_id == "" || length(var.existing_public_subnet_ids) >= 2
error_message = "existing_public_subnet_ids must include at least two subnets."
}
}
check "existing_subnets_in_vpc" {
assert {
condition = alltrue([
for subnet in data.aws_subnet.existing_public : subnet.vpc_id == var.existing_vpc_id
])
error_message = "Every existing_public_subnet_ids value must belong to existing_vpc_id."
}
}

View file

@ -39,7 +39,7 @@ resource "aws_ecr_lifecycle_policy" "api" {
resource "aws_security_group" "alb" {
name = "${local.project}-alb"
description = "Public ALB for meal-order-manager"
vpc_id = aws_vpc.this.id
vpc_id = local.vpc_id
ingress {
# CloudFront prefix lists cannot cover GitHub-hosted weekly-menu HMAC
@ -63,7 +63,7 @@ resource "aws_security_group" "alb" {
resource "aws_security_group" "api" {
name = "${local.project}-api"
description = "Fargate tasks for meal-order-manager"
vpc_id = aws_vpc.this.id
vpc_id = local.vpc_id
ingress {
description = "From ALB"
@ -86,7 +86,7 @@ resource "aws_lb" "api" {
load_balancer_type = "application"
idle_timeout = 120
security_groups = [aws_security_group.alb.id]
subnets = aws_subnet.public[*].id
subnets = local.public_subnet_ids
drop_invalid_header_fields = true
}
@ -95,7 +95,7 @@ resource "aws_lb_target_group" "api" {
name = "${local.project}-api"
port = 8080
protocol = "HTTP"
vpc_id = aws_vpc.this.id
vpc_id = local.vpc_id
target_type = "ip"
health_check {
@ -202,7 +202,7 @@ resource "aws_ecs_service" "api" {
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.public[*].id
subnets = local.public_subnet_ids
security_groups = [aws_security_group.api.id]
assign_public_ip = true
}

View file

@ -7,8 +7,8 @@ locals {
github_oidc_provider_arn = "arn:aws:iam::${local.account_id}:oidc-provider/token.actions.githubusercontent.com"
# Prod has no default VPC. 10.60 is unused in 011934824531
# (10.0 proposal-system, 10.20 payments-dashboard, 10.40 syslog, 10.80 apm-wo).
# Created only when existing_vpc_id is empty. 10.60 is unused in 011934824531.
manage_vpc = var.existing_vpc_id == ""
vpc_cidr = "10.60.0.0/16"
public_subnet_cidrs = ["10.60.0.0/24", "10.60.1.0/24"]

View file

@ -91,3 +91,15 @@ variable "checkcomponents_queue_arn" {
type = string
default = "arn:aws:sqs:us-east-1:011934824531:paychex-checkcomponents"
}
variable "existing_vpc_id" {
description = "When set, place the ALB and Fargate tasks in this VPC instead of creating one."
type = string
default = ""
}
variable "existing_public_subnet_ids" {
description = "Public subnet IDs in existing_vpc_id. Required with existing_vpc_id; ignored when that variable is empty."
type = list(string)
default = []
}

View file

@ -1,8 +1,21 @@
data "aws_availability_zones" "available" {
count = local.manage_vpc ? 1 : 0
state = "available"
}
data "aws_vpc" "existing" {
count = local.manage_vpc ? 0 : 1
id = var.existing_vpc_id
}
data "aws_subnet" "existing_public" {
for_each = toset(var.existing_public_subnet_ids)
id = each.value
}
resource "aws_vpc" "this" {
count = local.manage_vpc ? 1 : 0
cidr_block = local.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
@ -19,7 +32,9 @@ resource "aws_vpc" "this" {
}
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
count = local.manage_vpc ? 1 : 0
vpc_id = aws_vpc.this[0].id
tags = {
Name = "${local.project}-igw"
@ -27,11 +42,11 @@ resource "aws_internet_gateway" "this" {
}
resource "aws_subnet" "public" {
count = length(local.public_subnet_cidrs)
count = local.manage_vpc ? length(local.public_subnet_cidrs) : 0
vpc_id = aws_vpc.this.id
vpc_id = aws_vpc.this[0].id
cidr_block = local.public_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
availability_zone = data.aws_availability_zones.available[0].names[count.index]
map_public_ip_on_launch = true
tags = {
@ -40,7 +55,9 @@ resource "aws_subnet" "public" {
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.this.id
count = local.manage_vpc ? 1 : 0
vpc_id = aws_vpc.this[0].id
tags = {
Name = "${local.project}-public"
@ -48,14 +65,46 @@ resource "aws_route_table" "public" {
}
resource "aws_route" "public_default" {
route_table_id = aws_route_table.public.id
count = local.manage_vpc ? 1 : 0
route_table_id = aws_route_table.public[0].id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.this.id
gateway_id = aws_internet_gateway.this[0].id
}
resource "aws_route_table_association" "public" {
count = length(local.public_subnet_cidrs)
count = local.manage_vpc ? length(local.public_subnet_cidrs) : 0
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
route_table_id = aws_route_table.public[0].id
}
locals {
vpc_id = local.manage_vpc ? aws_vpc.this[0].id : data.aws_vpc.existing[0].id
public_subnet_ids = local.manage_vpc ? aws_subnet.public[*].id : var.existing_public_subnet_ids
}
moved {
from = aws_vpc.this
to = aws_vpc.this[0]
}
moved {
from = aws_internet_gateway.this
to = aws_internet_gateway.this[0]
}
moved {
from = aws_route_table.public
to = aws_route_table.public[0]
}
moved {
from = aws_route.public_default
to = aws_route.public_default[0]
}
moved {
from = data.aws_availability_zones.available
to = data.aws_availability_zones.available[0]
}

View file

@ -1,4 +1,4 @@
"""Meals owns a dedicated VPC. Prod has no default VPC."""
"""Meals creates a VPC unless existing_vpc_id is set (prod shares afterhours)."""
from pathlib import Path
@ -14,13 +14,22 @@ def test_meals_owns_a_vpc_instead_of_looking_up_default():
vpc = _read("vpc.tf")
ecs = _read("ecs.tf")
locals_tf = _read("locals.tf")
variables = _read("variables.tf")
data = _read("data.tf")
assert 'resource "aws_vpc" "this"' in vpc
assert "cidr_block = local.vpc_cidr" in vpc
assert 'vpc_cidr = "10.60.0.0/16"' in locals_tf
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 'data "aws_vpc" "default"' not in ecs
assert "data.aws_vpc.default" not in ecs
assert "data.aws_subnets.default" not in ecs
assert "aws_vpc.this.id" in ecs
assert "aws_subnet.public[*].id" in ecs
assert "vpc_id = local.vpc_id" in ecs
assert "subnets = local.public_subnet_ids" in ecs
assert "subnets = local.public_subnet_ids" in ecs
assert "ec2:CreateVpc" in _read("hcp_iam.tf")
assert 'check "existing_vpc_pair"' in data
assert 'check "existing_subnets_in_vpc"' in data
assert "from = aws_vpc.this" in vpc
assert "to = aws_vpc.this[0]" in vpc