{"id":3720,"date":"2023-04-22T18:03:03","date_gmt":"2023-04-22T18:03:03","guid":{"rendered":"https:\/\/geekmungus.co.uk\/?p=3720"},"modified":"2023-04-22T18:03:03","modified_gmt":"2023-04-22T18:03:03","slug":"example-aws-application-load-balancer-alb","status":"publish","type":"post","link":"https:\/\/geekmungus.co.uk\/?p=3720","title":{"rendered":"Example AWS Application Load Balancer (ALB)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The AWS application load balancer is a key infrastructure component for providing access to your application to the Internet, or even for use within your VPC between components.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this example we&#8217;ll create a VPC, then within deploy 2 private and 2 public subnets, we&#8217;ll then attach an Internet Gateway, a NAT gateway (to allow the private subnet instances to reach the Internet) and finally deploy two EC2 instances that sit behind the Application Load Balancer and have the example application published.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can find the AWS CloudFormation template here:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/tristanhself\/general\/blob\/67ce1f2922ad6893ab0cf6941f346b231fec36e3\/aws\/alb\/alb-v2.yaml\">https:\/\/github.com\/tristanhself\/general\/blob\/67ce1f2922ad6893ab0cf6941f346b231fec36e3\/aws\/alb\/alb-v2.yaml<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s step over some of the key parts, but broken down. First, the VPC and subnets:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Resources:\n  VPC:\n    Type: AWS::EC2::VPC\n    Properties:\n      EnableDnsSupport: true\n      EnableDnsHostnames: true\n      CidrBlock: 192.168.0.0\/16\n      Tags:\n      - Key: Name\n        Value: !Sub \"${AWS::StackName}-VPC\"\n\n# Public and Private Subnets\n\n  PublicSubnet1:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: eu-west-2a\n      CidrBlock: 192.168.0.0\/24\n      MapPublicIpOnLaunch: true # Makes any instance launched within get a public IP address assigned, note this is different from the elastic IP.\n      Tags:\n      - Key: Name\n        Value: !Sub \"${AWS::StackName}-PublicSubnet-1\"\n\n  PublicSubnet2:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: eu-west-2b\n      CidrBlock: 192.168.100.0\/24\n      MapPublicIpOnLaunch: true # Makes any instance launched within get a public IP address assigned, note this is different from the elastic IP.\n      Tags:\n      - Key: Name\n        Value: !Sub \"${AWS::StackName}-PublicSubnet-2\"\n\n  PrivateSubnet1:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: eu-west-2a\n      CidrBlock: 192.168.1.0\/24\n      MapPublicIpOnLaunch: false # Makes any instance launched within get a public IP address assigned, note this is different from the elastic IP.\n      Tags:\n      - Key: Name\n        Value: !Sub \"${AWS::StackName}-PrivateSubnet-1\"\n\n  PrivateSubnet2:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      AvailabilityZone: eu-west-2b\n      CidrBlock: 192.168.101.0\/24\n      MapPublicIpOnLaunch: false # Makes any instance launched within get a public IP address assigned, note this is different from the elastic IP.\n      Tags:\n      - Key: Name\n        Value: !Sub \"${AWS::StackName}-PrivateSubnet-2\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">All quite straight forward, we create two private and two public subnets, notice the &#8220;MapPublicIPOnLaunch&#8221; attribute on the public subnet, this is set to &#8220;true&#8221; to ensure anything connected is given a globally routable IPv4 address at creation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next we deploy the Internet Gateway, we attach it to the VPC with a VPCGatewayAttachment, simple enough. The next parts are separate from this, the Elastic IP address is a static IP address created to be assigned to the NAT Gateway. A NAT Gateway you ask, well that is used to provide Internet access to any instances on the private subnets. By default only a public subnet gets Internet access, but in many cases you&#8217;ll want your instances on the private subnet to get access to the Internet if not just for updates, note though this access is one way, i.e. egress\/outbound only.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Internet Gateway and Attachment to VPC\n\n  InternetGateway:\n    Type: AWS::EC2::InternetGateway\n\n  VPCGatewayAttachment:\n    Type: AWS::EC2::VPCGatewayAttachment\n    Properties:\n      VpcId: !Ref VPC\n      InternetGatewayId: !Ref InternetGateway\n\n# Elastic IP Address for NAT Gateway\n  NatGWPublicIP:\n    Type: AWS::EC2::EIP\n    DependsOn: VPC\n    Properties:\n      Domain: vpc\n\n# NAT Gateway (to provide egress only Internet access from Private Subnets)\n  NatGateway:\n    Type: AWS::EC2::NatGateway\n    DependsOn: NatGWPublicIP\n    Properties: \n      SubnetId: !Ref PublicSubnet1\n      AllocationId: !GetAtt NatGWPublicIP.AllocationId\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Good stuff, now let&#8217;s look at the routing tables. We have a Public Route Table which is used by the public subnets, and a Private Route Table that is used by the private subnets. These Route Tables tell instances where they need to go to reach other resources and\/or the Internet. They are very similar, the only real difference in this example is the &#8220;GatewayId&#8221; and &#8220;NatGatewayId&#8221; between the public and private route tables.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Public Routing\n\n  PublicRouteTable:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n\n  PublicRoute:\n    Type: AWS::EC2::Route\n    Properties:\n      DestinationCidrBlock: 0.0.0.0\/0\n      GatewayId: !Ref InternetGateway\n      RouteTableId: !Ref PublicRouteTable\n\n  PublicSubnet1RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      SubnetId: !Ref PublicSubnet1\n\n  PublicSubnet2RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      SubnetId: !Ref PublicSubnet2\n\n# Private Routing\n\n  PrivateRouteTable:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n\n  PrivateRoute:\n    Type: AWS::EC2::Route\n    Properties:\n      DestinationCidrBlock: 0.0.0.0\/0\n      NatGatewayId: !Ref NatGateway\n      RouteTableId: !Ref PrivateRouteTable\n\n  PrivateSubnet1RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable\n      SubnetId: !Ref PrivateSubnet1\n\n  PrivateSubnet2RouteTableAssociation:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable\n      SubnetId: !Ref PrivateSubnet2\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now we are getting to the juicy bits, the Application Load Balancer (excuse the ELB name, that was left in by accident). So we create a Security Group that defines what traffic is allowed into the ALB, in this case we are allowing port 80 (HTTP) from anywhere (0.0.0.0\/0). Then we define the Application Load Balancer itself, and bind it to the two Public Subnets we created, i.e. there will be an endpoint in each of these which gets an IPv4 address (essentially) so it can be reached, and finally binds itself to the Security Group we just created.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    # ELB Security Group allowing Port 80 from anywhere\n  ELBSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n        GroupDescription: 'Allow inbound HTTP access to Application Load Balancer'\n        VpcId:\n          Ref: VPC\n        SecurityGroupIngress:\n            - IpProtocol: tcp\n              FromPort: 80\n              ToPort: 80\n              CidrIp: 0.0.0.0\/0\n\n  ApplicationLoadBalancer:\n    Type: AWS::ElasticLoadBalancingV2::LoadBalancer\n    Properties:\n      Scheme: internet-facing # or internal\n      Subnets:\n      - Ref: PublicSubnet1\n      - Ref: PublicSubnet2\n      SecurityGroups:\n      - Ref: ELBSecurityGroup\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Okay, now we need to create a Listener, the ALB is essentially a placeholder for other components, in this case the Listener and Target Group. The Listener is the externally facing part of the ALB, its what listens for client connections (in this example out on the Internet) and in this example port 80 (HTTP). The other part is the Target Group, the target group is to what the Listener will send the traffic, in our case we want it to go to our two EC2 instances (which we will create in a moment).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> EC2TargetGroup:\n    Type: AWS::ElasticLoadBalancingV2::TargetGroup\n    Properties:\n      HealthCheckIntervalSeconds: 30\n      HealthCheckProtocol: HTTP\n      HealthCheckTimeoutSeconds: 15\n      HealthyThresholdCount: 5\n      Matcher:\n        HttpCode: '200'\n      Name: EC2TargetGroup\n      Port: 80\n      Protocol: HTTP\n      TargetGroupAttributes:\n      - Key: deregistration_delay.timeout_seconds\n        Value: '20'\n      Targets:\n      - Id: !Ref EC2Instance1\n      - Id: !Ref EC2Instance2\n      UnhealthyThresholdCount: 3\n      VpcId:\n        Ref: VPC\n      Tags:\n      - Key: Name\n        Value: EC2TargetGroup\n      - Key: Port\n        Value: 80\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Okay, now we define our two EC2 instances, along with a Security Group which restricts what can reach these instances. They&#8217;ll be deployed into the private subnet so they are not directly reachable from the Internet anyway, but in this case we add them to a security group which only allows traffic from the ALB Security Group we created a few steps back.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># eC2 Instance and security group\n\n  EC2SecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n        GroupDescription: 'Allow inbound HTTP access to EC2 instance.'\n        VpcId:\n          Ref: VPC\n\n  EC2SecurityGroupIngress: \n    Type: AWS::EC2::SecurityGroupIngress\n    Properties:\n      Description: \"Rule to allow connections to EC2 instance from the ELBSecurityGroup\"\n      GroupId: !Ref EC2SecurityGroup\n      SourceSecurityGroupId: !Ref ELBSecurityGroup\n      FromPort: 80\n      IpProtocol: tcp\n      ToPort: 80\n\n  EC2Instance1:\n    Type: AWS::EC2::Instance\n    Properties:\n      ImageId: ami-028a5cd4ffd2ee495 # This specifies a particular AMI Linux 2 image, youd probably want it to select a suitable from a pre-created list for each region.\n      InstanceType: t2.medium\n      IamInstanceProfile: !Ref Ec2SsmInstanceProfile\n      SubnetId: !Ref PrivateSubnet1\n      SecurityGroupIds: \n        - !Ref EC2SecurityGroup\n      Tags:\n        - Key: Name\n          Value: !Sub \"${AWS::StackName}-EC2Instance-1\"\n      UserData:\n        Fn::Base64: !Sub |\n          #!\/bin\/bash\n          echo \"---- UserData Start ----\"\n          apt update\n          apt install -y apache2\n          echo \"Hello World from $(hostname -f)\" > \/var\/www\/html\/index.html\n          echo \"---- UserData Complete ----\"\n\n  EC2Instance2:\n    Type: AWS::EC2::Instance\n    Properties:\n      ImageId: ami-028a5cd4ffd2ee495 # This specifies a particular AMI Linux 2 image, youd probably want it to select a suitable from a pre-created list for each region.\n      InstanceType: t2.medium\n      IamInstanceProfile: !Ref Ec2SsmInstanceProfile\n      SubnetId: !Ref PrivateSubnet2\n      SecurityGroupIds: \n        - !Ref EC2SecurityGroup\n      Tags:\n        - Key: Name\n          Value: !Sub \"${AWS::StackName}-EC2Instance-2\"\n      UserData:\n        Fn::Base64: !Sub |\n          #!\/bin\/bash\n          echo \"---- UserData Start ----\"\n          apt update\n          apt install -y apache2\n          echo \"Hello World from $(hostname -f)\" > \/var\/www\/html\/index.html\n          echo \"---- UserData Complete ----\"\n\n# The Outputs are bits of information that the Cloudformation template will output once it completes running.\n# For example if you are creating EC2 instances, this can output the IP addresses, auto-generated name and so on.\n\nOutputs:\n  VpcId:\n    Description: The VPC ID\n    Value: !Ref VPC\n\n  ALBHostName:\n      Description: 'Application Load Balancer Hostname'\n      Value:\n        !GetAtt ApplicationLoadBalancer.DNSName\n\n  ApplicationLoadBalancer:\n      Description: 'Application Load Balancer'\n      Value:\n        Ref: ApplicationLoadBalancer\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And finally lets look at the SSM Role, what this does is provide a role that the EC2 instances are part of which you can use access the EC2 instance console without use of SSH.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Declare Role\n# We declare the role, it will create it with an autogenerated name.\n\n  Ec2SsmIamRole:\n    Type: AWS::IAM::Role\n    Properties:\n      RoleName: !Sub \"${AWS::StackName}-Ec2SsmIamRole\"\n      AssumeRolePolicyDocument: \n        Statement:\n          - Effect: Allow\n            Principal:\n              Service: &#91;ec2.amazonaws.com]\n            Action: &#91;sts:AssumeRole]\n      Path: \/\n      ManagedPolicyArns:\n        - arn:aws:iam::aws:policy\/AmazonSSMManagedInstanceCore\n      Tags:\n        - Key: Name\n          Value: !Sub \"${AWS::StackName}-Ec2SsmIamRole\"\n\n  Ec2SsmInstanceProfile:\n    Type: AWS::IAM::InstanceProfile\n    Properties:\n      InstanceProfileName: !Sub \"${AWS::StackName}-Ec2SsmIamRole-InstanceProfileName\"\n      Path: \/\n      Roles: \n        - !Ref Ec2SsmIamRole\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s it! Now, I stepped through this config bit by bit, but in reality, you&#8217;d deploy the template in one fell swoop, with a command like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>aws cloudformation create-stack --template-body file:\/\/alb-v2.yaml --stack-name albtest  --capabilities CAPABILITY_NAMED_IAM<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In this case the stack-name is the name of the CloudFormation Stack that will be created into which your resources will go.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Conclusion<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hopefully this has given you an overview of deploying a simple Application Load Balancer deployment, you can add a listener for HTTPS which can use a certificate to secure connections from clients, you can also make the HTTP listener redirect to HTTPS automatically. There are also a number of other tweaks that you can make to change the behaviour of the load balancer, for example Sticky Sessions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/docs.aws.amazon.com\/AWSCloudFormation\/latest\/UserGuide\/quickref-elb.html\">https:\/\/docs.aws.amazon.com\/AWSCloudFormation\/latest\/UserGuide\/quickref-elb.html<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/unsplash.com\/@floriankrumm\" data-type=\"URL\" data-id=\"https:\/\/unsplash.com\/@floriankrumm\" target=\"_blank\" rel=\"noreferrer noopener\">Image Attribution<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The AWS application load balancer is a key infrastructure component for providing access to your application to the Internet, or even for use within your VPC between components. In this example we&#8217;ll create a VPC, then within deploy 2 private and 2 public subnets, we&#8217;ll then attach an Internet Gateway, a NAT gateway (to allow &#8230; <a title=\"Example AWS Application Load Balancer (ALB)\" class=\"read-more\" href=\"https:\/\/geekmungus.co.uk\/?p=3720\" aria-label=\"Read more about Example AWS Application Load Balancer (ALB)\">Read more<\/a><\/p>\n","protected":false},"author":4,"featured_media":1631,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,3],"tags":[],"class_list":["post-3720","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aws","category-cloud"],"_links":{"self":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3720","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3720"}],"version-history":[{"count":2,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3720\/revisions"}],"predecessor-version":[{"id":3722,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/3720\/revisions\/3722"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=\/wp\/v2\/media\/1631"}],"wp:attachment":[{"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3720"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3720"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/geekmungus.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3720"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}