A Practical Guide to Surviving AWS SAM
Part 1
AWS SAM is an open-source framework that you can use to build serverless applications on AWS. Opposed to other tools, such as the serverless framework, SAM is an official AWS product — making it valuable to give it a try.
If you are new to the serverless world you may feel overwhelmed by definitions, tools, best practices. But don’t worry, once you start playing with it you will not be able to stop.
But I don’t want to dwell on other details so let’s start. You can find all the code on my GitHub
Setup
First thing to do is initializing our first AWS SAM project (ok it’s the second one but I’ll leave up to you the boring installation of all we need, but you can find more details on my GitHub page.
sam init --runtime python3.7 --name aws-sam
This command is very helpful since create the skeleton of our project
.
└── aws-sam
├── hello_world # code and requirements of our lambda function
│ ├── app.py
│ └── requirements.txt
├── tests # folder containing test code
│ └── unit
│ └── test_handler.py
├── event.json # API gateway event example
├── README.md
├── template.yaml # AWS SAM template
└── .gitignore
template.yaml
file come with predefined a lambda function with an API gateway event attached to it
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: hello world aws sam
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.7
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
If you prefer you can directly create your project from yor preferred IDE supporting AWS Toolkit. I will use AWS Toolkit for PyCharm, and if you go to
File --> New Project... --> AWS Serverless Application
where you will find the basic configuration for your project

Build
For building dependencies AWS SAM CLI comes with a built-in build command
sam build --template template.yaml
The command will create a .aws-sam
folder containing our code packaged with dependencies installed from requirements.txt
For our example will look like this
.
└── .aws-sam
└── build
├── HelloWorldFunction
│ ├── HelloWorldFunction
│ │ ├── app.py
│ │ └── ... # packaged dependencies
└── template.yaml
As you can see the command recreates the original tree substituting the name of the folder with the name of the resource defined in the template. So folder hello_world
in our project became folder HelloWorldFunction
, like the name of the resource in template.yaml
file.
Each of our lambda folder must contain a requirements.txt
file specifying required dependencies, you will have so maximum granularity specifying dependencies for each function but AWS SAM will have to install the dependencies for each lambda resulting in longer build time if your template contains an high number of lambda function.
Keep also in mind that all the content of the folder you specify in CodeUri
will be packaged for our lambda, counting in the size limit of lambda deployment package.
Package
Now that all our dependencies have been downloaded we can package all in way that AWS lambda will accept our code. AWS SAM is an optimized cloudformation for serverless application so it comes with all the benefits (and pains) of cloudformation. One of the greatest advantage it’s the possibility to use the built-in package command.
sam package --template-file .aws-sam/build/template.yaml --s3-bucket artifact-bucket --s3-prefix aws-sam/versions/1 --output-template-file .aws-sam/build/template-packaged.yaml
This command package all our code, upload it on an existing S3 and updates our template CodeUri
with the correct S3 key. Remember to use the build folder otherwise all our dependencies will be missing once deployed our function.
I suggest you to take advantage of the --s3-prefix
parameter to create a clean structure also for yuor deployments, otherwise you will end with all our package in the root of our S3 bucket.
Our packaged template will look like this
AWSTemplateFormatVersion: '2010-09-09'
Description: hello world aws sam
Globals:
Function:
Timeout: 3
Outputs:
HelloWorldApi:
Description: API Gateway endpoint URL for Prod stage for Hello World function
Value:
Fn::Sub: https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/
HelloWorldFunction:
Description: Hello World Lambda Function ARN
Value:
Fn::GetAtt:
- HelloWorldFunction
- Arn
HelloWorldFunctionIamRole:
Description: Implicit IAM Role created for Hello World function
Value:
Fn::GetAtt:
- HelloWorldFunctionRole
- Arn
Resources:
HelloWorldFunction:
Properties:
CodeUri: s3://artifact-bucket/aws-sam/versions/1/b891a08245ed5940781e64b483cbcb14
Events:
HelloWorld:
Properties:
Method: get
Path: /hello
Type: Api
Handler: app.lambda_handler
Runtime: python3.7
Type: AWS::Serverless::Function
Transform: AWS::Serverless-2016-10-31
As you can see CodeUri
path now points to an S3 key, containing our package.
Deploy
It’s now time to see our application working for real. As stated before we can use the utilities of cloudformation in particular we can leverage deploy command
sam deploy --stack-name hello-world-sam --template-file .aws-sam/build/template-packaged.yaml --capabilities CAPABILITY_IAM
As you can see we are telling sam to deploy our template-packaged.yaml
template, the one generated from package command, CloudFormation will read the template and create all the resource defined within it.
We can check the correct deployment from AWS CloudFormation console

Test
Till now we have assumed all our code and configurations are correct, once deployed all will work like a charm, but usually it’s not the case, testing it’s always an important step to keep in mind when your are developing an application. In the world of serverless testing strategies has changed a little focusing more on the integration rather than unit introducing new challenges.
Try to define a testing strategies for all the components of your application from template passing through the code till the roles your function will have once deployed.
We will roughly cover some of the major testing strategies for our application without digging into details, I just wanna start make you comfortable with the basics of AWS SAM, next parts will cover with major details this topic.
Let’s start with the validate command, utility letting you check whenever your template is syntactically valid:
sam validate --template template.yaml
It’s a really easy command that can saves time during deployment phase.
Passing to code we can use local invoke command that let you invoke locally your lambda handler with a mocked event passed from a json file or directly to the command.
sam local invoke --event event.json
You can also invoke your function directly from the template within Pycharm with AWS Toolkit

And from Run/Debug configuration you can set basic information like AWS credential to be used, automatically loaded from AWS CLI configuration

If you have created the project from CLI or you have added new function and you try to run a function you may incur in the following error

This problem can be easily solved marking the folder containing your function as source root, this way Python interpreter will know where to find you function code.

Another useful utility it’s the possibility to locally start an API gateway hosting all your function
sam local start-api
Now you can locally invoke your HTTP endpoint from localhost, with an API manager like Postman.
If you want to test your function within python you can use one of the testing libraries supported and locally invoke the lambda handler function. AWS SAM init preconfigure for you a tests
folder containing an example.
Here i reported a slightly different version leveraging the event.json
file instead of generating event directly from code.
import json
import pytest
from hello_world import app
@pytest.fixture()
def apigw_event():
""" Generates API GW Event"""
with open("../../event.json") as json_file:
return json.load(json_file)
def test_lambda_handler(apigw_event, mocker):
ret = app.lambda_handler(apigw_event, "")
data = json.loads(ret["body"])
assert ret["statusCode"] == 200
assert "message" in ret["body"]
assert data["message"] == "hello world!"
# assert "location" in data.dict_keys()
If you are experiencing problems with the import of your function from test folder check if sys path are correctly set.
Great you survived to AWS SAM! It’s not that difficult in the end but we have only scratched the surface of what SAM is capable of. So stay tuned for the second part of the tutorial. Thank you for reading.
More content at bip.xTech