Getting Started with AWS and Terraform: Setting Up Application Load Balancer (ALB) Across Multiple AZs Using Terraform
Tags: aws, terraform, alb, upskilling
Published: 2024-01-27 06:40:08


In a previous post, we discussed how to create CloudWatch alarms and receive alarm emails through SNS topics. 

This post will guide you through the process of setting up an Application Load Balancer (ALB) in front of a website distributed across multiple Availability Zones (AZs) using Terraform.

Application Load Balancer operates at the application layer (Layer 7) of the OSI model. It is best suited for routing HTTP/HTTPS traffic. It supports content-based routing and host-based routing and allows for routing decisions based on content and application behavior.

## Architecture Overview
Before diving into the implementation details, let's briefly review the architecture we'll be working with:

![Architecture ALB across AZs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgm3fixu3w63r48s2vce.png)

Variables used in this section:
```terraform
variable "aws_region" {
  type        = string
  description = "AWS region to use for resources."
  default     = "us-east-1"
}
variable "aws_azs" {
  type        = list(string)
  description = "AWS Availability Zones"
  default     = ["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"]
}
variable "enable_dns_hostnames" {
  type        = bool
  description = "Enable DNS hostnames in VPC"
  default     = true
}
variable "vpc_cidr_block" {
  type        = string
  description = "Base CIDR Block for VPC"
  default     = "10.0.0.0/16"
}
variable "vpc_public_subnets_cidr_block" {
  type        = list(string)
  description = "CIDR Block for Public Subnets in VPC"
  default     = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24", "10.0.104.0/24"]
}
variable "instance_type" {
  type        = string
  description = "Type for EC2 Instance"
  default     = "t2.micro"
}
variable "instance_key" {
  default = "MyKeyPair"
}
```

## Step 1: Creating the VPC and Network Components
Create VPC with IGW, 4 public subnets in separate AZs and route table with association. Please refer to my github repo in resources section below.

## Step 2: Creating 4 Linux EC2 Web Server Instances in Separate AZs
Create 4 AWS EC2 web servers in public subnets across 4 AZs. Please refer to my github repo in resources section below.

## Step 3: Creating an Application Load Balancer
Create an ALB using its separate security group:
```terraform
####################################################
# create application load balancer
####################################################
resource "aws_lb" "aws-application_load_balancer" {
  internal           = false
  load_balancer_type = "application"
  security_groups    = [var.security_group_alb[0]]
  subnets                    = tolist(var.public_subnets)
  enable_deletion_protection = false
}
```
Add an inbound rule to EC2 security group to allow traffic from ALB security group
```terraform
####################################################
# Add an inbound rule to EC2 security group to allow traffic from ALB security group
####################################################
resource "aws_security_group_rule" "ASG-sg-to-alb-sg-ingress" {
  type                     = "ingress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  security_group_id        = aws_security_group.sg.id
  source_security_group_id = aws_security_group.aws-sg-load-balancer.id
}
```
Create target group for ALB with a listener on port 80
```terraform
####################################################
# create target group for ALB
####################################################
resource "aws_lb_target_group" "alb_target_group" {
  target_type = "instance"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = var.vpc_id

  health_check {
    enabled             = true
    interval            = 300
    path                = "/"
    timeout             = 60
    matcher             = 200
    healthy_threshold   = 5
    unhealthy_threshold = 5
  }
  lifecycle {
    create_before_destroy = true
  }
}

####################################################
# create a listener on port 80 with redirect action
####################################################
resource "aws_lb_listener" "alb_http_listener" {
  load_balancer_arn = aws_lb.aws-application_load_balancer.id
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.alb_target_group.id

  }
}
```

Finally attach target group with instances:
```terraform
####################################################
# Target Group Attachment with Instance
####################################################

resource "aws_alb_target_group_attachment" "tgattachment" {
  count            = length(var.instance_ids)
  target_group_arn = aws_lb_target_group.alb_target_group.arn
  target_id        = element(var.instance_ids, count.index)
}

```

## Steps to run Terraform
Now we will see how to run the above created terraform infrastructure automation
```terraform
terraform init
terraform plan 
terraform apply -auto-approve
```

Upon successful completion of the Terraform apply, you'll see essential outputs containing information about the created resources.

```terraform
Apply complete! Resources: 25 added, 0 changed, 0 destroyed.

Outputs:

alb_dns_name = "tf-lb-20231130193349621000000004-1500099737.us-east-1.elb.amazonaws.com"
ec2_instance_ids = [
  "i-05eb93181588e2b83",
  "i-09431707f204e99ad",
  "i-067704933b1bc4b3b",
  "i-07bc08112198ff133",
]
```

## Testing the ALB
After the Terraform apply, you can test the functionality of the ALB. Use the ALB's DNS name to access the website.

![Created ALB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dmbarn24ams6d3r7uwc4.png)

![Running EC2 instances across AZs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2yh8hskxydw3aukktppm.png)

Refresh the page to observe how the ALB routes requests to the EC2 instances in its target group.

![Routing 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53b3oyawbqf79dbg8cb1.png)

![Routing 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hfcg1c1pombwvrtgeoje.png)

![Routing 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w5f2iov4og3fv2dnnul3.png)


![Routing 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7imnv3ahz73gwqj6e7n.png)

## Cleanup
To avoid incurring unnecessary costs, remember to stop AWS components when they are no longer needed:
```terraform
terraform destroy -auto-approve
```

Congratulations! You've successfully deployed an Application Load Balancer (ALB) in front of four Web Server EC2 instances across multiple Availability Zones. 

In the next module, we'll explore enhancing the setup by incorporating EC2 auto scaling with ALB to further optimize the scalability and availability of our application. Happy Coding!

## Resources:
Github Link: https://github.com/chinmayto/terraform-aws-linux-webserver-ec2-alb
ALB: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html