Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
83 changes: 83 additions & 0 deletions terraform/modules/alb/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
locals {
alb_name = var.name_override != null ? var.name_override : "${var.platform.app}-${var.platform.env}-${var.platform.service}-alb"

# Use explicitly provided subnets, or fall back to the platform's private subnets
subnet_ids = var.subnet_ids != null ? var.subnet_ids : [for s in var.platform.private_subnets : s.id]
}

# -------------------------------------------------------
# Application Load Balancer
# -------------------------------------------------------
resource "aws_lb" "this" {
name = local.alb_name
internal = var.internal
load_balancer_type = "application"
subnets = local.subnet_ids
security_groups = var.security_group_ids

tags = merge(
{ Name = local.alb_name },
var.platform.tags
)
}

# -------------------------------------------------------
# HTTPS Listener (port 443)
# Default action is a 404 fixed response — apps can attach
# their own listener rules with path/host conditions.
# -------------------------------------------------------
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.this.arn
port = 443
protocol = "HTTPS"
ssl_policy = var.ssl_policy
certificate_arn = var.acm_certificate_arn

default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "Not Found"
status_code = "404"
}
}
}

# -------------------------------------------------------
# HTTP Listener (port 80) redirect
# -------------------------------------------------------
resource "aws_lb_listener" "http_redirect" {
count = var.enable_http_redirect ? 1 : 0

load_balancer_arn = aws_lb.this.arn
port = 80
protocol = "HTTP"

default_action {
type = "redirect"
redirect {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
}

resource "aws_lb_listener" "extra_https" {
for_each = local.extra_listeners_map

load_balancer_arn = aws_lb.this.arn
port = each.value.port
protocol = "HTTPS"
ssl_policy = coalesce(each.value.ssl_policy, var.ssl_policy)
certificate_arn = coalesce(each.value.acm_certificate_arn, var.acm_certificate_arn)

default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "Not Found"
status_code = "404"
}
}
}
39 changes: 39 additions & 0 deletions terraform/modules/alb/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
output "alb_arn" {
description = "ARN of the ALB."
value = aws_lb.this.arn
}

output "alb_dns_name" {
description = "DNS name of the ALB — use this for Route 53 alias records."
value = aws_lb.this.dns_name
}

output "alb_zone_id" {
description = "Hosted zone ID of the ALB — required for Route 53 alias records."
value = aws_lb.this.zone_id
}

output "https_listener_arn" {
description = "ARN of the HTTPS:443 listener. Listener can be used in downstream modules."
value = aws_lb_listener.https.arn
}

output "all_listener_arns" {
description = <<-EOT
Map of (string) to listener ARNs, including the default 443 listener.
Use this in ecs-service module calls:
alb_listener_arn = module.my_alb.all_listener_arns["9900"]
EOT
value = merge(
{ "443" = aws_lb_listener.https.arn },
{
for port, listener in aws_lb_listener.extra_https :
port => listener.arn
}
)
}

output "internal" {
description = "Whether the ALB is internal (private) or internet-facing."
value = var.internal
}
77 changes: 77 additions & 0 deletions terraform/modules/alb/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# -------------------------------------------------------
# Platform / Core
# -------------------------------------------------------
variable "platform" {
description = "Object representing the CDAP platform module."
type = object({
app = string
env = string
primary_region = object({ name = string })
private_subnets = map(object({ id = string }))
service = string
vpc_id = string
})
}

variable "name_override" {
type = string
default = null
description = "Override for the ALB name. Defaults to '${var.platform.app}-${var.platform.env}-${var.platform.service}-alb'."
}

# -------------------------------------------------------
# Networking
# -------------------------------------------------------
variable "subnet_ids" {
type = list(string)
default = null
description = "Subnet IDs to place the ALB in. Defaults to the platform's private subnets."
}

variable "security_group_ids" {
type = list(string)
default = []
description = "Security group IDs to attach to the ALB."
}

# -------------------------------------------------------
# ALB Visibility
# -------------------------------------------------------
variable "internal" {
type = bool
default = true
description = "true = private (internal) ALB; false = public (internet-facing) ALB."
}

# -------------------------------------------------------
# TLS / ACM
# -------------------------------------------------------
variable "acm_certificate_arn" {
type = string
description = "ARN of the ACM certificate (public or private CA) for the HTTPS listener."
}

variable "ssl_policy" {
type = string
default = "ELBSecurityPolicy-TLS13-1-2-2021-06"
description = "TLS security policy. Default enforces TLS 1.2+ per CMS/FISMA requirements."
}

# -------------------------------------------------------
# Listeners
# -------------------------------------------------------
variable "enable_http_redirect" {
type = bool
default = true
description = "When true, adds an HTTP:80 listener that redirects all traffic to HTTPS:443."
}

variable "extra_listeners" {
description = "Additional HTTPS listeners beyond the default 443"
type = list(object({
port = number
acm_certificate_arn = string
ssl_policy = optional(string)
}))
default = []
}