################################################################################ # 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 }