AWS environment: - VPC (3-AZ, public + private subnets, NAT gateways, VPC endpoints for ECR/SM/CW) - ECS Fargate service (sentryagent/agentidp) — secrets from Secrets Manager - RDS PostgreSQL 14 (Multi-AZ, encrypted, VPC-internal, storage autoscaling) - ElastiCache Redis 7 (primary + replica, at-rest + in-transit encryption) - ALB with HTTPS/443, HTTP→HTTPS redirect, ACM certificate - Route 53 alias record GCP environment: - VPC + private services access + Serverless VPC connector - Cloud Run service — secrets from Secret Manager - Cloud SQL PostgreSQL 14 (private IP, no public endpoint) - Cloud Memorystore Redis 7 (VPC-internal, AUTH enabled) Shared: - 4 reusable modules: agentidp (dual AWS/GCP), rds, redis, lb - No hardcoded secrets; all sensitive vars marked sensitive=true - terraform.tfvars.example for both environments - docs/devops/deployment.md — AWS + GCP step-by-step walkthrough, rollback procedures Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
184 lines
5.1 KiB
HCL
184 lines
5.1 KiB
HCL
################################################################################
|
|
# Module: lb
|
|
# Main — AWS Application Load Balancer
|
|
#
|
|
# - Internet-facing ALB in public subnets
|
|
# - HTTPS listener (443) with ACM certificate, TLS 1.2+ enforced
|
|
# - HTTP listener (80) redirects permanently to HTTPS — no plaintext traffic
|
|
# - Target group pointing to ECS Fargate tasks on the app port
|
|
# - Access logs optionally streamed to S3
|
|
################################################################################
|
|
|
|
terraform {
|
|
required_version = ">= 1.6.0"
|
|
|
|
required_providers {
|
|
aws = {
|
|
source = "hashicorp/aws"
|
|
version = ">= 5.40.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
locals {
|
|
identifier = "${var.project}-${var.environment}"
|
|
|
|
common_tags = {
|
|
environment = var.environment
|
|
project = var.project
|
|
managed_by = "terraform"
|
|
}
|
|
}
|
|
|
|
################################################################################
|
|
# Security Group — ALB allows inbound 80 + 443 from the internet
|
|
################################################################################
|
|
|
|
resource "aws_security_group" "alb" {
|
|
name = "${local.identifier}-alb-sg"
|
|
description = "ALB security group — inbound 80/443 from internet, outbound to app"
|
|
vpc_id = var.vpc_id
|
|
|
|
ingress {
|
|
description = "HTTP from internet (redirected to HTTPS)"
|
|
from_port = 80
|
|
to_port = 80
|
|
protocol = "tcp"
|
|
cidr_blocks = var.allowed_ingress_cidrs
|
|
}
|
|
|
|
ingress {
|
|
description = "HTTPS from internet"
|
|
from_port = 443
|
|
to_port = 443
|
|
protocol = "tcp"
|
|
cidr_blocks = var.allowed_ingress_cidrs
|
|
}
|
|
|
|
egress {
|
|
description = "Forward to ECS app tasks"
|
|
from_port = var.target_group_port
|
|
to_port = var.target_group_port
|
|
protocol = "tcp"
|
|
cidr_blocks = ["0.0.0.0/0"]
|
|
}
|
|
|
|
tags = merge(local.common_tags, {
|
|
Name = "${local.identifier}-alb-sg"
|
|
})
|
|
}
|
|
|
|
################################################################################
|
|
# Application Load Balancer
|
|
################################################################################
|
|
|
|
resource "aws_lb" "main" {
|
|
name = "${local.identifier}-alb"
|
|
internal = false
|
|
load_balancer_type = "application"
|
|
security_groups = [aws_security_group.alb.id]
|
|
subnets = var.subnet_ids
|
|
|
|
idle_timeout = var.idle_timeout
|
|
enable_deletion_protection = var.enable_deletion_protection
|
|
|
|
# HTTP/2 is enabled by default on ALB; leave it on for performance.
|
|
enable_http2 = true
|
|
|
|
# Drop invalid header fields to harden against request smuggling.
|
|
drop_invalid_header_fields = true
|
|
|
|
dynamic "access_logs" {
|
|
for_each = var.access_logs_bucket != "" ? [1] : []
|
|
content {
|
|
bucket = var.access_logs_bucket
|
|
prefix = var.access_logs_prefix
|
|
enabled = true
|
|
}
|
|
}
|
|
|
|
tags = merge(local.common_tags, {
|
|
Name = "${local.identifier}-alb"
|
|
})
|
|
}
|
|
|
|
################################################################################
|
|
# Target Group — ECS Fargate tasks register here
|
|
################################################################################
|
|
|
|
resource "aws_lb_target_group" "app" {
|
|
name = "${local.identifier}-tg"
|
|
port = var.target_group_port
|
|
protocol = "HTTP"
|
|
vpc_id = var.vpc_id
|
|
target_type = "ip" # Required for Fargate (awsvpc network mode)
|
|
|
|
deregistration_delay = 30
|
|
|
|
health_check {
|
|
enabled = true
|
|
path = var.target_group_health_check_path
|
|
port = "traffic-port"
|
|
protocol = "HTTP"
|
|
interval = var.target_group_health_check_interval
|
|
timeout = var.target_group_health_check_timeout
|
|
healthy_threshold = var.target_group_healthy_threshold
|
|
unhealthy_threshold = var.target_group_unhealthy_threshold
|
|
matcher = "200"
|
|
}
|
|
|
|
stickiness {
|
|
type = "lb_cookie"
|
|
enabled = false # AgentIdP is stateless (JWT-based); no sticky sessions needed
|
|
}
|
|
|
|
tags = merge(local.common_tags, {
|
|
Name = "${local.identifier}-tg"
|
|
})
|
|
|
|
lifecycle {
|
|
create_before_destroy = true
|
|
}
|
|
}
|
|
|
|
################################################################################
|
|
# HTTPS Listener (port 443) — primary listener
|
|
################################################################################
|
|
|
|
resource "aws_lb_listener" "https" {
|
|
load_balancer_arn = aws_lb.main.arn
|
|
port = 443
|
|
protocol = "HTTPS"
|
|
ssl_policy = var.ssl_policy
|
|
certificate_arn = var.certificate_arn
|
|
|
|
default_action {
|
|
type = "forward"
|
|
target_group_arn = aws_lb_target_group.app.arn
|
|
}
|
|
|
|
tags = local.common_tags
|
|
}
|
|
|
|
################################################################################
|
|
# HTTP Listener (port 80) — permanent redirect to HTTPS
|
|
################################################################################
|
|
|
|
resource "aws_lb_listener" "http_redirect" {
|
|
load_balancer_arn = aws_lb.main.arn
|
|
port = 80
|
|
protocol = "HTTP"
|
|
|
|
default_action {
|
|
type = "redirect"
|
|
|
|
redirect {
|
|
port = "443"
|
|
protocol = "HTTPS"
|
|
status_code = "HTTP_301"
|
|
}
|
|
}
|
|
|
|
tags = local.common_tags
|
|
}
|