From 950e22da4e4ef91f603bb15a777cc505cb70a905 Mon Sep 17 00:00:00 2001 From: mianava Date: Thu, 9 Apr 2026 11:48:26 -0400 Subject: [PATCH 1/6] Establish ALB module that ingests ACM and sets up a TLS listener by default. --- terraform/modules/alb/README.md | 0 terraform/modules/alb/main.tf | 64 ++++++++++++++++++++++++++++ terraform/modules/alb/outputs.tf | 24 +++++++++++ terraform/modules/alb/variables.tf | 67 ++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 terraform/modules/alb/README.md create mode 100644 terraform/modules/alb/main.tf create mode 100644 terraform/modules/alb/outputs.tf create mode 100644 terraform/modules/alb/variables.tf diff --git a/terraform/modules/alb/README.md b/terraform/modules/alb/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf new file mode 100644 index 000000000..13fecd0b0 --- /dev/null +++ b/terraform/modules/alb/main.tf @@ -0,0 +1,64 @@ +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" + } + } +} \ No newline at end of file diff --git a/terraform/modules/alb/outputs.tf b/terraform/modules/alb/outputs.tf new file mode 100644 index 000000000..840e4dd35 --- /dev/null +++ b/terraform/modules/alb/outputs.tf @@ -0,0 +1,24 @@ +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 "internal" { + description = "Whether the ALB is internal (private) or internet-facing." + value = var.internal +} \ No newline at end of file diff --git a/terraform/modules/alb/variables.tf b/terraform/modules/alb/variables.tf new file mode 100644 index 000000000..4e23569b4 --- /dev/null +++ b/terraform/modules/alb/variables.tf @@ -0,0 +1,67 @@ +# ------------------------------------------------------- +# 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." +} + +# ------------------------------------------------------- +# HTTP Redirect +# ------------------------------------------------------- +variable "enable_http_redirect" { + type = bool + default = true + description = "When true, adds an HTTP:80 listener that redirects all traffic to HTTPS:443." +} From cb975b82a83c7c464567fb9e6ce13e8d1726de32 Mon Sep 17 00:00:00 2001 From: mianava Date: Thu, 9 Apr 2026 12:24:48 -0400 Subject: [PATCH 2/6] Format --- terraform/modules/alb/main.tf | 2 +- terraform/modules/alb/outputs.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf index 13fecd0b0..39a143cfd 100644 --- a/terraform/modules/alb/main.tf +++ b/terraform/modules/alb/main.tf @@ -61,4 +61,4 @@ resource "aws_lb_listener" "http_redirect" { status_code = "HTTP_301" } } -} \ No newline at end of file +} diff --git a/terraform/modules/alb/outputs.tf b/terraform/modules/alb/outputs.tf index 840e4dd35..1d026f4a6 100644 --- a/terraform/modules/alb/outputs.tf +++ b/terraform/modules/alb/outputs.tf @@ -21,4 +21,4 @@ output "https_listener_arn" { output "internal" { description = "Whether the ALB is internal (private) or internet-facing." value = var.internal -} \ No newline at end of file +} From deaaf39c89af52907f6680a6300cf266e31d73b2 Mon Sep 17 00:00:00 2001 From: mianava Date: Thu, 9 Apr 2026 21:39:40 -0400 Subject: [PATCH 3/6] Support multiple listeners. --- terraform/modules/alb/main.tf | 19 +++++++++++++++++++ terraform/modules/alb/outputs.tf | 15 +++++++++++++++ terraform/modules/alb/variables.tf | 12 +++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf index 39a143cfd..404f483a9 100644 --- a/terraform/modules/alb/main.tf +++ b/terraform/modules/alb/main.tf @@ -62,3 +62,22 @@ resource "aws_lb_listener" "http_redirect" { } } } + +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" + } + } +} diff --git a/terraform/modules/alb/outputs.tf b/terraform/modules/alb/outputs.tf index 1d026f4a6..4c80e7029 100644 --- a/terraform/modules/alb/outputs.tf +++ b/terraform/modules/alb/outputs.tf @@ -18,6 +18,21 @@ output "https_listener_arn" { 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 diff --git a/terraform/modules/alb/variables.tf b/terraform/modules/alb/variables.tf index 4e23569b4..2d6932572 100644 --- a/terraform/modules/alb/variables.tf +++ b/terraform/modules/alb/variables.tf @@ -58,10 +58,20 @@ variable "ssl_policy" { } # ------------------------------------------------------- -# HTTP Redirect +# 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 = [] +} \ No newline at end of file From 485f44dd013b586c8523a59bdb7ee691220ba229 Mon Sep 17 00:00:00 2001 From: mianava Date: Wed, 20 May 2026 01:54:33 -0400 Subject: [PATCH 4/6] Formatting --- terraform/modules/alb/variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/modules/alb/variables.tf b/terraform/modules/alb/variables.tf index 2d6932572..ad289bdb0 100644 --- a/terraform/modules/alb/variables.tf +++ b/terraform/modules/alb/variables.tf @@ -74,4 +74,4 @@ variable "extra_listeners" { ssl_policy = optional(string) })) default = [] -} \ No newline at end of file +} From 302fe8ea038a96c19f9ff4a32a9a5d5cebd8a1ae Mon Sep 17 00:00:00 2001 From: mianava Date: Wed, 20 May 2026 01:58:47 -0400 Subject: [PATCH 5/6] Switch to CMS approved domain entries --- terraform/modules/acm_certificate/data.tf | 2 +- terraform/modules/acm_certificate/variables.tf | 2 +- terraform/services/hosted_zones/main.tf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/terraform/modules/acm_certificate/data.tf b/terraform/modules/acm_certificate/data.tf index 5c5e3ec3d..fbee94a3b 100644 --- a/terraform/modules/acm_certificate/data.tf +++ b/terraform/modules/acm_certificate/data.tf @@ -1,5 +1,5 @@ locals { - hosted_zone_base_internal = "${var.platform.env}.${var.platform.app}.cmscloud.internal" + hosted_zone_base_internal = "${var.platform.env}.${var.platform.app}.internal.cms.gov" hosted_zone_base_zscaler = "${var.platform.env}.${var.platform.app}.cmscloud.local" } diff --git a/terraform/modules/acm_certificate/variables.tf b/terraform/modules/acm_certificate/variables.tf index 7a64f5d41..6c93cc294 100644 --- a/terraform/modules/acm_certificate/variables.tf +++ b/terraform/modules/acm_certificate/variables.tf @@ -12,7 +12,7 @@ variable "platform" { } # ------------------------------------------------------- -# Internal endpoint (VPC-only, cmscloud.internal) +# Internal endpoint (VPC-only, <>.internal.cms.gov) # ------------------------------------------------------- variable "enable_internal_endpoint" { type = bool diff --git a/terraform/services/hosted_zones/main.tf b/terraform/services/hosted_zones/main.tf index 7f892e6fa..9916a638c 100644 --- a/terraform/services/hosted_zones/main.tf +++ b/terraform/services/hosted_zones/main.tf @@ -9,7 +9,7 @@ module "platform" { } resource "aws_route53_zone" "internal" { - name = "${var.env}.${var.app}.cmscloud.internal" + name = "${var.env}.${var.app}.internal.cms.gov" vpc { vpc_id = module.platform.vpc_id From 33ae71b0024dc7ce361105754daa46a505132860 Mon Sep 17 00:00:00 2001 From: mianava Date: Wed, 20 May 2026 02:29:37 -0400 Subject: [PATCH 6/6] Update hosted zones with new approved url. --- terraform/modules/alb/main.tf | 7 ++----- terraform/modules/alb/variables.tf | 2 +- terraform/services/hosted_zones/README.md | 2 ++ terraform/services/hosted_zones/variables.tf | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf index 404f483a9..12e91ccf0 100644 --- a/terraform/modules/alb/main.tf +++ b/terraform/modules/alb/main.tf @@ -15,10 +15,7 @@ resource "aws_lb" "this" { subnets = local.subnet_ids security_groups = var.security_group_ids - tags = merge( - { Name = local.alb_name }, - var.platform.tags - ) + tags = { Name = local.alb_name } } # ------------------------------------------------------- @@ -64,7 +61,7 @@ resource "aws_lb_listener" "http_redirect" { } resource "aws_lb_listener" "extra_https" { - for_each = local.extra_listeners_map + for_each = tomap(var.extra_listeners) load_balancer_arn = aws_lb.this.arn port = each.value.port diff --git a/terraform/modules/alb/variables.tf b/terraform/modules/alb/variables.tf index ad289bdb0..d99efa7de 100644 --- a/terraform/modules/alb/variables.tf +++ b/terraform/modules/alb/variables.tf @@ -16,7 +16,7 @@ variable "platform" { 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'." + description = "Override for the ALB name. Defaults to '---alb'." } # ------------------------------------------------------- diff --git a/terraform/services/hosted_zones/README.md b/terraform/services/hosted_zones/README.md index 4fcb44e30..adb077872 100644 --- a/terraform/services/hosted_zones/README.md +++ b/terraform/services/hosted_zones/README.md @@ -1,3 +1,5 @@ +To manage domain resolution at this path, contact the Akamai team. +