Kaizen

Application Load Balancer

The HTTPS front door. Redirects HTTP, ends TLS, and routes to healthy containers.

GovCloudFIPSView source
Module
alb
Layer
Edge
Interface
18 inputs, 10 outputs
Used by
11 Kaizen apps

Why it matters

The load balancer is the one public address users reach. It upgrades every plain HTTP request to HTTPS, sends traffic only to copies of the app that pass a health check, and drops malformed headers before they reach the code. Federal stacks change one setting to get a FIPS 140-3 TLS policy, and that policy exists in both commercial AWS and GovCloud.

Use it when

  • An app on ecs-service needs a public HTTPS endpoint with an ACM certificate.
  • An internal tool needs a private HTTPS endpoint. Set internal = true and pass private subnets.

Reach for something else when

  • The app is a static site with no server. Serve it from S3 behind CloudFront instead. Use s3-bucket.
  • You need request filtering, rate limits, or IP allowlists. Add a web ACL on top of this load balancer. Use waf.

What it creates

  • aws_security_group (ports 80 and 443 from allowed_cidr_blocks, 443 from allowed_security_group_ids)
  • aws_lb (application type, deletion protection on)
  • aws_lb_target_group (ip targets, health check on health_check_path)
  • aws_lb_listener (HTTP on 80, 301 redirect to HTTPS)
  • aws_lb_listener (HTTPS on 443 with ssl_policy and certificate_arn)

Secure by default

  • HTTP on port 80 always answers with a 301 redirect to HTTPS.
  • Deletion protection is on (enable_deletion_protection = true).
  • Invalid HTTP header fields are dropped (drop_invalid_header_fields = true, not configurable).
  • The HTTPS listener uses ELBSecurityPolicy-TLS13-1-2-Res-2021-06, which allows TLS 1.2 and TLS 1.3 only.

Commercial and GovCloud

main.tf
module "alb" {
  source = "git::ssh://git@github.com/the-kaizen-labs/terraform-modules.git//alb?ref=v1.7.0"

  app_name          = local.app_name
  vpc_id            = module.networking.vpc_id
  public_subnet_ids = module.networking.public_subnet_ids
  certificate_arn   = local.certificate_arn
  container_port    = 3000
  health_check_path = "/api/health"

  tags = local.tags
}
SettingCommercial defaultFederal settingNote
ssl_policyELBSecurityPolicy-TLS13-1-2-Res-2021-06ELBSecurityPolicy-TLS13-1-2-FIPS-2023-04FIPS 140-3 TLS on the listener. The policy exists in both partitions.
internalfalsetrue for tools that only staff reachPass private subnets in public_subnet_ids when internal is true.
access_logs_bucketnull (off)an s3-bucket name that grants PutObject to the regional ELB account
allowed_cidr_blocks["0.0.0.0/0"]agency or Cloudflare ranges, or [] with allowed_security_group_ids

How it connects

  • From networking: vpc_id, public_subnet_ids
  • From s3-bucket: bucket_name (as access_logs_bucket)
  • To ecs-service: target_group_arn, alb_security_group_id
  • To waf: alb_arn
  • To bastion: alb_dns_name (as tls_passthrough_target)

Inputs

NameTypeDefaultRequiredDescription
app_namestringyesApplication name used in resource naming
certificate_arnstringyesACM certificate ARN for HTTPS
public_subnet_idslist(string)yesSubnet IDs for the ALB. Pass public subnets for an internet-facing ALB; pass private subnets when internal = true. Variable name kept for backward compatibility.
vpc_idstringyesVPC ID
access_logs_bucketstringnullnoS3 bucket name to receive ALB access logs. Bucket must already grant PutObject to the regional ELB service account. Set to null to disable.
access_logs_prefixstringnullnoKey prefix within access_logs_bucket
allowed_cidr_blockslist(string)["0.0.0.0/0"]noCIDR blocks allowed to reach the ALB on ports 80/443. Override to restrict to Cloudflare IPs etc. Set to [] when using allowed_security_group_ids exclusively.
allowed_security_group_idslist(string)[]noSecurity group IDs allowed to reach the ALB on 443. Use this when callers come from a known SG (e.g. a transit/VPN ENI SG) rather than a CIDR range.
container_portnumber3000noPort the container listens on (used for the target group)
enable_deletion_protectionbooltruenoProtect the ALB from accidental deletion
health_check_matcherstring"200"noHTTP status code(s) considered healthy. Override when the backend returns a non-200 (e.g. Grafana returns 302 from / when unauthenticated).
health_check_pathstring"/"noHealth check path
idle_timeoutnumber60noALB idle connection timeout in seconds. Increase for SSE or long-lived connections.
internalboolfalsenoWhether the ALB is internal-only (private subnets, no public IP). Federal/internal observability deployments set this to true.
security_group_descriptionstringnullnoDescription for the ALB security group. Set this when adopting an existing SG - AWS does not allow editing SG descriptions in place, so a mismatch forces replacement.
ssl_policystring"ELBSecurityPolicy-TLS13-1-2-Res-2021-06"noALB SSL policy. Default is the modern TLS 1.3 policy. Federal callers should override to ELBSecurityPolicy-TLS13-1-2-FIPS-2023-04.
stickiness_enabledboolfalsenoEnable lb_cookie stickiness (useful during rolling deployments)
tagsmap(string){}noTags to apply to all resources

Outputs

NameDescription
alb_arnValue: aws_lb.main.arn
alb_arn_suffixValue: aws_lb.main.arn_suffix
alb_dns_nameValue: aws_lb.main.dns_name
alb_idValue: aws_lb.main.id
alb_security_group_idValue: aws_security_group.alb.id
alb_zone_idValue: aws_lb.main.zone_id
http_listener_arnValue: aws_lb_listener.http.arn
https_listener_arnValue: aws_lb_listener.https.arn
target_group_arnValue: aws_lb_target_group.app.arn
target_group_arn_suffixValue: aws_lb_target_group.app.arn_suffix

Used by

AppPinned ref
BidBuddyv1.0.0
castlev1.6.0
evergreenv1.0.0
foragerv1.1.0
ginkgov1.3.0
juniperv1.1.0
magnoliav1.1.0
meridianv1.0.0
oakv1.1.0
saplingsv1.3.0
test-health-dashboardv1.4.0

On this page