This tutorial aims to take the reader through creating an Application Load balancer and its dependencies using CloudFormation. The template will create:
- The Application Load Balancer
- The Target Groups
- The Listeners
- The Listener Rules
The AWS cloud platform provides managed load balancers using the Elastic Load Balancer service. One has options to create an Application (layer7), Network (layer 4), or Classic Load Balancer (both layer 4 and 7). For this tutorial, we will create an Application Load balancer.
A load balancer is useful because:
It gives our applications high availability. Using a load balancer in our applications enables one to route traffic to multiple backend servers. In case one server fails, traffic will be routed to the other servers. This is also good for ensuring even distribution of traffic between the various servers.
It is also useful for directing/filtering traffic based on the rules configured. Using host-based, path-based routing, or a combination of both, one can direct traffic to different servers.
Setup Prerequisites
The user will need to have:
- An AWS Account.
- A user with permissions to create resources on AWS.
- Generated a certificate for their specific domain name (We used AWS Certificate Manager to generate ours).
- An IDE to write and edit your CloudFormation Template.
N/B: A user can import certificates generated from other certificate generation entities to the AWS certificate manager and use them.
Step 1: Create CloudFormation Template
Use the below code for your CloudFormation template. The user can edit the various parts of the template as explained in the next section.
AWSTemplateFormatVersion: "2010-09-09"
Description: "Create ALB, ALB security group, target groups, listeners and listener rules"
Parameters:
VPC:
Type: String
Description: The vpc to launch the service
Default: vpc-ID
PublicSubnet1:
Type: String
Description: The subnet where to launch the service
Default: subnet-ID
PublicSubnet2:
Type: String
Description: the subnet where to Launch the service
Default: subnet-ID
Resources:
ALBSecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: "security group for ALB"
GroupName: "test-ALB-SG"
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Environment"
Value: "test"
-
Key: "Name"
Value: "test-ALB-SG"
VpcId: !Ref VPC
SecurityGroupIngress:
-
CidrIp: "0.0.0.0/0"
FromPort: 80
IpProtocol: "tcp"
ToPort: 80
-
CidrIp: "0.0.0.0/0"
FromPort: 443
IpProtocol: "tcp"
ToPort: 443
ApplicationLoadBalancer:
Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
Properties:
Name: "test-Application-Load-Balancer"
Scheme: "internet-facing"
Type: "application"
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
SecurityGroups:
- !Ref ALBSecurityGroup
IpAddressType: "ipv4"
LoadBalancerAttributes:
-
Key: "access_logs.s3.enabled"
Value: "false"
-
Key: "idle_timeout.timeout_seconds"
Value: "60"
-
Key: "deletion_protection.enabled"
Value: "false"
-
Key: "routing.http2.enabled"
Value: "true"
-
Key: "routing.http.drop_invalid_header_fields.enabled"
Value: "false"
HTTPSListener:
Type: "AWS::ElasticLoadBalancingV2::Listener"
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 443
Protocol: "HTTPS"
SslPolicy: "ELBSecurityPolicy-2016-08"
Certificates:
-
CertificateArn: arn:aws:acm:eu-central-1:**************:certificate/*********************
DefaultActions:
-
Order: 1
TargetGroupArn: !Ref Test1TargetGroup
Type: "forward"
HTTPListener:
Type: "AWS::ElasticLoadBalancingV2::Listener"
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 80
Protocol: "HTTP"
DefaultActions:
-
Order: 1
RedirectConfig:
Protocol: "HTTPS"
Port: "443"
Host: "#{host}"
Path: "/#{path}"
Query: "#{query}"
StatusCode: "HTTP_301"
Type: "redirect"
Test1TargetGroup:
Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
Port: 80
Protocol: "HTTP"
HealthCheckPort: "traffic-port"
HealthCheckProtocol: "HTTP"
HealthCheckTimeoutSeconds: 5
UnhealthyThresholdCount: 2
TargetType: "instance"
Matcher:
HttpCode: "200"
HealthyThresholdCount: 5
VpcId: !Ref VPC
Name: "target-group-1"
HealthCheckEnabled: true
TargetGroupAttributes:
-
Key: "stickiness.enabled"
Value: "false"
-
Key: "deregistration_delay.timeout_seconds"
Value: "300"
-
Key: "stickiness.type"
Value: "lb_cookie"
-
Key: "stickiness.lb_cookie.duration_seconds"
Value: "86400"
-
Key: "slow_start.duration_seconds"
Value: "0"
-
Key: "load_balancing.algorithm.type"
Value: "round_robin"
Test2TargetGroup:
Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
Port: 80
Protocol: "HTTP"
HealthCheckPort: "traffic-port"
HealthCheckProtocol: "HTTP"
HealthCheckTimeoutSeconds: 5
UnhealthyThresholdCount: 2
TargetType: "instance"
Matcher:
HttpCode: "200"
HealthyThresholdCount: 5
VpcId: !Ref VPC
Name: "target-group-2"
HealthCheckEnabled: true
TargetGroupAttributes:
-
Key: "stickiness.enabled"
Value: "false"
-
Key: "deregistration_delay.timeout_seconds"
Value: "300"
-
Key: "stickiness.type"
Value: "lb_cookie"
-
Key: "stickiness.lb_cookie.duration_seconds"
Value: "86400"
-
Key: "slow_start.duration_seconds"
Value: "0"
-
Key: "load_balancing.algorithm.type"
Value: "round_robin"
TestListenerRule1:
Type: "AWS::ElasticLoadBalancingV2::ListenerRule"
Properties:
Priority: "1"
ListenerArn: !Ref HTTPSListener
Conditions:
-
Field: "host-header"
Values:
- "test1.blog.avrcr.com"
Actions:
-
Type: "forward"
TargetGroupArn: !Ref Test1TargetGroup
Order: 1
ForwardConfig:
TargetGroups:
-
TargetGroupArn: !Ref Test1TargetGroup
Weight: 1
TargetGroupStickinessConfig:
Enabled: false
TestListenerRule2:
Type: "AWS::ElasticLoadBalancingV2::ListenerRule"
Properties:
Priority: "2"
ListenerArn: !Ref HTTPSListener
Conditions:
-
Field: "host-header"
Values:
- "test2.blog.com"
Actions:
-
Type: "forward"
TargetGroupArn: !Ref Test2TargetGroup
Order: 1
ForwardConfig:
TargetGroups:
-
TargetGroupArn: !Ref Test2TargetGroup
Weight: 1
TargetGroupStickinessConfig:
Enabled: false
Outputs:
ALB:
Description: The created loadbalancer
Value: !Ref ApplicationLoadBalancer
TargetGroup1:
Description: The created TargetGroup 1
Value: !Ref Test1TargetGroup
TargetGroup2:
Description: The created TargetGroup 2
Value: !Ref Test2TargetGroup
LoadBalancerSecurityGroup:
Description: the securty group for the ALB
Value: !Ref ALBSecurityGroup
The CloudFormation Template Explained
The template contains 3 sections. Parameters, Resources and Outputs.
Parameters Section
In the parameters section, the user inputs their dynamic variables. On our template, the user should customize the template by inputting their VPC and Subnets IDs. In our case, the load balancer is internet-facing hence the need to have it created on public subnets. Should the user want to create an internal load balancer, it would be prudent to have it created on private subnets.
Resources Section
The resources section allows the user to define the AWS resources they will create.
On our template, we start by creating the load balancer security group. The security group creates allows inbound traffic from port 80 and 443. The user can also customize or add more rules to the security group.
Next, the template creates a load balancer. The user can customize the name of the load balancer, the scheme, or whether it will be internal or internet-facing. If internal, kindly ensure you change the subnets from public to private. They can also customize the load balancer attributes as per their specific needs.
Since we have opened port 80 and 443 on the load balancer security group, the template creates the two listeners for the load balancer. One listener on port 80 (HTTP) and the other on port 443 (HTTPS). Then for the HTTP listener, the template is configured to create a default action to forward all requests to the HTTPS listener by default. This can also be customizable as per the user’s needs.
For the HTTPS listener, we have included the certificate we generated for our domain name. The user should change the certificate ARN to their own certificate ARN ID. Also, we created a default action to forward traffic to a target group. This will forward all traffic otherwise not routed to the target group. Again, the user can customize the HTTPS Listener default actions to their specific needs.
Next, the template creates two target groups. The user can customize the target groups’ properties as per their specific needs. Important to note is that the target type can either be an instance (EC2) or an IP.
Finally, the template creates listener rules. For our case based on host-based routing, we created listener rules that forward to the specific target groups. The user has the option to customize the rules. Also, they can change from host-based to path-based routing.
Outputs Section
The outputs section outputs the names of the resources you created.
Step 2: Create CodePipeline to Deploy the Template
Create the CodePipeline role to deploy template to CloudFormation. Use the below cloudformation template to create the role.
AWSTemplateFormatVersion: "2010-09-09"
Description: "Template to create centos ec2 instance and install ssm on it"
Resources:
IAMInstanceRole:
Type: 'AWS::IAM::Role'
Properties:
Description: The SSM Instance Profile
RoleName: codepipeline-test
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- cloudformation.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCloudFormationFullAccess
- arn:aws:iam::aws:policy/CloudWatchFullAccess
- arn:aws:iam::aws:policy/AmazonEC2FullAccess
Tags:
-
Key: "Project"
Value: "test-blog"
-
Key: "Environment"
Value: "test"
-
Key: "createdBy"
Value: "Maureen Barasa"
-
Key: "Name"
Value: "codepipeline-test"
IAMInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: codepipeline-test
Roles:
- !Ref IAMInstanceRole
Outputs:
Profile:
Description: The created Instance Profile
Value: !Ref IAMInstanceProfile
Role:
Description: The created role
Value: !Ref IAMInstanceRole
Next, go to the CodeCommit console. Then create a code commit repository. Commit your alb template to the repository.
On the CodePipeline console, select create pipeline.
After, choose pipeline settings. For service role, opt to create a new service role.
Also, under Advanced Settings choose the S3 bucket that you will use to store your artifacts. For the encryption key choose the default AWS key. Then click next.
On the add source stage screen, choose code commit as your source provider.
Enter the details of your CodeCommit repository name and the branch. Also, for change detection leave the setting to Amazon CloudWatch Events. This enables CloudWatch to detect changes made on your code and auto-start the pipeline to update those changes. When done click next.
On the add build stage screen, click skip build stage.
Finally, on the add deploy stage screen, select CloudFormation as your deployment option.
Fill in the details for your CloudFormation deployment. N/B: for the role, use the role you created with the CloudFormation template at the beginning of the section.
The next stage allows the user to review all the configurations done. If all configurations are correct click on create pipeline.
You have now created your first pipeline to deploy a CloudFormation template.
Important Links
- https://aws.amazon.com/elasticloadbalancing/
- https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html
More guides on AWS:
- Setup AWS RDS MySQL DB Instance with CloudFormation
- Create Amazon DocumentDB (MongoDB) Database on AWS With CloudFormation
- Setup Elasticsearch Cluster with Kibana on AWS
- Grant Developers Access to EKS Kubernetes Cluster
Happy Building!!!!