In Building Continuous Deployment on AWS with AWS CodePipeline, Jenkins and AWS Elastic Beanstalk, AWS describes how to manually configure CodePipeline to deploy an Elastic Beanstalk application. In this post, after describing how to create and connect to a new CodeCommit repository, I’ll explain how to fully automate the provisioning of all of the AWS resources in CloudFormation to achieve Continuous Delivery for a Node.js application in Elastic Beanstalk. This includes CloudFormation, CodeCommit, CodePipeline, Elastic Beanstalk, and IAM using a sample Node.js provided by AWS.
AWS Elastic Beanstalk is an easy-to-use service for deploying and scaling web applications and services developed with Java, .NET, PHP, Node.js, Python, Ruby, Go, and Docker on familiar servers such as Apache, Nginx, Passenger, and IIS.[1]  

codepipeline_beanstalkCreate and Connect to a CodeCommit Repository

Follow these instructions for creating and connecting to an AWS CodeCommit repository: Create and Connect to an AWS CodeCommit Repository. Take note of the repository name as you’ll be using it as a CloudFormation user parameter later.

Create an Elastic Beanstalk Stack

  • Go to the AWS Elastic Beanstalk Console and click Create New Application.
  • Enter your Application name and Description
  • Click Create web server and choose Next
  • For this walkthrough, select Node.js as your platform from Predefined configuration and use the default Environment type (Load balancing, auto scaling) and choose Next
  • For the Source option of the Application Version section, choose S3 URL and assuming you’re in the us-east-1 region, enter https://elasticbeanstalk-samples-us-east-1.s3.amazonaws.com/nodejs-sample.zip. Leave the defaults for the Deployment Limits option and choose Next. If you’re in a different region, modify the URL as appropriate.
  • Choose Next from the Environment Information page.
  • Choose Next from the Additional Resources page
  • Leave the default options on the Configuration Details page and choose Next
  • Leave the default options on the Environment Tags page and choose Next
  • Leave the default options on the Permissions page and choose Next
  • From the Review page, choose Launch

You’ll wait about 5-10 minutes for the application to launch into an environment. Once it’s successfully complete, your Elastic Beanstalk console will look similar the one below.
 
elastic_beanstalk_manual

Get the Beanstalk Configuration

From a client on which you’ve installed and configured the AWS CLI, type the following command (replacing ENVIRONMENT and APPLICATION with your Elastic Beanstalk environment and application names):

aws elasticbeanstalk describe-configuration-settings --environment-name ENVIRONMENT
--application-name APPLICATION > beanstalk.json

When you open the JSON file (or view the output if you didn’t pipe the contents to a file), you’ll get output similar to the following JSON fragment.

{
    "ConfigurationSettings": [
        {
            "ApplicationName": "EBPipeline-nodeApplication-1QOSJOIITHEZ6",
            "EnvironmentName": "EBPi-node-1UUQR8I2565GT",
            "Description": "AWS ElasticBeanstalk Sample Node Environment",
            "DeploymentStatus": "deployed",
            "DateCreated": "2016-05-05T15:41:07Z",
            "OptionSettings": [
                {
                    "OptionName": "Availability Zones",
                    "ResourceName": "AWSEBAutoScalingGroup",
                    "Namespace": "aws:autoscaling:asg",
                    "Value": "Any"
                },
                {
                    "OptionName": "Cooldown",
                    "ResourceName": "AWSEBAutoScalingGroup",
                    "Namespace": "aws:autoscaling:asg",
                    "Value": "360"
                },...

There are several default values that get automatically configured in every Elastic Beanstalk stack. Later in the Configuration Template section, you’ll see that I chose to specify the configuration for only three items: MinSize and MaxSize on the Auto Scaling Group and that I’m using a load-balanced solution with ELB.

Create a CloudFormation Template based on the Beanstalk Configuration

In this solution, the CloudFormation template is composed of several components. It includes an Elastic Beanstalk application, application version, and configuration template. “Under the hood”, Elastic Beanstalk uses CloudFormation to launch its resources. The solution also creates an IAM role and policy for the CodePipeline pipeline stages and actions – including the inclusion of CodeCommit. Finally, in the CloudFormation template, the pipeline is configured to use CodeCommit for its Source stage and actions. An illustration of this architecture is shown in the figure below.
codepipeline_eb_arch_2Each of the key components of this CloudFormation template is described in greater detail in the following sections.

Application

The application is the starting point for the rest of the solution. All you do here is define the name and description for the other components to have a namespace to apply their configuration.

    "nodeApplication":{
      "Type":"AWS::ElasticBeanstalk::Application",
      "Properties":{
        "Description":"AWS Elastic Beanstalk Sample Application"
      }
    },

Application Version

In the snippet below, you see that I’m defining an application version. A required property is the SourceBundle. Currently, SourceBundle requires that we use an S3 location. This might be a little confusing as we’re attempting to use CodeCommit as our “source of truth” with the pipeline. Rest assured, the solution still treats CodeCommit as the source of truth in the pipeline, but using S3 is a current workaround for the initial definition. Keep in mind that the integration between CodeCommit and CodePipeline was just released a few weeks ago – at the time of this writing. Basically, the Beanstalk application version uses S3 once and then CodePipeline uses CodeCommit for any changes thereafter.
In the example below, and if you’re running from the Northern Virginia region, the S3 Bucket and Key translate to https://elasticbeanstalk-samples-us-east-1.s3.amazonaws.com/nodejs-sample.zip.

    "nodeApplicationVersion":{
      "Type":"AWS::ElasticBeanstalk::ApplicationVersion",
      "Properties":{
        "ApplicationName":{
          "Ref":"nodeApplication"
        },
        "Description":"AWS ElasticBeanstalk Sample Application Version",
        "SourceBundle":{
          "S3Bucket":{
            "Fn::Join":[
              "-",
              [
                "elasticbeanstalk-samples",
                {
                  "Ref":"AWS::Region"
                }
              ]
            ]
          },
          "S3Key":"nodejs-sample.zip"
        }
      }
    },

Configuration Template

The Configuration Template snippet below defines the configuration of the Elastic Beanstalk stack. There are only three in this example, but Beanstalk provides many types of configuration options that you can add to your template. The most relevant part of this example is the SolutionStackName as this tells Beanstalk what kind of application it’s going to define and deploy. In this case, we’re using Node.js on Linux. You can find a list of Elastic Beanstalk’s supported platforms at Supported Platforms. The solution names are very odd as they require you to define a long, convoluted name that includes spaces, upper and lower case characters, dots and version numbers so be sure to copy, paste, and replace with your preferred and supported versions.

    "nodeConfigurationTemplate":{
      "Type":"AWS::ElasticBeanstalk::ConfigurationTemplate",
      "Properties":{
        "ApplicationName":{
          "Ref":"nodeApplication"
        },
        "Description":"AWS ElasticBeanstalk Sample Configuration Template",
        "OptionSettings":[
          {
            "Namespace":"aws:autoscaling:asg",
            "OptionName":"MinSize",
            "Value":"2"
          },
          {
            "Namespace":"aws:autoscaling:asg",
            "OptionName":"MaxSize",
            "Value":"6"
          },
          {
            "Namespace":"aws:elasticbeanstalk:environment",
            "OptionName":"EnvironmentType",
            "Value":"LoadBalanced"
          }
        ],
        "SolutionStackName":"64bit Amazon Linux 2015.09 v2.0.5 running Node.js"
      }
    },

Environment

The Elastic Beanstalk environment type is basically a collection of other resources in a “container” containing application, version and configuration resources. The application is deployed onto an Elastic Beanstalk environment which consists of 1-n EC2 instances configured based on your configuration.

    "nodeEnvironment":{
      "Type":"AWS::ElasticBeanstalk::Environment",
      "DependsOn":[
        "nodeApplication",
        "nodeConfigurationTemplate",
        "nodeApplicationVersion"
      ],
      "Properties":{
        "ApplicationName":{
          "Ref":"nodeApplication"
        },
        "Description":"AWS ElasticBeanstalk Sample Node Environment",
        "TemplateName":{
          "Ref":"nodeConfigurationTemplate"
        },
        "VersionLabel":{
          "Ref":"nodeApplicationVersion"
        }
      }
    },

CodePipeline

To get started with CodePipeline, you can manually create a two-stage pipeline and use CodeCommit as your Source and Elastic Beanstalk as your deployment provider. For more information, see Create a Pipeline using the AWS CodePipeline Console. The basic steps are:

  1. Go to the CodePipeline Console
  2. Select Create pipeline.
  3. Enter a Pipeline name.
  4. Choose AWS CodeCommit as the Source provider
    1. Choose a Repository name and Branch name from the repository you created in the Create and Connect to a CodeCommit Repository earlier in this post
  5. Select Next step.
  6. Choose No Build as the Build provider and select Next step.
  7. Choose AWS Elastic Beanstalk as the Deployment provider and enter the Application name and Environment name from the one you manually created before and select Next step.
  8. Choose a Role name.
  9. Click Create pipeline.

Once your pipeline successfully runs, go to your AWS CLI and type the following (replacing MYPIPELINE with the name of your pipeline).

aws codepipeline get-pipeline --name MYPIPELINE > MYPIPELINE.json

You’ll then use the output of the contents in the JSON file to create your CodePipeline stack in CloudFormation as shown in the snippet below. After copying the contents, be sure to update your template to use title case vs. camel case for some of the attribute names in order to conform to the CloudFormation DSL.

"Stages":[
          {
            "Name":"Source",
            "Actions":[
              {
                "InputArtifacts":[
                ],
                "Name":"Source",
                "ActionTypeId":{
                  "Category":"Source",
                  "Owner":"AWS",
                  "Version":"1",
                  "Provider":"CodeCommit"
                },
                "OutputArtifacts":[
                  {
                    "Name":"MyApp"
                  }
                ],
                "Configuration":{
                  "BranchName":{
                    "Ref":"RepositoryBranch"
                  },
                  "RepositoryName":{
                    "Ref":"RepositoryName"
                  }
                },
                "RunOrder":1
              }
            ]
          },
          {
            "Name":"Beta",
            "Actions":[
              {
                "InputArtifacts":[
                  {
                    "Name":"MyApp"
                  }
                ],
                "Name":"EbApp",
                "ActionTypeId":{
                  "Category":"Deploy",
                  "Owner":"AWS",
                  "Version":"1",
                  "Provider":"ElasticBeanstalk"
                },
                "OutputArtifacts":[
                ],
                "Configuration":{
                  "ApplicationName":{
                    "Ref":"nodeApplication"
                  },
                  "EnvironmentName":{
                    "Ref":"nodeEnvironment"
                  }
                },
                "RunOrder":1
              }
            ]
          }
        ],

Launch the Stack

To launch the CloudFormation stack, simply click the button below to open the template from the CloudFormation console in your AWS account. You’ll need to enter a value for the CodeCommit Repository Name, and, optionally, CodeCommit Repository Branch. You’ll be charged for the use of CodePipeline, EC2, S3, and other AWS resources.

codepipeline_beanstalk_cfn.jpg
To launch the same stack from your AWS CLI, type the following (while modifying the same parameter value described above):

aws cloudformation create-stack --stack-name EBPipelineStack
--template-url https://s3.amazonaws.com/stelligent-training-public/public/codepipeline/codepipeline-eb.json
--region us-east-1 --disable-rollback --capabilities="CAPABILITY_IAM"
--parameters ParameterKey=RepositoryName,ParameterValue=YOURCODECOMMITREPONAME
ParameterKey=RepositoryBranch,ParameterValue=master

Outputs

Once the CloudFormation stack successfully launches, there are several outputs but the two most relevant are AppURL and CodePipelineURL. You can click on the AppURL value to launch the Elastic Beanstalk application from the ELB endpoint. The CodePipelineURL output value launches the generated pipeline from the CodePipeline console. See the screenshot below.
codepipeline_beanstalk_cfn_outputs   

Access the Application

By clicking on the AppURL Output value in CloudFormation, you’ll launch Node.js application deployed via Elastic Beanstalk. You should see a page like the one below.
eb_app_before.jpg

Commit Changes to CodeCommit

Make some visual changes to the code and commit these changes to your CodeCommit repository to see them get deployed through your pipeline. You perform these actions from the directory where you cloned a local version of your CodeCommit repo (in the directory created by your git clone command). Some example command-line operations are shown below.

git commit -am "change color to red"
git push

Once you push these changes to the CodeCommit git repo, CodePipeline will discover these changes and kickoff the pipeline to deploy your changes. Once complete, you should see your changes have been applied to the application – similar to what you see in the figure below.
eb_app_after

Summary

In this post, you learned how to use CloudFormation to fully automate the provisioning of an Elastic Beanstalk stack along with a CodePipeline pipeline that uses CodeCommit as its version-control repository so that whenever a change is made to the Git repo, the changes are automatically applied to your Elastic Beanstalk application. By using a pipeline, you can apply Continuous Delivery so that it runs through all the tests and other checks to enable you to deliver changes to the production whenever there’s a business need to do so.

Sample Code

The code for the examples demonstrated in this post are located at https://github.com/stelligent/stelligent_commons/blob/master/cloudformation/eb/codepipeline-eb.json. Let us know if you have any comments or questions @stelligent or @paulduvall.
Stelligent is hiring! Do you enjoy working on complex problems like figuring out ways to automate all the things as part of a deployment pipeline? Do you believe in the “everything-as-code” mantra? If your skills and interests lie at the intersection of DevOps automation and the AWS cloud, check out the careers page on our website.

Resources

Stelligent Amazon Pollycast
Voiced by Amazon Polly