You can support us by downloading this article as PDF from the Link below. Download the guide as PDF

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.

Create Pipeline

After, choose pipeline settings. For service role, opt to create a new service role.

Choose Pipeline Settings

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.

Advanced Settings

On the add source stage screen, choose code commit as your source provider.

Add Source Stage

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.

Add Source Stage Settings

On the add build stage screen, click skip build stage.

Skip Build Stage

Finally, on the add deploy stage screen, select CloudFormation as your deployment option.

Select CloudFormation as 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.

Add Deployment Stage Details

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.

A Successful Deployment Pipeline

More guides on AWS:

Happy Building!!!!

You can support us by downloading this article as PDF from the Link below. Download the guide as PDF