Supporting multiple output artifacts from CodeBuild in CodePipeline
An AWS Lambda function for your pipeline
After recent Jenkins security vulnerabilities*, we decided to move our CI/CD to the — relatively new — AWS solution: CodeBuild and CodePipeline. However, out-of-the-box a CodeBuild activity can’t return multiple output artifacts in CodePipeline. I’ve outlined how to work around this using the Lambda function below.
*We missed a (critical) Jenkins update AND had temporarily left our server open to the world — whoops. A remote execution vulnerability was exploited to mine crypto currency (Monero). The t2 server exhausted its CPU credits and builds started to run very slowly…! This issue had already been reported and announced in the JenkinsCI Advisories.
Why CodePipeline
CodePipeline is in essence a state machine that passes around artifacts. Each activity takes Inputs and returns Outputs, and is either a Success or Failure. Each activity can also have side-effects e.g. a Deployment.
The Problem
After starting to migrate we realized: CodeBuild could only create at most one output artifact, even though Lambda and Jenkins (!) support up to 5.
Need for multiple output artifacts
One web deployment involved building several source bundle zips. Specifically .ebextensions must be in the source bundle. If .ebextensions differ across deployments then need multiple source bundles.
I bet there are plenty of other use cases.
An alternative workaround, is to define multiple CodePipelines or CodeBuilds. (One for each Elastic Beanstalk environment.) That’s what we did initially, but building the same code multiple times is unsatisfying…
The Solution
Until AWS CodeBuild supports more, a straightforward solution is to have:
- CodeBuild create up to 5 files which are zipped to a single Output artifact.
- Lambda unzip this Input artifact to the (5) files, and Outputs them.
Simple…
Example Code
A skeleton buildspec.yml to output two files into an output directory:
Note, it’s critical to set Artifacts packaging: zip in the CodeBuild advanced settings. Otherwise, CodePipeline screws up nesting zips — and it’s zips all the way down.
This Lambda function: downloads the Output zip, unzips it and returns the Output artifacts FooZip and BarZip. It also does some book-keeping to CodePipeline to say when it’s completed.
This function needs to be given permission to label the activity outcome (Success or Failure). This is so CodePipeline knows whether it can carry on with further Stages.
It surprised me a little that the credentials, in the event, were only for artifacts. To be fair, the attribute for these credentials is named artifactCredentials.
Our CodePipeline to run the CodeBuild and then invoke the Lambda function:
et voilà
Addendum
- Our Elastic Beanstalk environments post to Slack on environment updates, in my continued efforts to Slack all the things...
- CodeBuild can cache dependencies. You should do that.
- Legal: I am licensing all code in this article as MIT. Enjoy!