In this Terraform script, I am creating an Application Load Balancer which is associated to 3 Autoscaling Groups. This will Loadbalance the HTTP traffic among the EC2 instances in the AutoScaling Groups based on the path pattern in the header of the web request.
In This Application LoadBalancer, the traffic reaching to the Loadbalancer will split into 3 Target groups as below:
- if the web request is to example.tld, the traffic will go to first target group
- if the web request is to example.tld/path1, the traffic will go to second target group
- if the web request is to example.tld/path2, the traffic will go to third target group
- You can change the values of path1 and path2 from terraform.tfvars according to your requirement.
- Fully Automated
- Easy to customise and use as the Terraform modules are created using variables,allowing the module to be customized without altering the module's own source code, and allowing modules to be shared between different configurations.
- This script will create a the infrastructure in default VPC. If you want to create your own VPC, you may click here.
- AWS informations are defined using tfvars file and can easily changed (Automated/Manual)
- Project name is appended to the resources that are creating which will make easier to identify the resources.
- Create an IAM user on your AWS console that have access to create the required resources.
- Create a dedicated directory where you can create terraform configuration files.
- Install Terraform. Click here for Terraform installation steps
- Knowledge to the working principles of AWS services especially AutoScaling, Loadbalancers and EC2.
- Define AWS region in which you are going to work in terraform.tfvars
region = ""
- Set a Project name in terraform.tfvars
project = ""
This will fetch all available Availability Zones in working AWS region and store the details in variable az
data "aws_availability_zones" "az" {
state = "available"
}
- You can generate SSH key-pair from any Linux systems using ssh-keygen command as below:
- Upload the generated SSH public key to AWS
resource "aws_key_pair" "mykey" {
key_name = "mykey"
public_key = "ssh-rsa AAAAB3NzaC1yc------------------------------------------------------------
-------------------------------------------------------------------------------------.compute.internal"
}
resource "aws_security_group" "webserver" {
name = "webserver-sg"
description = "allows 22, 80,443 anywhere"
vpc_id = aws_default_vpc.default.id
ingress = [
{
description = "port 22"
from_port = "22"
to_port = "22"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
security_groups = []
cidr_blocks = []
ipv6_cidr_blocks = []
self = false
prefix_list_ids = []
},
{
description = "port 80"
from_port = "80"
to_port = "80"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
self = false
prefix_list_ids = []
security_groups = []
},
{
description = "port 443"
from_port = "443"
to_port = "443"
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
self = false
prefix_list_ids = []
security_groups = []
}
]
egress = [
{
description = ""
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
prefix_list_ids = []
security_groups = []
self = false
}
]
tags = {
Name = "${var.project}-webserver-sg"
Project = var.project
}
}
This will create 3 Target Groups which are handling traffic with 3 different headers as explained in the Description.
resource "aws_lb_target_group" "index" {
name = "tg-index"
port = 80
protocol = "HTTP"
vpc_id = aws_default_vpc.default.id
tags = {
Name = "${var.project}-tg-index"
Project = var.project
}
}
resource "aws_lb_target_group" "path1" {
name = "tg-path1"
port = 80
protocol = "HTTP"
vpc_id = aws_default_vpc.default.id
tags = {
Name = "${var.project}-tg-path1"
Project = var.project
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
interval = 30
path = "/var.path1"
protocol = "HTTP"
matcher = 200
}
}
resource "aws_lb_target_group" "path2" {
name = "tg-path2"
port = 80
protocol = "HTTP"
vpc_id = aws_default_vpc.default.id
tags = {
Name = "${var.project}-tg-path2"
Project = var.project
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
interval = 30
path = "/var.path2"
protocol = "HTTP"
matcher = 200
}
}
AutoScaling Groups will use these Launch configurations to launch EC2 instances
resource "aws_launch_configuration" "index" {
name_prefix = "index-"
image_id = var.ami
instance_type = var.type
key_name = "ssh-key-name"
security_groups = [aws_security_group.webserver.id]
lifecycle {
create_before_destroy = true
}
}
resource "aws_launch_configuration" "path1" {
name_prefix = "path-"
image_id = var.ami
instance_type = var.type
key_name = "ssh-key-name"
security_groups = [aws_security_group.webserver.id]
lifecycle {
create_before_destroy = true
}
}
resource "aws_launch_configuration" "path2" {
name_prefix = "path2-"
image_id = var.ami
instance_type = var.type
key_name = "ssh-key-name"
security_groups = [aws_security_group.webserver.id]
lifecycle {
create_before_destroy = true
}
}
This will create an AutoScaling Groups which launches ec2 instances as configured in above Launch Configurations. You can set your Desired capacity, Minimum capacity and Maximum capacity of AutoScaling Groups in terraform.tfvars file.
This Autoscaling group is for the instances hosting index site(example.tld)
resource "aws_autoscaling_group" "index" {
name_prefix = "index-"
max_size = var.max_size
min_size = var.min_size
health_check_grace_period = 300
health_check_type = "EC2"
desired_capacity = var.desired_size
launch_configuration = aws_launch_configuration.index.name
target_group_arns = [ aws_lb_target_group.index.arn ]
vpc_zone_identifier = [-choose-public-subnet-one-,-choose-public-subnet-two-]
tag {
key = "Name"
value = "index"
propagate_at_launch = true
}
lifecycle {
create_before_destroy = true
}
}
This Autoscaling group is for the instances hosting path1 site(example.tld/path1)
resource "aws_autoscaling_group" "path1" {
name_prefix = "path1-"
max_size = var.max_size
min_size = var.min_size
health_check_grace_period = 300
health_check_type = "EC2"
desired_capacity = var.desired_size
launch_configuration = aws_launch_configuration.blog.name
target_group_arns = [ aws_lb_target_group.blog.arn ]
vpc_zone_identifier = [-choose-public-subnet-one-,-choose-public-subnet-two-]
tag {
key = "Name"
value = "path1"
propagate_at_launch = true
}
lifecycle {
create_before_destroy = true
}
}
This Autoscaling group is for the instances hosting path2 site(example.tld/path2)
resource "aws_autoscaling_group" "path2" {
name_prefix = "path2-"
max_size = var.max_size
min_size = var.min_size
health_check_grace_period = 300
health_check_type = "EC2"
desired_capacity = var.desired_size
launch_configuration = aws_launch_configuration.about.name
target_group_arns = [ aws_lb_target_group.about.arn ]
vpc_zone_identifier = [-choose-public-subnet-one-,-choose-public-subnet-two-]
tag {
key = "Name"
value = "path2"
propagate_at_launch = true
}
lifecycle {
create_before_destroy = true
}
}
Here, I added an output variable DNS-name-of-Loadbalancer to print the DNS name of the created Loadbalancer which will be used in DNS configuration of the website.
resource "aws_lb" "alb" {
name = "alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.webserver.id]
subnets = data.aws_subnet_ids.default.ids
tags = {
Name = "${var.project}-alb"
Project = var.project
}
}
output "DNS-name-of-Loadbalancer" {
value = aws_lb.alb.dns_name
}
This rule will forward any requests otherthan
example.tld
orexample.tld/path1/*
orexample.tld/path2/*
to a fixed response as No Site Found
resource "aws_lb_listener" "listener" {
load_balancer_arn = aws_lb.alb.arn
port = "80"
protocol = "HTTP"
default_action {
type = "fixed-response"
fixed_response {
content_type = "text/html"
message_body = "<h1>No Site Found</h1>"
status_code = "200"
}
}
}
This rule will forward all requests with header pattern / to index target group
resource "aws_lb_listener_rule" "index" {
listener_arn = aws_lb_listener.listener.arn
priority = 1
action {
type = "forward"
target_group_arn = aws_lb_target_group.index.arn
}
condition {
path_pattern {
values = ["/"]
}
}
}
This rule will forward all requests with header pattern /path1 to path1 target group
resource "aws_lb_listener_rule" "path1" {
listener_arn = aws_lb_listener.listener.arn
priority = 2
action {
type = "forward"
target_group_arn = aws_lb_target_group.blog.arn
}
condition {
path_pattern {
values = ["/var.path1/*"]
}
}
}
This rule will forward all requests with header pattern /path2 to path2 target group
resource "aws_lb_listener_rule" "path2" {
listener_arn = aws_lb_listener.listener.arn
priority = 3
action {
type = "forward"
target_group_arn = aws_lb_target_group.about.arn
}
condition {
path_pattern {
values = ["/var.path2/*"]
}
}
}
terraform validate
terraform plan
terraform apply
x------------------x---------------------x---------------------x-------------------x--------------------x---------------------x-------------------x