AWS ALB Context Path based Routing using Terraform
Step-00: Pre-requisites
- You need a Registered Domain in AWS Route53 to implement this usecase
- Lets discuss more about it
- Go to AWS Services -> Route53 -> Domains -> Registered Domains -> Register Domain
- Choose a domain name: abcabc.com and click on Check
- If available, click on Add to Cart and Click on Continue
- Provide
Contact Details for Your 1 Domain
and Click on Continue
- Terms and Conditions: Check and click on Complete Order
- Go back to Billing and complete the payment for the domain to be approved
- Copy your
terraform-key.pem
file to terraform-manifests/private-key
folder
Step-01: Introduction
- We are going to implement Context Path based Routing in AWS Application Load Balancer using Terraform.
- To achieve that we are going to implement many series of steps.
- Our core focus in the entire section should be primarily targeted to two things
- Listener Indexes:
https_listener_index = 0
- Target Group Indexes:
target_group_index = 0
- If we are good with understanding these indexes and how to reference them, we are good with handling these multiple context paths or multiple header based routes or anything from ALB perspective.
- We are going to implement the following using AWS ALB
- Fixed Response for /* : http://apps.devopsincloud.com
- App1 /app1* goes to App1 EC2 Instances: http://apps.devopsincloud.com/app1/index.html
- App2 /app2* goes to App2 EC2 Instances: http://apps.devopsincloud.com/app2/index.html
- HTTP to HTTPS Redirect
Step-02: Copy all files from previous section
- We are going to copy all files from previous section
09-AWS-ALB-Application-LoadBalancer-Basic
- Files from
c1 to c10
- Create new files
- c6-02-datasource-route53-zone.tf
- c11-acm-certificatemanager.tf
- c12-route53-dnsregistration.tf
- Review the files
- app1-install.sh
- app2-install.sh
Step-03: c5-05-securitygroup-loadbalancersg.tf
- Update load balancer security group to allow port 443
ingress_rules = ["http-80-tcp", "https-443-tcp"]
Step-04: c6-02-datasource-route53-zone.tf
# Get DNS information from AWS Route53
data "aws_route53_zone" "mydomain" {
name = "devopsincloud.com"
}
# Output MyDomain Zone ID
output "mydomain_zoneid" {
description = "The Hosted Zone id of the desired Hosted Zone"
value = data.aws_route53_zone.mydomain.zone_id
}
Step-05: c7-04-ec2instance-private-app1.tf
- We will change the module name from
ec2_private
to ec2_private_app1
- We will change the
name
to "${var.environment}-app1"
# EC2 Instances that will be created in VPC Private Subnets for App1
module "ec2_private_app1" {
depends_on = [ module.vpc ] # VERY VERY IMPORTANT else userdata webserver provisioning will fail
source = "terraform-aws-modules/ec2-instance/aws"
version = "2.17.0"
# insert the 10 required variables here
name = "${var.environment}-app1"
ami = data.aws_ami.amzlinux2.id
instance_type = var.instance_type
key_name = var.instance_keypair
#monitoring = true
vpc_security_group_ids = [module.private_sg.this_security_group_id]
#subnet_id = module.vpc.public_subnets[0]
subnet_ids = [
module.vpc.private_subnets[0],
module.vpc.private_subnets[1]
]
instance_count = var.private_instance_count
user_data = file("${path.module}/app1-install.sh")
tags = local.common_tags
}
Step-06: c7-05-ec2instance-private-app2.tf
- Create new EC2 Instances for App2 Application
- Module Name: ec2_private_app2
- Name:
"${var.environment}-app2"
- User Data:
user_data = file("${path.module}/app2-install.sh")
# AWS EC2 Instance Terraform Module
# EC2 Instances that will be created in VPC Private Subnets for App2
module "ec2_private_app2" {
depends_on = [ module.vpc ] # VERY VERY IMPORTANT else userdata webserver provisioning will fail
source = "terraform-aws-modules/ec2-instance/aws"
version = "2.17.0"
# insert the 10 required variables here
name = "${var.environment}-app2"
ami = data.aws_ami.amzlinux2.id
instance_type = var.instance_type
key_name = var.instance_keypair
#monitoring = true
vpc_security_group_ids = [module.private_sg.this_security_group_id]
#subnet_id = module.vpc.public_subnets[0]
subnet_ids = [
module.vpc.private_subnets[0],
module.vpc.private_subnets[1]
]
instance_count = var.private_instance_count
user_data = file("${path.module}/app2-install.sh")
tags = local.common_tags
}
Step-07: c7-02-ec2instance-outputs.tf
- Update App1 and App2 Outputs based on new module names
# App1 - Private EC2 Instances
## ec2_private_instance_ids
output "app1_ec2_private_instance_ids" {
description = "List of IDs of instances"
value = module.ec2_private_app1.id
}
## ec2_private_ip
output "app1_ec2_private_ip" {
description = "List of private IP addresses assigned to the instances"
value = module.ec2_private_app1.private_ip
}
# App2 - Private EC2 Instances
## ec2_private_instance_ids
output "app2_ec2_private_instance_ids" {
description = "List of IDs of instances"
value = module.ec2_private_app2.id
}
## ec2_private_ip
output "app2_ec2_private_ip" {
description = "List of private IP addresses assigned to the instances"
value = module.ec2_private_app2.private_ip
}
Step-08: c11-acm-certificatemanager.tf
- Terraform AWS ACM Module
- Create a SAN SSL Certificate using DNS Validation with Route53
- This is required for us with ALB Load Balancer HTTPS Listener to associate SSL certificate to it
- Test trimsuffic function using
terraform console
# Terraform Console
terraform console
# Provide Trim Suffix Function
trimsuffix("devopsincloud.com.", ".")
# Verify Output
"devopsincloud.com"
- ACM Module Terraform Configuration
# ACM Module - To create and Verify SSL Certificates
module "acm" {
source = "terraform-aws-modules/acm/aws"
version = "~> 2.0"
domain_name = trimsuffix(data.aws_route53_zone.mydomain.name, ".")
zone_id = data.aws_route53_zone.mydomain.id
subject_alternative_names = [
"*.devopsincloud.com"
]
tags = local.common_tags
}
# Output ACM Certificate ARN
output "acm_certificate_arn" {
description = "ACM Certificate ARN"
value = module.acm.this_acm_certificate_arn
}
Step-09: c10-02-ALB-application-loadbalancer.tf
Step-09-01: HTTP to HTTPS Redirect
# HTTP Listener - HTTP to HTTPS Redirect
http_tcp_listeners = [
{
port = 80
protocol = "HTTP"
action_type = "redirect"
redirect = {
port = "443"
protocol = "HTTPS"
status_code = "HTTP_301"
}
}
]
Step-09-02: Add Target Group app2
# App2 Target Group - TG Index = 1
{
name_prefix = "app2-"
backend_protocol = "HTTP"
backend_port = 80
target_type = "instance"
deregistration_delay = 10
health_check = {
enabled = true
interval = 30
path = "/app2/index.html"
port = "traffic-port"
healthy_threshold = 3
unhealthy_threshold = 3
timeout = 6
protocol = "HTTP"
matcher = "200-399"
}
protocol_version = "HTTP1"
# App2 Target Group - Targets
targets = {
my_app2_vm1 = {
target_id = module.ec2_private_app2.id[0]
port = 80
},
my_app2_vm2 = {
target_id = module.ec2_private_app2.id[1]
port = 80
}
}
tags =local.common_tags # Target Group Tags
}
Step-09-03: Add HTTPS Listener
- Associate SSL Certificate ARN
- Add fixed response for Root Context
/*
# HTTPS Listener
https_listeners = [
# HTTPS Listener Index = 0 for HTTPS 443
{
port = 443
protocol = "HTTPS"
certificate_arn = module.acm.this_acm_certificate_arn
action_type = "fixed-response"
fixed_response = {
content_type = "text/plain"
message_body = "Fixed Static message - for Root Context"
status_code = "200"
}
},
]
Step-09-04: Add HTTPS Listener Rules
- Understand about
https_listener_index
- Create Rule-1: /app1* should go to App1 EC2 Instances
- Understand about
target_group_index
- Create Rule-2: /app2* should go to App2 EC2 Instances
# HTTPS Listener Rules
https_listener_rules = [
# Rule-1: /app1* should go to App1 EC2 Instances
{
https_listener_index = 0
actions = [
{
type = "forward"
target_group_index = 0
}
]
conditions = [{
path_patterns = ["/app1*"]
}]
},
# Rule-2: /app2* should go to App2 EC2 Instances
{
https_listener_index = 0
actions = [
{
type = "forward"
target_group_index = 1
}
]
conditions = [{
path_patterns = ["/app2*"]
}]
},
]
Step-10: c12-route53-dnsregistration.tf
# DNS Registration
resource "aws_route53_record" "apps_dns" {
zone_id = data.aws_route53_zone.mydomain.id
name = "apps9.devopsincloud.com"
type = "A"
alias {
name = module.alb.this_lb_dns_name
zone_id = module.alb.this_lb_zone_id
evaluate_target_health = true
}
}
# Terraform Initialize
terraform init
# Terraform Validate
terraform validate
# Terraform Plan
terraform plan
# Terraform Apply
terraform apply -auto-approve
# Verify
Observation:
1. Verify EC2 Instances for App1
2. Verify EC2 Instances for App2
3. Verify Load Balancer SG - Primarily SSL 443 Rule
4. Verify ALB Listener - HTTP:80 - Should contain a redirect from HTTP to HTTPS
5. Verify ALB Listener - HTTPS:443 - Should contain 3 rules
5.1 /app1* to app1-tg
5.2 /app2* to app2-tg
5.3 /* return Fixed response
6. Verify ALB Target Groups App1 and App2, Targets (should be healthy)
5. Verify SSL Certificate (Certificate Manager)
6. Verify Route53 DNS Record
# Test (Domain will be different for you based on your registered domain)
# Note: All the below URLS shoud redirect from HTTP to HTTPS
1. Fixed Response: http://apps.devopsincloud.com
2. App1 Landing Page: http://apps.devopsincloud.com/app1/index.html
3. App1 Metadata Page: http://apps.devopsincloud.com/app1/metadata.html
4. App2 Landing Page: http://apps.devopsincloud.com/app2/index.html
5. App2 Metadata Page: http://apps.devopsincloud.com/app2/metadata.html
Step-12: Clean-Up
# Terraform Destroy
terraform destroy -auto-approve
# Delete files
rm -rf .terraform*
rm -rf terraform.tfstate*
References