Skip to content

The alb.tf File

Filename Location Group Project/Repository
alb.tf ./alb.tf infrastructure terraform

Why?

The ALB will be used to load balance traffic across our EC2 Instances. It'll also detect a failed instance and stop sending traffic to it. This prevents visitors from seeing a dead website.

Breakdown

First we create an actual Application Load Balancer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
resource "aws_alb" "httpcats" {
  name = "httpcats-beta"

  subnets = [
    aws_subnet.httpcats-http-az-a.id,
    aws_subnet.httpcats-http-az-b.id,
  ]

  security_groups = [
    aws_security_group.alb.id
  ]

  tags = merge(local.common_tags, {
    "Name" = "HTTP DevOps Cats HTTP ALB"
  })
}

We'll use the name attribute to define the name of the resource inside of AWS. This has to be unique.

As we're only using two subnets right now, we'll place the ALB in the same subnets that the web servers will go in using the subnets attribute.

The security_groups attribute sets up the firewall rules for the ALB.

Next we need to define a Listener for the ALB as well as a Target Group.

Listeners

A listener is used to configure the ALB to listen on a particular (Internet facing) port. In our case, that port will be TCP/443, more commonly known as HTTPS.

We'll also configure the ALB, via the listener, to use an ACM issued TLS certificate at this point. That means the encrypted nature of the HTTPS traffic will be terminated here, at the ALB. Our application will receive unencrypted, plain text traffic over TCP/80 (HTTP).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
resource "aws_alb_listener" "http" {
  load_balancer_arn = aws_alb.httpcats.id
  port              = "443"
  protocol          = "HTTPS"
  certificate_arn   = aws_acm_certificate_validation.cert.certificate_arn

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

The default_action is what the ALB/Listener will do if none of our other rules match anything.

Note

At this point in time this code will not run because it's referencing a TLS certificate, managed by ACM, which we haven't defined yet. It's a sort of chick and egg problem.

Rules

Listeners require rules to direct traffic. Here we're creating a rule and attaching it to our ALB:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
resource "aws_lb_listener_rule" "cats" {
  listener_arn = aws_alb_listener.http.arn
  priority     = 100

  action {
    type             = "forward"
    target_group_arn = aws_alb_target_group.cats.arn
  }

  condition {
    path_pattern {
      values = ["*"]
    }
  }
}

This rule instructs the Listener to send all traffic to the Target Group called cats based on the path of the request matching *. That means absolutely any request we make will always match this rule.

Target Groups

Finally we need a Target Group so that the ALB knows what targets (EC2 Instances, Lambda Functions, etc.) to send traffic to:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
resource "aws_alb_target_group" "cats" {
  name     = "httpcats-web-servers"
  port     = "8080"
  protocol = "HTTP"
  vpc_id   = aws_vpc.httpcats.id

  tags = merge(local.common_tags, {
    "Name" = "HTTP DevOps Cats HTTP Servers"
  })
}

Then we need to attach our EC2 Instances to the Target Group, essentially registering them against it so that they become valid targets for inbound traffic:

1
2
3
4
5
6
7
8
9
resource "aws_lb_target_group_attachment" "meow-a" {
  target_group_arn = aws_alb_target_group.cats.arn
  target_id        = aws_instance.meow_1.id
}

resource "aws_lb_target_group_attachment" "meow-b" {
  target_group_arn = aws_alb_target_group.cats.arn
  target_id        = aws_instance.meow_2.id
}

The Solution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
resource "aws_alb" "httpcats" {
  name = "httpcats-beta"

  subnets = [
    aws_subnet.httpcats-http-az-a.id,
    aws_subnet.httpcats-http-az-b.id,
  ]

  security_groups = [
    aws_security_group.alb.id
  ]

  tags = merge(local.common_tags, {
    "Name" = "HTTP DevOps Cats HTTP ALB"
  })
}

resource "aws_alb_listener" "http" {
  load_balancer_arn = aws_alb.httpcats.id
  port              = "443"
  protocol          = "HTTPS"
  certificate_arn   = aws_acm_certificate_validation.cert.certificate_arn

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

resource "aws_lb_listener_rule" "cats" {
  listener_arn = aws_alb_listener.http.arn
  priority     = 100

  action {
    type             = "forward"
    target_group_arn = aws_alb_target_group.cats.arn
  }

  condition {
    path_pattern {
      values = ["*"]
    }
  }
}

resource "aws_alb_target_group" "cats" {
  name     = "httpcats-web-servers"
  port     = "8080"
  protocol = "HTTP"
  vpc_id   = aws_vpc.httpcats.id

  tags = merge(local.common_tags, {
    "Name" = "HTTP DevOps Cats HTTP Servers"
  })
}

resource "aws_lb_target_group_attachment" "meow-a" {
  target_group_arn = aws_alb_target_group.cats.arn
  target_id        = aws_instance.meow_1.id
}

resource "aws_lb_target_group_attachment" "meow-b" {
  target_group_arn = aws_alb_target_group.cats.arn
  target_id        = aws_instance.meow_2.id
}

Terraform Documentation

Type Documentation
aws_alb Terraform AWS Provider
aws_alb_listener Terraform AWS Provider
aws_lb_listener_rule Terraform AWS Provider
aws_alb_target_group Terraform AWS Provider
aws_lb_target_group_attachment Terraform AWS Provider

Committing the Code

  1. Set your working directory to the infrastructure/terraform repository
  2. Save the file as alb.tf and use git add alb.tf to add it to the Git staging area
  3. Use git commit -am 'defining the load balancer for our application' to commit the file to our repository
  4. Push the code to GitLab.com: git push

Last update: August 25, 2021