AWS SAM CI/CD with Code Pipeline

Vyas Chinnam
10 min readNov 27, 2018

--

AWS has many components that can be used for building a serverless application. However, assembling components like Lamdba functions, API gateway, DynamoDB and Kinesis together to form an application does create a bit of work and some of it can be rather tedious. Serverless Application Model (SAM) is designed to make it easier to assemble complete application by treating the components as part of an application stack. What that means is, it is possible to build a complete web application stack following the canonical three layer (front end, middleware/services layer and database) model without having to worry about the infrastructure components like EC2, VPC ..etc.

Designing and maintaining infrastructure components requires expertise and they continue to incur cost once created . Using managed services in a serverless model helps bypass these. However, complete serverless model may not be possible in all scenarios but with the right design it should be possible to mix both.In this article we are going to discuss what it takes to build part of this application stack (services and storage) with a sample application called BikeShare and automate CI/CD aspects through AWS CodePipeline.

Bikeshare App:

First a few details about the application, fairly basic design that allows an user to get/book a bike and and to return it back once done. Key architectural aspects like security and concurrency along with features like user registration, payment set up are not included in the design. Focus is on SAM CI/CD pipeline rather than building a complete bikeshare application. Design therefore has two simple services implemented through corresponding Lambda functions. Database layer has 3 tables — BikeLocations, Bikes and BikeRides implemented on top of Dynamodb. Divided the application into two stacks, one for services and another for database layer, with the assumption that code changes more frequently than data and it doesn’t make sense to mix them together. For this session, considered automating the application stack through code pipeline.

To implement the Lambda functions, started off by creating a sample SAM application with Node.js run time. By default this creates a ‘Hello World’ application stub with a SAM template.yaml file, modified this to add two ‘serverless ‘ functions and added corresponding Node.js implementations for these functions. It would have been more elegant if there was a web application type of a template that generates basic stubs rather than having to cut out ‘Hello World’ code!. However, didn’t find one such template in the SAM application repository. Using the ‘cookie cutter’ framework it may be possible to design such a template but that is a task for the future.

Complete application source code is available at the link below, with documentation on how to get app installed and running. Once the app is developed and functionality tested locally, it is possible to continue in that model using local SAM commands to package and deploy. However, this can become a bit tedious over time and automation would certainly help, especially as the app grows with additional features.

Source Code : https://github.com/vyask/bikeshareapp

Pipeline Automation:

First a brief summary of what needs to be done and then we will go through the actual steps.Typical pipeline involves steps to pull source code from repository, build the code, create artifacts and deploy the code. First step, therefore, is to check in the local code to a git repository. I choose AWS Codecommit but you can use Github or other source code systems if you like. Next step is to define the build stage. This stage uses the code pulled from repository, builds, packages code and pushes to S3. As part of this build process, SAM template is transformed into cloudformation template and generated output file also is pushed out to S3. Next step is deploying the packaged code and we will use cloudformation as the provider.

AWS has documentation on automating CI/CD for Lambda at the link shown below. You can follow this documentation to create the pipeline, however you are likely to run into a couple of issues as I did. That is part of the reason why I decided to write it up based on my experience. However, please make sure you create the IAM role as described in the documentation and keep it ready before starting to build a pipeline.

https://docs.aws.amazon.com/lambda/latest/dg/build-pipeline.html

Step 1 starts with ‘create pipeline ‘ command on AWS console. Screen shot shown below. Name the pipeline and allow AWS to create an IAM role or select an existing role if you already have one with the right permissions. For artifact store, you should have an S3 bucket if you followed earlier instructions in source code, if not use default location. Click next when ready.

Step 2 is about selecting the source code repository, as explained before I choose AWS codecommit, screenshot shown below. You can leave the change detect option with the default cloudwatch option, once ready click next.

Step 3 involves defining build stage, sample screenshot shown below. Choose AWS Codebuild as the provider and create an application as shown in the screenshot below. AWS documentation advises to let AWS create an IAM role and later update the role to have right permissions for S3 bucket. If you have the right role use it to avoid this unnecessary step, if not allow AWS to create a role and update it later. Bear in mind, until you update that role with correct S3 permissions your build stage will fail.

Create Project popup.

Source code uploaded to github has a buildspec.yml file, it looks like the screenshot below. As you can see, cloudformation command is exactly the same as command line . This stage produces the packaged.yaml and uploads to S3 bucket selected above. Lambda function code gets packaged and uploaded to same S3 bucket as well. As explained before, IAM role used at this stage should have the right permissions for S3 bucket. Otherwise this stage will fail.

Please note that we are using AWS CLI rather than SAM as AWS managed AMI used for build machine comes with AWS CLI already installed. Using SAM, on the other hand, requires an explicit installation on the build machine every time pipeline goes through build step or build a new AMI with SAM CLI and specify that for the build spec.

Step 4 involves the deploy stage, cloudformation acts as the provider to this stage. Screenshot shown below, input to this stage involves specifying the name of the stack, name of the change set and template file . Template file is the output generated from prior build stage and this is where AWS documentation is a bit unclear. You need to specify it as BuildArtifact::packaged.yaml file. ‘BuildArtifact’ is the name of artifact produced from prior build stage. If you look at the pipeline stages, you will see names like SourceArtifact, BuildArtifact. If you happen to call it by a different name, make sure you specify the correct name. Packaged.Yaml is the output of SAM template as specified in builspec.yml in build stage. If you used different name please make sure to specify the right name. Need to specify ‘CAPABILITY-IAM’ for capabilities even though it is listed as optional. Provide the name of IAM role created before starting step 1, if you have not done that please go ahead and create that role in a separate window before proceeding further . It is important to have the right permissions for this role, otherwise this stage will fail and error messages aren’t always helpful.

Click next when ready (ignore the validation error that your cloudformation stack doesn’t exist) and it will take you to summary screen. Click on ‘Create pipeline’, your pipeline will get created and starts going through each stage as it executes. However, even if all stages of your pipeline are successful, as shown in the screenshot below, you don’t have the app stack yet!. That is because cloudformation change set is created but not executed. So we need to add an extra step to execute the change set. Please note that, if you followed AWS documentation and didn’t provide the right permissions to IAM role during build stage (Step 3) , that stage will fail as it doesn’t have correct permissions for S3. You will have to update the role policy permissions to correct the issue.

At this point, you have two options, one is to add an extra action at deploy stage to execute the change set or the other option is to add an extra stage after deploy to execute the changes. AWS documentation refers to adding extra action at deploy stage. To do that, go back to your pipeline, click on edit and click on edit stage in the deploy section. Click on add action and provide input as shown in screenshot below. As you can see, it looks very similar to the input provided during deploy section except for ‘execute change set’ option.

Save this action and your pipeline, click on ‘Release change’ to trigger pipeline again. At this point you should have your application APIs created and should see something on API Gateway. APIs can be tested from gateway with query inputs. As explained before, additional stage can be added to pipeline instead of adding extra action, tried both options and made them work.

Now that you have pipeline created, any changes pushed to your git repository will trigger your pipeline (at times there may be a delay of a minute or two, so be patient!) and your stack gets redeployed. You will start appreciating this capability as you start adding additional features to your application and make testing as part of the application stack.

Summary and Next Steps :

After going through the exercise, you can see a few places where either the documentation or the implementation could have been a bit more helpful. First issue was with the IAM role AWS creates for build stage, it would be helpful if the role is created with the right S3 permissions. Second issue was around specifying template file, AWS documentation simply says provide file name where as the screen requires a specific format like ‘inputartifact::filename’. Took me a while to figure this one out. Critical thing to always remember while building the pipeline is to ensure output of one stage becomes input for next stage. Third issue was the error message that your cloud formation stack or change set doesn’t exist while saving deploy stage. After a few trials, I learned to ignore that and move ahead. In spite of all these issues, once you successfully build the pipeline you will start appreciating and enjoying the convenience of automation. As explained at the beginning of this article, using SAM model takes away all the grunt work of binding Lambda functions to API gateway and automation avoids repeating this.

While it can look a bit intimidating at first and error messages can be somewhat confusing or unhelpful, going through this exercise might help to provide a bit more understanding of what happens during this process.

As noted before, APIs are not secured so please make sure you are using it for your testing only and not publicize to the world. Also remember to tear down your stack, can be done directly on cloudformation screen. With pipeline in place, you have the ability to rebuild your stack when you need it again.

In terms of design choices, APIs are meant to be fast and efficient. At times, that requires some compromises or deviation from the pure REST model. In this implementation, I put action verb (like ‘book’ or ‘return’ in the path) and that may not appeal to everyone. Important point though is you can implement REST model but how strictly you adhere to depends on your needs and choices. On the database side, designed 3 tables even though didn’t end up using one of them. One good thing about using DynamoDB is the ability to add additional attributes without having to recreate tables. Given the concerns around cold starts with Java VM, choose Node.js for run time even though I am not a javascript programmer. Lambda function runtime is Node.js 8.10, but coded for earlier version 6.10 as I am yet to learn correct usage of ‘async’ and ‘await’. In my view it makes sense to use Node.js or Python for Lambda functions, typically these functions shouldn’t have complex code and the need to load a lot of libraries. So even if you are new to these languages, it makes sense to try them out.

As explained before, this implementation didn’t consider user management and front end. I am looking forward to using AWS Cognito to secure the APIs. Cognito has option to use ‘User Pool’ as well as ‘federated web identity’, intend to experiment with both options. AWS Amplify was released earlier in the year and it provides a relatively easy way of building a front end. Intend to use these two components for the next exercise and would provide more details on these in a future article so stay tuned.

--

--