Lambda with CodePipeline

Introduction

Building a continuous delivery pipeline for a Lambda application with AWS CodePipeline.

Using the steps available from the AWS Lambda developer guide.

The CDK helped define the following AWS resources.

  • CodeCommit repository
  • CodePipeline
  • S3 Bucket

Assets

The following assets are required for this example.

index.js

A Lambda function that returns the current time.

var time = require('time');
exports.handler = (event, context, callback) => {
    var currentTime = new time.Date();
    currentTime.setTimezone("America/Los_Angeles");
    callback(null, {
        statusCode: '200',
        body: 'The time in Los Angeles is: ' + currentTime.toString(),
    });
};

template.yml

The AWS SAM (Server-less Application Model) template that defines the application

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Outputs the time
Resources:
  TimeFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs10.x
      CodeUri: ./
      Events:
        MyTimeApi:
          Type: Api
          Properties:
            Path: /TimeResource
            Method: GET

buildspec.yml

An AWS CodeBuild build specification that installs required packages and uploads the deployment package to Amazon S3.

Replace lambda-deployment-artifacts-123456789012 with the name of your bucket.

version: 0.2
phases:
  install:
    runtime-versions:
        nodejs: 10
  build:
    commands:
      - npm install time
      - export BUCKET=lambda-deployment-artifacts-123456789012
      - aws cloudformation package --template-file template.yml --s3-bucket $BUCKET --output-template-file outputtemplate.yml
artifacts:
  type: zip
  files:
    - template.yml
    - outputtemplate.yml

Defining the code pipeline resources

App

#!/usr/bin/env python3

from aws_cdk import core

from lambda_code_pipeline.LambdaCodePipelineStack import LambdaCodePipelineStack

props = {
    'namespace' : 'LambdaCodePipeline',
    'repositoryName' : 'lambda-pipeline-repo',
    'codeBuildProjectName' : 'lambda-pipeline-build'
}
app = core.App()

LambdaCodePipelineStack(app, f"{props['namespace']}-Pipeline",props)

app.synth()

LambdaCodePipelineStack

from aws_cdk import (
    core,
    aws_codecommit as code_commit,
    aws_codepipeline as codepipeline,
    aws_codepipeline_actions as codepipeline_actions,
    aws_codebuild as code_build,
    aws_s3 as s3,
    aws_cloudformation as cloudformation,
    aws_iam as iam
)

class LambdaCodePipelineStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str,props, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # The code that defines your stack goes here

        # Pipeline requires a bucket for versioning

        artifact_bucket = s3.Bucket(
            self,
            "SourceBucket",
            bucket_name=f"lambda-deployment-artifacts-{core.Aws.ACCOUNT_ID}",
            versioned=True,
            removal_policy=core.RemovalPolicy.DESTROY
            
        )


        # Create a CodeCommit repository

        repository = code_commit.Repository(self, "CodeCommitRepository",repository_name=props['repositoryName'])

        # Create a CodePipline

        pipeline = codepipeline.Pipeline(
            self, "code-pipeline",
            pipeline_name="MyFirstPipeline",
            artifact_bucket=artifact_bucket,
            
        )
    
        # Create pipeline stages

        source_stage = pipeline.add_stage(stage_name="Source")
        build_stage = pipeline.add_stage(stage_name="Build")
        deploy_stage = pipeline.add_stage(stage_name="Deploy")

        source_output = codepipeline.Artifact(artifact_name='source')

        source_stage.add_action(
            codepipeline_actions.CodeCommitSourceAction(
                action_name="Source",
                repository=repository,
                run_order=1,
                output=source_output,
                trigger=codepipeline_actions.CodeCommitTrigger.EVENTS,
                branch="master"
            )
        )

        # create a Pipeline project
        code_build_project = code_build.PipelineProject(
            self,
            "CodeBuildProject",
            project_name=props['codeBuildProjectName'],
            build_spec=code_build.BuildSpec.from_source_filename(
                filename="buildspec.yml"
            ),
            environment=code_build.BuildEnvironment(
                build_image=code_build.LinuxBuildImage.UBUNTU_14_04_BASE,
                compute_type=code_build.ComputeType.SMALL
            )
        )
        # define a build action

        build_stage.add_action(
            codepipeline_actions.CodeBuildAction(
                action_name="CodeBuild",
                input=source_output,
                project=code_build_project
            )
        )

        # define deploy action
        deploy_stage.add_action(
            codepipeline_actions.CloudFormationCreateReplaceChangeSetAction(
                stack_name="lambda-pipeline-stack",
                change_set_name="lambda-pipeline-changeset",
                template_path=source_output.at_path("outputtemplate.yml"),
                capabilities=[
                    core.CfnCapabilities.AUTO_EXPAND,
                    core.CfnCapabilities.NAMED_IAM
                ],
                admin_permissions=False,
                action_name="execute-changeset",               
            )
        )

Commit assets to code repository

Once the code repository is setup.

Checkout the code repository and then commit the assets defined earlier into the repository.

~/lambda-pipeline-repo$ git add .
~/lambda-pipeline-repo$ git commit -m "project files"
~/lambda-pipeline-repo$ git push

Test the application

The application includes an API Gateway API with a public endpoint that returns the current time. Use the Lambda console to view the application and access the API.

To test the application

Open the Lambda console Applications page
Choose lambda-pipeline-stack.
Under Resources, expand ServerlessRestApi.
Choose Prod API endpoint.
Add /TimeResource to the end of the URL. For example, https://l193nqxdjj.execute-api.us-east-2.amazonaws.com/Prod/TimeResource.
Open the URL.

The API returns the current time in the following format.

The time in Los Angeles is: Thu Jun 27 2019 16:07:20 GMT-0700 (PDT)
Last updated on 21 Aug 2020
Published on 21 Aug 2020