fix(infra): give meals its own VPC because prod has none

This commit is contained in:
Adam Moussa 2026-09-21 15:20:36 -04:00
parent 19cbddd2fc
commit 6573312bb3
No known key found for this signature in database
5 changed files with 137 additions and 34 deletions

View file

@ -1,22 +1,6 @@
# Always-on meals API: Fargate behind an ALB. GitHub Actions owns the image;
# Terraform ignores container_definitions after the bootstrap task definition.
data "aws_vpc" "default" {
default = true
}
data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
filter {
name = "default-for-az"
values = ["true"]
}
}
resource "aws_ecr_repository" "api" {
name = local.project
image_tag_mutability = "MUTABLE"
@ -55,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 = data.aws_vpc.default.id
vpc_id = aws_vpc.this.id
ingress {
# CloudFront prefix lists cannot cover GitHub-hosted weekly-menu HMAC
@ -79,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 = data.aws_vpc.default.id
vpc_id = aws_vpc.this.id
ingress {
description = "From ALB"
@ -102,7 +86,7 @@ resource "aws_lb" "api" {
load_balancer_type = "application"
idle_timeout = 120
security_groups = [aws_security_group.alb.id]
subnets = data.aws_subnets.default.ids
subnets = aws_subnet.public[*].id
drop_invalid_header_fields = true
}
@ -111,7 +95,7 @@ resource "aws_lb_target_group" "api" {
name = "${local.project}-api"
port = 8080
protocol = "HTTP"
vpc_id = data.aws_vpc.default.id
vpc_id = aws_vpc.this.id
target_type = "ip"
health_check {
@ -218,7 +202,7 @@ resource "aws_ecs_service" "api" {
launch_type = "FARGATE"
network_configuration {
subnets = data.aws_subnets.default.ids
subnets = aws_subnet.public[*].id
security_groups = [aws_security_group.api.id]
assign_public_ip = true
}

View file

@ -464,23 +464,45 @@ resource "aws_iam_role_policy" "hcptf_apply_services" {
},
{
Action = [
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeAccountAttributes",
"ec2:CreateSecurityGroup",
"ec2:DeleteSecurityGroup",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:AssociateRouteTable",
"ec2:AttachInternetGateway",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:RevokeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateInternetGateway",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:DeleteInternetGateway",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteSubnet",
"ec2:DeleteTags",
"ec2:DeleteVpc",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcs",
"ec2:DetachInternetGateway",
"ec2:DisassociateRouteTable",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
]
Resource = "*"
Effect = "Allow"
Sid = "VpcSecurityGroups"
Sid = "Ec2VpcManagement"
},
{
Action = [
@ -858,9 +880,17 @@ resource "aws_iam_role_policy" "hcptf_plan_refresh" {
},
{
Action = [
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcs",
]
Resource = "*"
Effect = "Allow"

View file

@ -7,6 +7,11 @@ 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).
vpc_cidr = "10.60.0.0/16"
public_subnet_cidrs = ["10.60.0.0/24", "10.60.1.0/24"]
form_bucket_name = "${local.project}-form-${local.account_id}"
reports_bucket_name = "${local.project}-reports-${local.account_id}"

58
terraform/vpc.tf Normal file
View file

@ -0,0 +1,58 @@
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_vpc" "this" {
cidr_block = local.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${local.project}-vpc"
}
# First apply updates the live hcptf apply role before CreateVpc.
depends_on = [aws_iam_role_policy.hcptf_apply_services]
}
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
tags = {
Name = "${local.project}-igw"
}
}
resource "aws_subnet" "public" {
count = length(local.public_subnet_cidrs)
vpc_id = aws_vpc.this.id
cidr_block = local.public_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${local.project}-public-${count.index}"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.this.id
tags = {
Name = "${local.project}-public"
}
}
resource "aws_route" "public_default" {
route_table_id = aws_route_table.public.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.this.id
}
resource "aws_route_table_association" "public" {
count = length(local.public_subnet_cidrs)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}

View file

@ -0,0 +1,26 @@
"""Meals owns a dedicated VPC. Prod has no default VPC."""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
TERRAFORM = ROOT / "terraform"
def _read(name: str) -> str:
return (TERRAFORM / name).read_text()
def test_meals_owns_a_vpc_instead_of_looking_up_default():
vpc = _read("vpc.tf")
ecs = _read("ecs.tf")
locals_tf = _read("locals.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 '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 "ec2:CreateVpc" in _read("hcp_iam.tf")