The AWS Serverless Application Model (AWS SAM) is an open-source framework you can use to build serverless applications on AWS. This allows developers to build and test serverless applications like Lambda functions locally before deploying them to AWS.
In this post, I’ll show how you can create a Lambda function locally, test it, package it and deploy it to AWS.
AWS SAM comprises of the following components:
- AWS SAM Template: Think of this as an extension to a Cloud Formation template. You use this to define your serverless application
- AWS SAM CLI: You use the CLI to build and test your serverless functions defined by AWS SAM templates. You use the CLI to invoke Lambda functions locally, package and deploy the function to AWS. SAM CLI uses the open source docker-lambda docker images
Pre-Requisites:
This post assumes that you have basic knowledge of AWS, Lambda, and Docker. You will need the following software installed prior to getting started:
Getting Started:
I’m going to creation a Lambda function written in Python that will give me the instance ids of stopped EC2 instances in my AWS account
I’m going to create a folder ec2-get-instances where I will create the following files:
▶ tree ec2-get-instances
ec2-get-instances
├── get_stopped_instances_function.py
└── template.yml
0 directories, 2 files
- template.yml: This is the SAM template that will define the properties for the Lambda function. As you can see from below, it looks very similar to a CloudFormation template where I specify properties of my Lambda function such as runtime, memory size, etc. I’m also creating an IAM role for the Lambda function with managed and inline policies
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: A starter AWS Lambda function.
Resources:
GetStoppedInstances:
Type: 'AWS::Serverless::Function'
Properties:
Handler: get_stopped_instances_function.lambda_handler
Runtime: python2.7
CodeUri: .
Description: A starter AWS Lambda function.
MemorySize: 128
Timeout: 3
Policies:
- ReadOnlyAccess # AWS Managed Policy
- Version: '2012-10-17' # Inline Policy
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectACL
Resource: '*'
- get_stopped_instances_function.py: My python function that is responsible for (as the name implies) getting stopped instances
from __future__ import print_function
import json
import boto3
ec2 = boto3.client('ec2')
print('Loading function \n')
def lambda_handler(event, context):
# create filter for stopped instances
instance_filters = [{
'Name': 'instance-state-name',
'Values': ['stopped']
}]
# get instances that are stopped
instance_status_response = ec2.describe_instance_status(Filters=instance_filters,IncludeAllInstances=True)
# iterate through the response to print just the instance id of stopped instances
for instance in instance_status_response['InstanceStatuses']:
print (instance['InstanceId'])
return 'End Function'
Invoking the Lambda Function:
Let’s look at how you can invoke the lambda function. Before that, ensure you have Docker up and running and you have assumed a role/profile via AWS CLI that has access to an AWS account.
Browse to the directory where your code resides and run the command below.
sam local invoke --no-event GetStoppedInstances
GetStoppedInstances is the name of the Resource defined in template.yml
You should see an output similar to below:
▶ sam local invoke --no-event GetStoppedInstances
2019-04-14 14:47:13 Found credentials in environment variables.
2019-04-14 14:47:13 Invoking get_stopped_instances_function.lambda_handler (python2.7)
Fetching lambci/lambda:python2.7 Docker container image......
2019-04-14 14:47:21 Mounting /Users/anoop/infra/testspace/ec2-get-instances as /var/task:ro inside runtime container
START RequestId: 2fbd4334-cdcb-4a4a-af1c-d5a3f0081726 Version: $LATEST
Loading function
i-1234567890123456
i-1234567890123456
i-1234567890123456
i-1234567890123456
END RequestId: 123456789
REPORT RequestId: 123456789 Duration: 811 ms Billed Duration: 900 ms Memory Size: 128 MB Max Memory Used: 41 MB
"End Function"
When you run sam local invoke, it pulls the appropriate docker image and executes the function inside that container. The output also gives you the duration and the amount of memory used to execute the function. This would serve as a guide for how much of memory you should assign to your function.
Events:
If you are familiar with Lambda functions, then you most likely know about the concept of events and passing them to your Lambda function. In my example above, I don’t have a need to pass any events, so I simply ran it with a --no-event flag
You can pass events to your Lambda function using the -e flag and specifying the name of a json file.
For example:
sam local invoke -e events.json MyFunctionName
You can also generate “sample” events from the SAM CLI for a wide range of AWS resources
The following command generates a sample event for a s3 put
▶ sam local generate-event s3 put
{
"Records": [
{
"eventVersion": "2.0",
"eventSource": "aws:s3",
"awsRegion": "us-east-1",
"eventTime": "1970-01-01T00:00:00.000Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "EXAMPLE"
},
"requestParameters": {
"sourceIPAddress": "127.0.0.1"
},
"responseElements": {
"x-amz-request-id": "EXAMPLE123456789",
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "testConfigRule",
"bucket": {
"name": "example-bucket",
"ownerIdentity": {
"principalId": "EXAMPLE"
},
"arn": "arn:aws:s3:::example-bucket"
},
"object": {
"key": "test/key",
"size": 1024,
"eTag": "0123456789abcdef0123456789abcdef",
"sequencer": "0A1B2C3D4E5F678901"
}
}
}
]
}
Package and Deploy:
Now that we have run the Lambda function locally, let’s look at how we can package the function and deploy it.
Browse to the directory where your code resides and run the following command:
sam package --template-file template.yml \
--output-template-file "packaged.yml" \
--s3-bucket "your_s3_bucket_name"
The above command will “transform” the template.yml to a finished file packaged.yml and upload it to the S3 bucket that you have specified.
Now, let’s deploy this to AWS using the following command:
sam deploy --template-file packaged.yml \
--stack-name "sam-lambda-tst" \
--capabilities "CAPABILITY_IAM"
The above command will create a cloudformation stack with the name specified. The --capabilities flag is required as the cloudformation stack creates an IAM resource
Output:
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - lambda-tst
This blog post has hopefully helped you get started with AWS SAM.