AWS with Terraform Tutorial: AWS Load Balancers (16)

· 10 min read · Terraform & OpenTofu Tutorials

How to create AWS Application Load Balancers with Terraform #

Using the Terraform aws_lb, aws_lb_target_group and aws_lb_listener resource blocks to distribute the traffic between the instances of an Auto Scaling group.

Welcome to our tutorial series about Terraform or OpenTofu on AWS. The previous section created a group of front-end instances whose IP addresses change all the time. In this section an Application Load Balancer becomes the single, stable entry point: it receives the traffic from the Internet, sends it only to healthy instances and, with a certificate, serves it over HTTPS.

Terraform is an Infrastructure as Code (IaC) tool used to provision and manage infrastructure. It helps define and deploy resources across various cloud providers using code, making it easier to maintain and scale infrastructure.Terraform is an Infrastructure as Code (IaC) tool used to provision and manage infrastructure. It helps define and deploy resources across various cloud providers using code, making it easier to maintain and scale infrastructure.
Terraform
Basics
Terraform...
AWS is the world’s leading cloud platform, it is used by a wide range of organizations, from startups to large enterprises, to power their online businesses. AWS offers a wide range of services, including computing, storage, database, networking, analytics, machine learning, and artificial intelligence.AWS is the world’s leading cloud platform, it is used by a wide range of organizations, from startups to large enterprises, to power their online businesses. AWS offers a wide range of services, including computing, storage, database, networking, analytics, machine learning, and artificial intelligence.
AWS
Basics
AWS...
The Terraform official AWS provider acts as an abstraction layer that lets Terraform configurations written in HCL define AWS services and infrastructure using code (IaC). Internally Terraform and the AWS provider handle authentication, and make the necessary AWS API calls to query, create, modify, and destroy the resources.The Terraform official AWS provider acts as an abstraction layer that lets Terraform configurations written in HCL define AWS services and infrastructure using code (IaC). Internally Terraform and the AWS provider handle authentication, and make the necessary AWS API calls to query, create, modify, and destroy the resources.
Terraform
AWS Provider
Terraform...
How to Create, and Manage AWS VPCs with TerraformHow to Create, and Manage AWS VPCs with Terraform
AWS VPC
AWS VPC
How to configure and use the Terraform aws_subnet resource block to create and manage AWS Subnets inside a VPC.How to configure and use the Terraform aws_subnet resource block to create and manage AWS Subnets inside a VPC.
AWS Subnets
AWS Subnets
How to configure and use the Terraform aws_internet_gateway resource block to create and manage AWS Internet Gateway inside a VPC to enable Internet access to and from instances. How to configure and use the Terraform aws_internet_gateway resource block to create and manage AWS Internet Gateway inside a VPC to enable Internet access to and from instances.
AWS Internet
Gateway
AWS Internet...
How to configure and use the Terraform aws_nat_gateway and aws_eip resource blocks to create and manage AWS NAT Gateway and its corresponding Public IPs inside each availability zone to enable Internet access from instances in private subnets.How to configure and use the Terraform aws_nat_gateway and aws_eip resource blocks to create and manage AWS NAT Gateway and its corresponding Public IPs inside each availability zone to enable Internet access from instances in private subnets.
AWS NAT
Gateway
AWS NAT...
How to configure and use the Terraform aws_route_table, aws_route, and aws_main_route_table_association resource blocks to create and manage AWS Routing Tables.How to configure and use the Terraform aws_route_table, aws_route, and aws_main_route_table_association resource blocks to create and manage AWS Routing Tables.
AWS Routing
Tables
AWS Routing...
How to configure and use the Terraform aws_security_group and aws_security_group_rule resource blocks to create and manage AWS Security Groups and secure the infrastructure.How to configure and use the Terraform aws_security_group and aws_security_group_rule resource blocks to create and manage AWS Security Groups and secure the infrastructure.
AWS Security
Groups
AWS Security...
How to configure and use the Terraform aws_key_pair resource block to create and manage AWS Key Pairs for performing SSH Public Key Authentication into EC2 instances.How to configure and use the Terraform aws_key_pair resource block to create and manage AWS Key Pairs for performing SSH Public Key Authentication into EC2 instances.
AWS Key
Pairs
AWS Key...
How to configure and use the Terraform aws_ami data source block to find and use AWS AMIs as templates (root volume snapshot with operating system and applications) for EC2 instances.How to configure and use the Terraform aws_ami data source block to find and use AWS AMIs as templates (root volume snapshot with operating system and applications) for EC2 instances.
AWS AMIs
AWS AMIs
Using the Terraform aws_instance resource block to configure, launch, and secure EC2 instances.Using the Terraform aws_instance resource block to configure, launch, and secure EC2 instances.
AWS EC2
Instances
AWS EC2...
AWS RDS
AWS RDS
Using the Terraform aws_route53_delegation_set, aws_route53_zone, and aws_route53_record resource blocks to configure DNS in AWS. Using the Terraform aws_route53_delegation_set, aws_route53_zone, and aws_route53_record resource blocks to configure DNS in AWS.
AWS Route 53
(DNS)
AWS Route 53...
Amazon EC2 Auto Scaling groups keep the right number of instances running, replace failed ones and scale with the load.Amazon EC2 Auto Scaling groups keep the right number of instances running, replace failed ones and scale with the load.
AWS Auto
Scaling
AWS Auto...
Elastic Load Balancing distributes the traffic between healthy instances. The tutorial creates an Application Load Balancer with HTTPS.Elastic Load Balancing distributes the traffic between healthy instances. The tutorial creates an Application Load Balancer with HTTPS.
AWS Load
Balancers
AWS Load...
This tutorial shows how to create infrastructure in AWS using Terraform and configure the operating system and applications using Ansible.This tutorial shows how to create infrastructure in AWS using Terraform and configure the operating system and applications using Ansible.
Terraform,
AWS & Ansible
Terraform,...
Modules package resources behind variables and outputs so they can be reused. The tutorial refactors the network into a module.Modules package resources behind variables and outputs so they can be reused. The tutorial refactors the network into a module.
Terraform
Modules
Terraform...
A backend stores the Terraform state. The tutorial uses an encrypted, versioned S3 bucket with state locking.A backend stores the Terraform state. The tutorial uses an encrypted, versioned S3 bucket with state locking.
Terraform
Backends
Terraform...
fmt, validate, TFLint, Trivy, Checkov, terraform-docs, Infracost and pre-commit: check the code before it reaches AWS.fmt, validate, TFLint, Trivy, Checkov, terraform-docs, Infracost and pre-commit: check the code before it reaches AWS.
Terraform
Tools
Terraform...
Run checks, plans and approved applies automatically with GitHub Actions and OIDC, without access keys.Run checks, plans and approved applies automatically with GitHub Actions and OIDC, without access keys.
Terraform
CI/CD
Terraform...
Select a tutorial section 
Select a tutorial sectio...

Prerequisites #

Read the previous sections of the tutorial, listed in the series index at the end of this page. This section builds on:

AWS Load Balancers #

Elastic Load Balancing offers several types of load balancers:

Type Layer Use it for
Application Load Balancer (ALB) 7 (HTTP/HTTPS) Web sites and APIs: routing by host name, path or header, redirects, HTTPS termination
Network Load Balancer (NLB) 4 (TCP/UDP/TLS) Very high performance, static IP addresses, non-HTTP protocols
Gateway Load Balancer (GWLB) 3 Firewalls and traffic inspection appliances

This tutorial uses an Application Load Balancer. It has four parts:

  • the load balancer (aws_lb), placed in the public subnets of at least two Availability Zones,
  • a security group that controls who can reach it,
  • a target group (aws_lb_target_group): the list of instances that receive the traffic and the health check used to know which are healthy,
  • a listener (aws_lb_listener): the port and protocol the load balancer listens on and what it does with the requests.

Definition of an Application Load Balancer with Terraform #

Security groups #

The load balancer accepts HTTP from the Internet. The front-end instances now accept HTTP only from the load balancer, which means nobody can reach them directly. Remove the rule ditwl-sr-internet-to-front-end-http created in the Security Groups section and add:

terraform-aws-tutorial.tf
# Security Group for the load balancer
resource "aws_security_group" "ditwl-sg-alb-front-end" {
  name        = "ditwl-sg-alb-front-end"
  vpc_id      = aws_vpc.ditlw-vpc.id
  description = "Load balancer of the front-end servers"
}

# Allow access from the Internet to port 80 HTTP in the load balancer
resource "aws_security_group_rule" "ditwl-sr-internet-to-alb-http" {
  security_group_id = aws_security_group.ditwl-sg-alb-front-end.id
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"] # Internet
  description       = "Allow access from the Internet to port 80 in the load balancer"
}

# Allow the load balancer to reach port 80 in the front-end servers
resource "aws_security_group_rule" "ditwl-sr-alb-to-front-end-egress" {
  security_group_id        = aws_security_group.ditwl-sg-alb-front-end.id
  type                     = "egress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.ditwl-sg-front-end.id
  description              = "Allow traffic from the load balancer to the front-end servers"
}

# Allow access from the load balancer to port 80 in the front-end servers
resource "aws_security_group_rule" "ditwl-sr-alb-to-front-end-http" {
  security_group_id        = aws_security_group.ditwl-sg-front-end.id
  type                     = "ingress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.ditwl-sg-alb-front-end.id
  description              = "Allow access from the load balancer to port 80 in the front-end"
}

Referencing a security group instead of an IP range is a best practice: the rule keeps working when the load balancer changes its addresses.

Load balancer, target group and listener #

terraform-aws-tutorial.tf
# Application Load Balancer in the two public subnets
resource "aws_lb" "ditwl-alb-front-end" {
  name                       = "ditwl-alb-front-end"
  load_balancer_type         = "application"
  internal                   = false
  security_groups            = [aws_security_group.ditwl-sg-alb-front-end.id]
  subnets                    = [aws_subnet.ditwl-sn-za-pro-pub-00.id, aws_subnet.ditwl-sn-zb-pro-pub-04.id]
  drop_invalid_header_fields = true
}

# Target group: the front-end instances and how to check that they are healthy
resource "aws_lb_target_group" "ditwl-tg-front-end" {
  name                 = "ditwl-tg-front-end"
  port                 = 80
  protocol             = "HTTP"
  vpc_id               = aws_vpc.ditlw-vpc.id
  deregistration_delay = 30 # seconds to finish the requests in progress before removing an instance

  health_check {
    path                = "/"
    matcher             = "200"
    interval            = 15
    timeout             = 5
    healthy_threshold   = 2
    unhealthy_threshold = 3
  }
}

# Listener: HTTP on port 80 forwards the requests to the target group
resource "aws_lb_listener" "ditwl-lbl-front-end-http" {
  load_balancer_arn = aws_lb.ditwl-alb-front-end.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.ditwl-tg-front-end.arn
  }
}

output "ditwl-alb-front-end-dns" {
  value = aws_lb.ditwl-alb-front-end.dns_name
}

Connect the Auto Scaling group to the target group #

Modify the Auto Scaling group from the previous section. Two arguments change: the group registers its instances in the target group, and it uses the load balancer health check, so an instance that does not answer is replaced and not only the ones that are stopped.

terraform-aws-tutorial.tf
resource "aws_autoscaling_group" "ditwl-asg-front-end" {
  # ... the rest of the arguments do not change ...
  target_group_arns = [aws_lb_target_group.ditwl-tg-front-end.arn]
  health_check_type = "ELB"
}

For a production environment, move the instances to the private subnets (vpc_zone_identifier = [aws_subnet.ditwl-sn-za-pro-pri-02.id, aws_subnet.ditwl-sn-zb-pro-pri-06.id]). Only the load balancer is then exposed to the Internet, and the instances use the NAT Gateways to download packages.

A DNS name for the load balancer #

An alias record in the public zone points www to the load balancer. Unlike a CNAME, it is free and can also be used for the zone apex.

terraform-aws-tutorial.tf
resource "aws_route53_record" "ditwl-r53-public-www" {
  zone_id = aws_route53_zone.ditwl-r53-public.zone_id
  name    = "www.${aws_route53_zone.ditwl-r53-public.name}"
  type    = "A"

  alias {
    name                   = aws_lb.ditwl-alb-front-end.dns_name
    zone_id                = aws_lb.ditwl-alb-front-end.zone_id
    evaluate_target_health = true
  }
}

HTTPS with AWS Certificate Manager #

AWS Certificate Manager (ACM) issues free public certificates for the load balancer. The certificate is validated with a DNS record, which Terraform creates in the public zone, so the zone must be delegated and working.

terraform-aws-tutorial.tf
# Certificate for www.demo.itwonderlab.com
resource "aws_acm_certificate" "ditwl-acm-www" {
  domain_name       = "www.${aws_route53_zone.ditwl-r53-public.name}"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

# DNS records that prove that we own the domain
resource "aws_route53_record" "ditwl-r53-acm-www-validation" {
  for_each = {
    for o in aws_acm_certificate.ditwl-acm-www.domain_validation_options : o.domain_name => {
      name   = o.resource_record_name
      record = o.resource_record_value
      type   = o.resource_record_type
    }
  }

  allow_overwrite = true
  zone_id         = aws_route53_zone.ditwl-r53-public.zone_id
  name            = each.value.name
  type            = each.value.type
  records         = [each.value.record]
  ttl             = 60
}

# Wait until the certificate is issued
resource "aws_acm_certificate_validation" "ditwl-acm-www" {
  certificate_arn         = aws_acm_certificate.ditwl-acm-www.arn
  validation_record_fqdns = [for r in aws_route53_record.ditwl-r53-acm-www-validation : r.fqdn]
}

# HTTPS listener
resource "aws_lb_listener" "ditwl-lbl-front-end-https" {
  load_balancer_arn = aws_lb.ditwl-alb-front-end.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06"
  certificate_arn   = aws_acm_certificate_validation.ditwl-acm-www.certificate_arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.ditwl-tg-front-end.arn
  }
}

Then allow port 443 in ditwl-sg-alb-front-end (a second aws_security_group_rule like ditwl-sr-internet-to-alb-http with port 443), and change the HTTP listener to redirect to HTTPS:

terraform-aws-tutorial.tf
resource "aws_lb_listener" "ditwl-lbl-front-end-http" {
  load_balancer_arn = aws_lb.ditwl-alb-front-end.arn
  port              = 80
  protocol          = "HTTP"

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

Run the Terraform Plan #

$ tofu plan
$ tofu apply

The ALB takes a few minutes to be active and the instances need to pass the health checks before they receive traffic. Then test it:

$ tofu output ditwl-alb-front-end-dns
$ curl http://<alb-dns-name>/
front-end ip-172-21-1-35
$ curl http://<alb-dns-name>/
front-end ip-172-21-5-120

Each request can be answered by a different instance: the response shows the name of the instance that served it. Check the status of the targets in the AWS console (EC2 → Target Groups → ditwl-tg-front-end → Targets) or with aws elbv2 describe-target-health.

To destroy the infrastructure and avoid charges:

$ tofu destroy

AWS Load Balancers Cost #

An Application Load Balancer is billed per hour while it exists plus per Load Balancer Capacity Unit (LCU) consumed (new connections, active connections, processed bytes and rule evaluations). A small test environment costs a few cents a day, but a forgotten load balancer is a monthly charge, so destroy it after the tests. See the Elastic Load Balancing pricing. ACM public certificates are free.

Common Questions About AWS Load Balancers #

Should I use an Application or a Network Load Balancer? #

Use an ALB for HTTP and HTTPS applications: it understands the requests and can route by host name or path. Use an NLB when you need static IP addresses, very low latency or protocols that are not HTTP.

How do I send different paths to different target groups? #

Add aws_lb_listener_rule resources to a listener with a condition on the path (/api/*) or the host name and an action that forwards to another target group.

Why does the target group show unhealthy instances? #

Check that the security group of the instances allows the traffic from the load balancer, that the application listens on the target group port and that the health_check path returns the code in matcher. The health_check_grace_period of the Auto Scaling group must be long enough for the instance to boot.

Can I terminate HTTPS in the instances instead? #

You can, with an NLB in TLS passthrough mode, but terminating HTTPS in the ALB is simpler: ACM renews the certificates automatically and the instances do not handle certificates.

Next Steps #

So far Terraform and AWS were used together to create the infrastructure. The next section shows how to use Terraform, AWS and Ansible together to configure the servers.

#AWS #AWS ALB #AWS ELB #Terraform #OpenTofu #AWS Terraform Tutorial