Files
shared-memory/terraform/alb.tf
T
shadowdaoandClaude Opus 4.7 08be60e661 feat(terraform): AWS Fargate deployment module
Adds a terraform/ directory with an opinionated module that deploys
shared-memory to ECS Fargate behind an ALB. The module assumes the
operator already provides the VPC, RDS Postgres, ACM cert, ECR images,
and OIDC clients, and creates everything else: ECS cluster + services,
ALB, Service Connect namespace for app-embedder discovery, EFS-backed
model cache for the embedder, Secrets Manager entries, IAM roles,
CloudWatch log groups, and a one-shot migrator task definition.

Includes examples/basic/ with a worked invocation and a README covering
prerequisites, quick start, the post-apply migrator run, image updates,
DNS setup, and a security note. Main README gains a short Mode C
pointer to the terraform/ guide.

Validated with `terraform fmt -check -recursive` and
`terraform validate` against AWS provider 5.x.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:40:12 -07:00

86 lines
2.4 KiB
Terraform

# -----------------------------------------------------------------------------
# Application Load Balancer.
#
# * Internet-facing, in the public subnets
# * HTTP listener on :80 returns a 301 to https://${domain}${path}
# * HTTPS listener on :443 terminates TLS with the user's ACM cert and
# forwards to the app target group on 3000
#
# Target type is `ip` because Fargate tasks register their ENI IPs directly,
# not via an EC2 instance.
# -----------------------------------------------------------------------------
resource "aws_lb" "this" {
name = "${var.name_prefix}-alb"
load_balancer_type = "application"
internal = false
subnets = var.public_subnet_ids
security_groups = [aws_security_group.alb.id]
# Keep HTTP/2 on (default) so MCP streaming works smoothly. drop_invalid
# headers protects against header smuggling against the upstream.
drop_invalid_header_fields = true
tags = merge(local.tags, { Name = "${var.name_prefix}-alb" })
}
resource "aws_lb_target_group" "app" {
name = "${var.name_prefix}-app"
port = local.app_port
protocol = "HTTP"
target_type = "ip"
vpc_id = var.vpc_id
deregistration_delay = 30
health_check {
enabled = true
path = "/api/health"
port = "traffic-port"
protocol = "HTTP"
matcher = "200"
interval = 15
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 3
}
tags = local.tags
}
# Port 80 → 301 redirect to HTTPS.
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.this.arn
port = 80
protocol = "HTTP"
default_action {
type = "redirect"
redirect {
protocol = "HTTPS"
port = "443"
status_code = "HTTP_301"
}
}
tags = local.tags
}
# Port 443 → app target group. TLS terminates at the ALB; the app speaks
# plain HTTP behind it. PUBLIC_URL teaches Auth.js and the MCP route that
# the public origin is HTTPS regardless.
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.this.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificate_arn = var.acm_certificate_arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.app.arn
}
tags = local.tags
}