Deploying to AWS Elastic Beanstalk from Visual Studio Team Services

When I set this site up in Elastic Beanstalk in AWS, I was initially using CodeCommit and CodePipeline to manage releases. CodePipeline was super easy to pick up and use as a way to get started. I did want an excuse to get around some of the PowerShell cmdlets for managing AWS though. I came up with the idea of using Visual Studio Team Services for my build and releases. It was an excellent way for me to test out some PowerShell!

Overview

The process is relatively straightforward. You create a zip package for your build in the build phase. Then in the release phase, you start by adding that to your Elastic Beanstalk application. Then you can release that package into one or more environments in Elastic Beanstalk. The build output itself gets stored in an S3 bucket when it is ready to release.

Creating the build

Firstly you need to create a build in VSTS that will generate your deployment package zip file. Start by heading to “Build and Release > Builds” and create a new build definition. Here I have four steps in my build:

The first step is to remove the “.git” directory from the local path on the build server. Alternatively, I could have moved everything from the source directory to a staging folder, but this seemed easier to implement. Since I don’t need the repository info either, this is still a clean enough way to do things. The code in my PowerShell step here is below:

$gitPath = Join-Path -Path $env:BUILD_SOURCESDIRECTORY -ChildPath ".git"
Remove-Item -Path $gitPath -Recurse -Force -Confirm:$false

The second step is to create an archive from your source folder. You can do this with the following settings on the “Create Archive” task.

You’ll notice a couple of settings here. Firstly, I’m packing up the entire sources directory, and I’m not prefixing archive paths with the root folder name. Lastly, I’m including the build ID in the output name, so each build gets a unique file name.

The last two tasks in the sequence are to publish build artifacts. The first is the zip file I just mentioned; the second is a script we’ll use in the release itself – which I cover in the next section.

Lastly, you can change things like the triggers for the build to suit your needs as well. I should probably look at some tests at this point also, but since I’m still getting my head around PHP, we’ll come back to that another time.

Installing the AWS extension to VSTS

Before you set up your release, you need to install the AWS extension to VSTS from the marketplace. If you select the marketplace button in the top right corner and choose “browse marketplace” you can find it there.

If you search for “AWS”, you’ll find the “AWS tools for Microsoft Visual Studio Team Services” extension. This free extension adds a bunch of useful task steps to VSTS that are useful when releasing to AWS! Click the “Get it free” button and choose your tenant to install.

Creating the release

Now that we have a build and AWS extension lets go and create a release to send it to Elastic Beanstalk. In VSTS go to “Build and Release > Releases” to create a new release definition.  Start by selecting the build artifact, choosing your build as the artifact.

Next, you can add your environments. Here I would typically add one for each environment you have for your application in Elastic Beanstalk. That way you can leverage things like approvals before each environment as well as scheduling releases too. When you add an environment, you’ll see a screen that is much like the build editor. You can create your list of tasks to run here.

The one task you need is the “AWS Tools for Windows PowerShell script” task. My configuration looks like this:

When you add this task for the first time, you’ll need to set up access to one of your AWS subscriptions. Use the “Manage” link next to “AWS Credentials” for this. Here you can add an access key and secret key that has access to your account. I created a specialised user for this in IAM that only has access to the S3 bucket we release through, and to Elastic Beanstalk.

The release script

Next, you specify your region, any parameters you need to pass to your script, and then the location of the script. In my case, I can select it with the picker there as we used to publish artifact action for the release script in the build phase. I’ll share the script first and then explain it below.

param(
    [Parameter(Mandatory=$true)]
    [String]
    $EnvironmentName
)

$appName = "BrianFarnhill.com"
$bucketName = "YOUR S3 BUCKET NAME"
$versionLabel = $env:BUILD_BUILDNUMBER
$description = "Automated release from VSTS: $versionLabel"

$contentPath = Join-Path -Path $PSScriptRoot -ChildPath "..\Content" -Resolve
$packageFile = Get-ChildItem -Path $contentPath -Filter "*.zip" | Select-Object -First 1
$packageName = $packageFile.Name
$binaryPath = Join-Path -Path $PSScriptRoot -ChildPath "..\Content\$packageName" -Resolve
Write-S3Object -BucketName $bucketName `
               -Key $packageName `
               -File $binaryPath `
               -CannedACLName bucket-owner-full-control

New-EBApplicationVersion -ApplicationName $appName `
                         -Description $description `
                         -VersionLabel $versionLabel `
                         -Process $true `
                         -SourceBundle_S3Bucket $bucketName `
                         -SourceBundle_S3Key $packageName

# Wait for the new version to be processed
$i = 0
$processed = $false
while ($i -lt 20 -and $processed -eq $false) 
{
    $version = Get-EBApplicationVersion -ApplicationName $appName `
                                        -VersionLabel $versionLabel

    if ($version.Status.Value -eq "Processed")
    {
        $processed = $true
    }
    else 
    {
        Start-Sleep -Seconds 30
        $i++
    }
}

Update-EBEnvironment -ApplicationName $appName `
                     -Description $description `
                     -EnvironmentName $EnvironmentName `
                     -VersionLabel $versionLabel

A quick rundown of what this script is doing

  1. Uploading the ZIP package to the S3 bucket you define at the top of the script
  2. Adding the ZIP file as a new version of your application to the Elastic Beanstalk application
  3. Updating the specified environment to release the specific version that was deployed

This script will return as completed as soon as the release begins in Elastic Beanstalk. If you want you could put in a test and loop to wait for the success (or failure) of your release so that VSTS would show the pass/fail correctly.

There is one drawback to this approach. When your release runs it needs to install the AWS PowerShell tools on the hosted VSTS build agent. The quickest I’ve seen this roll through is 5 minutes, but it can take up to 10. If the speed of release is an issue for you, then you might need to look at using a custom build agent that has the tools installed on it beforehand.

Save your release and trigger it against the latest build. You should see that when it completes your Elastic Beanstalk environment will be deploying a new build.

Summary

There you have it! Now you can check your code into VSTS and use that if it’s your preferred tool for managing your projects, but still be able to deploy to the AWS cloud for your applications directly. Happy deploying folks!

Leave a Reply