Mastering the AWS SAM CLI

Mar 12, 2020

 

mastering-sam-cli

Serverless technologies like AWS Lambda and API Gateway are the next logical step in the evolution of the cloud. Full utilization and simplified maintenance alone make serverless technologies worth considering for your next application.

But there is one problem: On AWS, infrastructure is code—CloudFormation (CFN) code, to be precise—and this can get a bit out of hand when you use services like Lambda and API Gateway. These have to be linked to each other and also need fine granular permissions via IAM. This was the reason for the creation of SAM, an extension for CFN that does away with much of the boilerplate needed to set up a serverless application. It also comes with a command-line interface (CLI) tool that helps with local debugging and deployment.

The latest SAM release includes custom domains for API Gateway using an existing SQS queue for SNS events and propagation of tags from generated Lambda functions to their corresponding IAM roles.

Getting Started with the AWS SAM CLI on Linux

The AWS SAM CLI is an open-source command-line tool written in Python, and its source code can be found on GitHub. It uses Docker containers in the background to simulate the AWS Lambda runtime environment locally to speed up debugging and testing. On Linux and macOS you can install it with the Homebrew package manager. 

To install the SAM CLI you will need the following:

The CLI is installed via Homebrew with the following commands:

$ brew tap aws/tap
$ brew install aws-sam-cli

After installation, the CLI should be available via the sam command.


Creating an Example SAM Application

You can test the installed CLI by initializing a new project with the following command:

$ sam init

Choose the 1 for the “Quick Start Templates” and 1 again for the “Node.js runtime” and use the default name.

This will initialize an example SAM application, whose main part is the sam-app/template.yaml file. In this file, we can see that only one CFN resource is defined, a Lambda function. The definition looks something like this:

HelloWorldFunction:
  Type: AWS::Serverless::Function
  Properties:
    CodeUri: hello-world/
    Handler: app.lambdaHandler
    Runtime: nodejs12.x
    Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get

These lines tell you that your Lambda function uses the “Node.js version 12.x” runtime and that this function is triggered by an event of type Api, which essentially means API Gateway triggers your Lambda function when it receives an HTTP request. The function could also be triggered by a different type of event, like an S3 upload or a CloudWatch timer.

CodeUri and Handler tell you that the function code is located inside the hello-world/app.js file, which should have a named export called lambdaHandler. This is the part of the template that links the SAM resource definition to the actual JavaScript code.

Inside the sam-app directory, we can now run the build and deploy commands to get our application into the AWS cloud.

$ sam build
$ sam deploy --guided

You should answer “yes” to all questions so the deploy command can create IAM roles in your account. It’s advisable to use a region close to you.

SAM CLI will create more CFN resources than just the Lambda function. This function was configured to be triggered by an API Gateway event, so it also creates an API Gateway resource and the corresponding API Gateway Stage resource. In addition, it will create Lambda permission and IAM role resources so your Lambda function can be called by API Gateway correctly.

The example SAM application template came out of the box with some outputs, too. One of them is the API Gateway endpoint URL, which is shown after the deployment is finished.

Since the Lambda function is triggered by an HTTP GET request to that URL, you can try the deployment right away with a cURL request:

$ curl \
https://<API_ID>.execute-api.<REGION>.amazonaws.com/Prod/hello/

This command should output something like the following JSON:

{"message": "hello world"}

You can also run the Lambda function locally with the local invoke command in conjunction with a pre-defined event JSON file.

$ sam local invoke "HelloWorldFunction" -e events/event.json

When you run the command the first time, Docker will download the lambci/lambda:nodejs12.x container image that AWS provides for you.

The idea here is that an event that triggers a Lambda function on AWS usually comes with event data as JSON. If you run the function locally, you need to supply this event information manually in the form of a JSON file. AWS SAM created an event JSON when it initialized the project, and we can use it for this purpose.

The output looks different from the output you got when using cURL.

First, you get some runtime information—how long the function ran, and how it would be billed if it actually ran on AWS infrastructure.

Duration: 4.58 ms
Billed Duration: 100 ms

But you also get the actual return value of your JavaScript function, which looks like this:

{"statusCode":200,"body":"{\"message\":\"hello world\"}"}

API Gateway used the statusCode property to add the right HTTP status code to the response it generated for your request. The content of the body property is what we saw when we accessed the API via cURL.

Important SAM CLI Commands

The SAM CLI has many commands and subcommands that handle building, testing, deploying, debugging, and more. Here are the four most important ones:

sam init

The init command is the first command executed. It creates a new SAM application by allowing you to choose from different base templates to minimize the boilerplate code you have to write to get started. The templates supplied by AWS for the different runtimes often come with a hello world Lambda function that is triggered by an API Gateway event.

sam build

The build command will gather all dependencies required by the Lambda function code. For some runtimes, it will also compile the final binaries that will be uploaded to AWS Lambda.

The build command must also be executed before you run the Lambda function locally via thelocal invoke command.

sam deploy

The deploy command will convert the SAM template to a CFN template and upload it, alongside the other code, to S3. It will then use CFN to deploy all assets to AWS infrastructure.

The deploy command comes with a --guided parameter that helps with the first deployment.

sam local invoke

The local invoke command is used to execute the Lambda function code locally inside a Docker container.

While this is helpful for high-velocity development cycles, this command can be a bit tricky because it doesn’t include any permissions when it runs your code. Often permissions can be quite problematic when you’re developing serverless applications. There should be as few as possible, but when there are too few, nothing works anymore.

As Thundra, we’re proud to come up with a solution to the debugging problem: a remote debugger that runs your code on AWS infrastructure but allows you to use your favorite IDE’s debugging client. Check out this blog post to learn how you can start your native debugging experience for AWS Lambda.

Saving Time with AWS SAM

Using AWS SAM for serverless application development saves a lot of time by eliminating much of the boilerplate that CFN templates require. It extends CFN with new resource types that follow AWS best practices and are more comfortable to use. The SAM CLI also generates code based on templates that are available online.

AWS SAM integrates nicely with the IDE of your choice, whether it is PyCharm, IntelliJ, VS Code, Visual Studio, or AWS’s own IDE service, Cloud9. With Thundra's new remote AWS Lambda debugging, you’ll be able to catch even more bugs right inside your IDE.