Stelligent

Automating AWS CodeDeploy Provisioning in CloudFormation

Over the past few weeks, I’ve been describing how I’m automating the provisioning of several of the AWS Code Services including CodePipeline and Custom CodePipeline Actions. This time, I’m describing a way of provisioning AWS CodeDeploy in CloudFormation. For now, I’m doing the automation against a generic application provided by AWS. I’ll apply it to our Dromedary demo application later and publish it in a future post.

   About AWS CodeDeploy

As AWS describes: “AWS CodeDeploy is a service that automates code deployments to any instance, including Amazon EC2 instances and instances running on-premises. AWS CodeDeploy makes it easier for you to rapidly release new features, helps you avoid downtime during application deployment, and handles the complexity of updating your applications. You can use AWS CodeDeploy to automate software deployments, eliminating the need for error-prone manual operations, and the service scales with your infrastructure so you can easily deploy to one instance or thousands.” Below, I describe the core components that make up CodeDeploy.

In order for CodeDeploy to work, you need to install the CodeDeploy agent on each EC2 instance on which you’re running CodeDeploy.  CodeDeploy communicates with the agents via HTTPS over port 443. The agent contains code that has CodeDeploy domain-specific knowledge and uses the defined configuration to run through its lifecycle events. 

  Executing the CloudFormation Template

Below, you see an example of running the template that I’ve defined. You’ll need to define your own stack name and EC2KeyPairName. From your AWS CLI, run a command to launch the CloudFormation stack. You can also launch the same using the CloudFormation console.

aws cloudformation create-stack --stack-name MyStackName --template-url https://s3.amazonaws.com/stelligent-training-public/public/codedeploy/codedeploy-master.json
--region us-east-1 --disable-rollback --capabilities="CAPABILITY_IAM"
--parameters ParameterKey=S3Bucket,ParameterValue=aws-codedeploy-us-east-1
ParameterKey=S3Key,ParameterValue=public/Sample_Linux_App.zip
ParameterKey=EC2TagValue,ParameterValue=CodeDeployEC2Tag
ParameterKey=EC2KeyPairName,ParameterValue=MyEC2KeyPairName

It’ll take about 10-15 minutes to launch the stacks that launch the EC2 instance, install the CodeDeploy agent, configure and run a deployment of the application. You can visually verify the application works by going to the CodeDeploy console (shown in Figure 1), select an application, then a deployment and click on the link under the Instance ID column. From the EC2 console, find the Public IP and prepend http:// to it from your web browser.

Figure 1: AWS CodeDeploy deployment status

Architecture

There are two CloudFormation templates to define the EC2 instances, S3 Distribution Location, IAM and CodeDeploy resources along with its overall orchestration via nested stacks. They are:

The resulting infrastructure is illustrated in Figure 2.

Figure 2: Infrastructure Architecture Diagram

Implementation

There are several parts to automating the process of downloading the sample application, provisioning EC2 instances with the CodeDeploy agent, provisioning CodeDeploy to create an application and to run a deployment for that application using CodeDeploy. The first example snippet below can be found in the codedeploy-master.json CloudFormation template. This launches a new stack from the AWS CloudFormation template that provisions EC2 instances and installs the CodeDeploy agent.

    "CodeDeployEC2InstancesStack":{
      "Type":"AWS::CloudFormation::Stack",
      "Properties":{
        "TemplateURL":"http://s3.amazonaws.com/aws-codedeploy-us-east-1/templates/latest/CodeDeploy_SampleCF_Template.json",
        "TimeoutInMinutes":"60",
        "Parameters":{
          "TagValue":{
            "Ref":"EC2TagValue"
          },
          "KeyPairName":{
            "Ref":"EC2KeyPairName"
          }
        }
      }
    },

The next snippet from the same codedeploy-master.json template uses the EC2 tag value that was set in the first stack as a way for this template to determine on which EC2 instances it will run the CodeDeploy deployment. It also uses the DependsOn attribute to ensure that the stack has been created before attempting to launch this stack – since it requires resources from the first stack.

    "CodeDeploySimpleStack":{
      "Type":"AWS::CloudFormation::Stack",
      "DependsOn":"CodeDeployEC2InstancesStack",
      "Properties":{
        "TemplateURL":"https://s3.amazonaws.com/stelligent-training-public/public/codedeploy/codedeploy-deployment.json",
        "TimeoutInMinutes":"60",
        "Parameters":{
          "TagValue":{
            "Ref":"EC2TagValue"
          },
          "RoleArn":{
            "Fn::GetAtt":[
              "CodeDeployEC2InstancesStack",
              "Outputs.CodeDeployTrustRoleARN"
            ]
          },
          "Bucket":{
            "Ref":"S3Bucket"
          },
          "Key":{
            "Ref":"S3Key"
          }
        }
      }
    }

In the codedeploy-deployment.json template snippet below, I’m defining the CodeDeploy application. This generates a unique identifier that’s used in the next resource definition in the template.

    "MyApplication":{
      "Type":"AWS::CodeDeploy::Application"
    },

In the snippet below (also from codedeploy-deployment.json), I’m defining how my deployment will behave using the CodeDeploy Deployment Group. Once again, I use the DependsOn attribute to ensure that the CodeDeploy application exists prior to provisioning the deployment group since it needs to already exist. Then, I find the application bundle in S3 using the S3Location property. From the command-line execution snippet described earlier, you’ll see that I’m passing aws-codedeploy-us-east-1 as the S3 bucket parameter and samples/latest/SampleApp_Linux.zip as the S3 Key. This resolves to https://s3.amazonaws.com/aws-codedeploy-us-east-1/samples/latest/SampleApp_Linux.zip.

    "MyDeploymentGroup":{
      "Type":"AWS::CodeDeploy::DeploymentGroup",
      "DependsOn":"MyApplication",
      "Properties":{
        "ApplicationName":{
          "Ref":"MyApplication"
        },
        "Deployment":{
          "Description":"First time",
          "IgnoreApplicationStopFailures":"true",
          "Revision":{
            "RevisionType":"S3",
            "S3Location":{
              "Bucket":{
                "Ref":"Bucket"
              },
              "BundleType":"Zip",
              "Key":{
                "Ref":"Key"
              }
            }
          }
        },
        "Ec2TagFilters":[
          {
            "Key":{
              "Ref":"TagKey"
            },
            "Value":{
              "Ref":"TagValue"
            },
            "Type":"KEY_AND_VALUE"
          }
        ],
        "ServiceRoleArn":{
          "Ref":"RoleArn"
        }
      }

Troubleshooting

As you’re experimenting with CodeDeploy and automating the provisioning of CodeDeploy in CloudFormation, you’ll likely experience a few problems along the way. There were a couple of useful suggestions provided to me by AWS Support.

zip -r varlog.zip /var/log
zip -r deployment-root.zip /opt/codedeploy-agent/deployment-root

aws cloudformation create-stack --stack-name MyStackName --template-body file://mystack.json --region us-east-1 --disable-rollback

Feature Backlog

There’s some additional functionality that I plan to implement in the coming weeks as described below.

Useful Resources