Building ElasticBeanstalk using CloudFormation

The purpose of this blog post is provide the reader an understanding of how to build an ElasticBeanstalk application utilizing a CloudFormation Stack. In particular, the post describes:

  • the resources required in a CloudFormation stack, including the
    • AWS::ElasticBeanstalk::Application Resource
    • AWS::ElasticBeanstalk::Environment Resource
  • the relationship between these resources

After reading this blog post you should be able to build an ElasticBeanstalk application with multiple environments using a CloudFormation file. Subsequent posts will describe methods of deploying code to the ElasticBeanstalk application.

Resources required to support an ElasticBeanstalk Application

Prerequisite Resources

An ElasticBeanstalk application will require some underlying infrastructure such as a VPC, Subnet and, presumably, Internet Gateway, NAT Gateway, Route Tables and Route Table Associations.

Required Resources

AWS::ElasticBeanstalk::Application

The actual ElasticBeanstalk application. An Application serves as a container for Environments and Application Versions.

ElasticBeanstalkApplication:
  Type: AWS::ElasticBeanstalk::Application
  Properties:
    ApplicationName: !Ref AWS::StackName

The screenshot below shows the ElasticBeanstalk Application created by a AWS::ElasticBeanstalk::Application resource.

ElasticBeanstalk - MultipleEnvs - Application.png

AWS::ElasticBeanstalk::Environment

An “Environment” is a subset of the ElasticBeanstalk application. The “Environments” are shown in the AWS Console as parts of an application. For each “Environment” AWS will launch a CloudFormation stack containing components (typically an Auto Scaling Group, ) required to run your application.

ElasticBeanstalkEnvironment:
  Type: AWS::ElasticBeanstalk::Environment
  Properties:
    ApplicationName: !Ref ElasticBeanstalkApplication
    TemplateName: !Ref ElasticBeanstalkConfigurationTemplate

For a given application you will likely have a “Prod” environment or a “QA” environment. In the image below, a CloudFormation file containing two “AWS::ElasticBeanstalk::Environment” resources is used to construct “QA” and “Prod” environments – each of these Environments can have unique configurations and utilize different versions of a codebase.

ElasticBeanstalk - MultipleEnvs - Environments Highlight.png

AWS::ElasticBeanstalk::ConfigurationTemplate

A “Configuration Template” is used to specify the resources required to build an Environment as well as the configuration of these resources. These configuration options include things such as:

  • the “Solution Stack” (where Solution Stack determines what type of AMI will be used to run a given application – for instance, an AMI that contains php, ruby, python or docker)
  • if the Application will utilize an ElasticLoad Balancer
  • the Min Size and Max Size of the Auto Scaling Group supporting the Elastic Beanstalk application if the environment utilizes Auto Scaling
  • the VPC in which an ElasticBeanstalk application should reside if the application resides in a VPC

The full list of “OptionSettings” are available are available here: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html. An example Configuration Template is below:

ElasticBeanstalkProdConfigurationTemplate:
  Type: AWS::ElasticBeanstalk::ConfigurationTemplate
  Properties:
    ApplicationName: !Ref ElasticBeanstalkApplication
      OptionSettings:
        - Namespace: aws:autoscaling:asg
          OptionName: MinSize
          Value: 2
        - Namespace: aws:autoscaling:asg
          OptionName: MaxSize
          Value: 2
        - Namespace: aws:autoscaling:launchconfiguration
          OptionName: InstanceType
          Value: t2.micro
        - Namespace: aws:elasticbeanstalk:environment
          OptionName: EnvironmentType
          Value: LoadBalanced
        - Namespace: aws:ec2:vpc
          OptionName: VPCId
          Value: !Ref VPC
        - Namespace: aws:ec2:vpc
          OptionName: Subnets
          # Value: !Join turns the individual subnets into a string
          Value: !Join [ ",", [ !Ref PublicSubnet01, !Ref PublicSubnet02 ] ]
        - Namespace: aws:ec2:vpc
          OptionName: AssociatePublicIpAddress
          Value: true
          SolutionStackName: 64bit Amazon Linux 2018.03 v2.8.4 running PHP 5.6

Link to CloudFormation File

The stack that I used to aid understanding the use of CloudFormation to create AWS ElasticBeanstalk applications is available here: https://github.com/cloudavail/snippets/tree/master/aws/elasticbeanstalk/elasticbeanstalk_with_multiple_envs

Conclusion

If you have any questions about this particular blog post please feel free to post a question below or email blog@cloudavail.com.

Using AWS CloudFormation’s Transform Function

Why use CloudFormation’s Transform Function?

There are two good reasons for using CloudFormation’s “Transform” function to include files. These two reasons are described below:

  1. Consistency.
    1. By including a snippet in each and every CloudFormation template – you’ll ensure that the included code is the same, stack to stack.
  2. Code reuse.
    1. You won’t need to update code across multiple stacks when you need to make changes. You will need to update stacks to get changes made to the included files – but you won’t have to update the actual code in each stack.

How to do this?

Creating a CloudFormation File that uses an Include.

You need to include a Fn::Transform statement where the given file is to be included. An example included is below:

Fn::Transform:
  Name: AWS::Include
  Parameters:
    Location : s3://187376578462-fn-transform-include/ubuntu_ami.yaml

An example of an include in the “Mappings” section of a CloudFormation template would look like:

Mappings:
  Fn::Transform:
    Name: AWS::Include
    Parameters:
      Location : s3://187376578462-fn-transform-include/ubuntu_ami.yaml

Lastly, here is a screenshot of a CloudFormation file that uses an include – see line 29.

CloudFormation - Fn Transform
CloudFormation template that utilizes a Transform function to include a file.

Creating the Included File

You will need to create a file that will be included in a given CloudFormation stack. This file is going to be inserted where the Fn::Transform statement is – this is akin to “import” or “include” in a programming language or “catting” two files together in a *nix Operating System.

The included file should look akin to the following:

AWSRegionArch2AMI:
  us-east-1:
    '64': ami-ddf13fb0
  us-west-1:
    '64': ami-b20542d2
  us-west-2:
    '64': ami-b9ff39d9
CloudFormation - File to be Included
File to be included in a CloudFormation template.

Uploading the Included File

The file that _will be_ included needs to be uploaded to S3. You can do this using the aws s3 command – see below:

aws s3 cp ubuntu_ami.yaml s3://$ubuntu_ami_file_s3_path --region us-west-2
CloudFormation - Included File Upload
AWS S3 command uploading a file to be included in a CloudFormation template.

Creating the CloudFormation Stack with an Include

You’ll need to use the “aws cloudformation deploy” command to deploy or update the given template. An example is below:

aws cloudformation deploy --stack-name FunctionTransformInclude --template-file autoscaling_with_yaml_userdata.yaml --parameter-overrides ubuntuAMISMapping3Location=s3://$ubuntu_ami_file_s3_path --region us-west-2
CloudFormation - Fn Transform Launch Stack
AWS CloudFormation “Deploy” command creating a CloudFormation stack

Summary

I’m planning on using for AMI mappings in particular, as well as for including sections of CloudFormation that might be better generated using code (for instance, user-data might be a consideration). I’ve yet to consider the use of “Fn::Transform / Include” to improve the security of stacks by removing passwords.

If you have questions or comments – reach me at colin@cloudavail.com.