Stelligent

Mocking AWS CodePipeline pipelines with Lambda

I’ve been spending a lot of time with AWS CodePipeline over the past few months and I’m really excited about where it’s going in terms of what I consider to be a true Continuous Delivery service. What’s more, I can codify everything in CodePipeline itself using CloudFormation so that I can reliably modify and provision it repeatedly.
codepipeline_mock
I’ve always been a fan of visualizing my architectures and since a key output of CodePipeline is a visualization of all actions and stages in a software delivery process, it made sense to use this visualization as a means of designing my software delivery process without yet implementing it. I wanted fast feedback and didn’t want the pipeline to actually do anything as I was in the process of thinking through the various stages and actions that’d make up my software delivery process.

Running Lambda Functions

AWS Lambda allows you to run functions without launching servers. Now that we can invoke functions via Lambda in CodePipeline, I created a dummy function that just returns success back to CodePipeline. This way, I can quickly run through many pipeline actions as I design my pipeline structure. Later, I can update the CloudFormation configuration to invoke actions that actually do something?—?like launch environments, deploy software and run tests. The “dummy” Node.js function I wrote is listed below.
Dummy Lambda Function that returns CodePipeline success

var assert = require(‘assert’);
var AWS = require(‘aws-sdk’);
console.log(‘Loading function’);
exports.handler = function(event, context) {
var codepipeline = new AWS.CodePipeline();
 // Retrieve the Job ID from the Lambda action
 var jobId = event["CodePipeline.job"].id;
//console.log(‘Received event:’, JSON.stringify(event, null, 2));
 console.log(‘value1 =’, event.key1);
 console.log(‘value2 =’, event.key2);
 console.log(‘value3 =’, event.key3);
 // Notify AWS CodePipeline of a successful job
 var putJobSuccess = function(message) {
   var params = {
     jobId: jobId
   };
   codepipeline.putJobSuccessResult(params, function(err, data) {
     if(err) {
       context.fail(err);
     } else {
     context.succeed(message);
     }
   });
 };
 // Succeed the job
 putJobSuccess("Tests passed.");

I named this lambdadummy.js, zipped it into a file named Archive.zip and uploaded it to an Amazon S3 bucket in my AWS account. You can find the source for this function here.
In the snippet below, I define the function so that it can run from CodePipeline using CloudFormation. Note the references to lambdadummy.handler, Archive.zip and the S3 bucket. The S3 bucket is the location of the Archive.zip, which contains the JavaScript file called lambdadummy.js.
Defining a Lambda function in AWS CloudFormation

"CodePipelineLambdaDummy":{
   "Type":"AWS::Lambda::Function",
   "DependsOn":[
     "CodePipelineLambdaRole",
     "LambdaCodePipelineExecutionPolicy"
   ],
   "Properties":{
     "Code":{
       "S3Bucket":{
       "Ref":"S3Bucket"
     },
     "S3Key":"Archive.zip"
   },
   "Role":{
     "Fn::GetAtt":[
       "CodePipelineLambdaRole",
       "Arn"
     ]
   },
   "Description":"Always return success",
   "Timeout":20,
   "Handler":"lambdadummy.handler",
   "Runtime":"nodejs",
   "MemorySize":128
  }
},

CodePipeline in CloudFormation

To run the Lambda function from CodePipeline, I define CodePipeline stages and actions in CloudFormation. You can see a sample of a CodePipeline stage and action defined in CloudFormation below.
I use the Invoke action category and Lambda as the provider.
Defining a CodePipeline action to run the Lambda function

{
   "Name":"Configure",
   "Actions":[
     {
       "InputArtifacts":[
       ],
       "Name":"LaunchEnvironment",
       "ActionTypeId":{
       "Category":"Invoke",
       "Owner":"AWS",
       "Version":"1",
       "Provider":"Lambda"
     },
     "OutputArtifacts":[
     ],
     "Configuration":{
       "FunctionName":{
         "Ref":"CodePipelineLambdaDummy"
       },
       "UserParameters":{
         "Ref":"AWS::StackName"
       }
    },
    "RunOrder":1
   },
 ...

Click on the CloudFormation Launch Stack button below to automatically provision the AWS resources described herein. You will be charged for your AWS usage. 

The rest of the CloudFormation template defines additional actions and stages and uses other required AWS resources such as AWS::IAM::Policy, AWS::IAM::Role, AWS::Lambda::Function and AWS::CodePipeline::Pipeline.
I also describe running Lambda functions in more detail at http://www.stelligent.com/automation/aws-lambda-functions-aws-codepipeline-cloudformation/.
Keep in mind that in using this technique, AWS will charge you for CodePipeline and Lambda usage. Anything described in this article will fall within the Lambda free tier, though.
Hope this helps. If you have any questions, reach out to us @stelligent or @paulduvall.