Deploying with AWS SAM: Lambda, API Gateway, DynamoDB, and S3

How to define and deploy a CloudFormation stack using SAM

gurlgilt
5 min readMay 3, 2018

I recently built a lightweight application using python and just a few AWS services:

  • Lambdas for processing and retrieving data
  • API Gateway for getting and posting data to the lambdas
  • DynamoDB
  • S3 for hosting static code

Architecturally, the application is rather simple; however, managing all the resources was cumbersome using the AWS console, so I decided to use AWS’s Serverless Application Model (or SAM) to manage and deploy all the resources I needed.

What this article will cover:

  • What is SAM
  • How to define a SAM template to provision the correct resources needed to run an app on AWS lambda, API Gateway, and S3 (there will be examples, fear not)
  • Deploying a Serverless Application

What it won’t cover: writing the actual lambda code (that will be in another post, I know, it’s backwards).

What is SAM

SAM allows you to create serverless applications by specifying your resources in a template (here on out referred to as SAM template) that is used to define an AWS CloudFormation stack.

A CloudFormation stack is a collection of resources that you can manage as a single unit. This is particularly useful for disparate applications, wherein the code is scattered across several independent resources.

During deployment, the SAM template is automatically translated into CloudFormation syntax.

This means versus going into the AWS console and defining individual resources and manually linking them, you simply define them all within the SAM template and let CloudFormation do its thing.

the boys

How to define a SAM template

SAM templates can be defined in either JSON or YAML. I personally prefer YAML.

Let’s pretend we’re making an app that wants to retrieve data about cat breeds (not a huge fan of cats but they are the darlings of the internet, so I will bend the knee) stored in a DynamoDB table.

Defining a Lambda function resource:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
# above two lines necessary to convert SAM template to CloudFormation templateDescription: Resource definitions for the get_cats appGlobals: # any parameters you want available to all your resources
Function:
Runtime: python3.6 # language used at runtime
Timeout: 180 # timeout for a given lambda function execution
Environment:
Variables: # these will be important later
DYNAMO_TABLE: !Ref DynamoMovieTable
DB_ENDPOINT: http://dynamodb.us-east-1.amazonaws.com
REGION_NAME: us-east-1
Resources:
GetBreedFunction: # the resource’s logical name
Type: AWS::Serverless::Function
Properties:
Handler: get_data_handler # the path to the lambda handler code
Policies: AmazonDynamoDBFullAccess # default IAM policy that allows the lambda to read and write to our soon to be defined Dynamo instance
Events:
Api: # This lambda is triggered by a request to the API Gateway Endpoint /get-breed
Type: Api
Properties:
Path: /get-breed
Method: get
PostBreedDataFunction: Type: AWS::Serverless::Function
Properties:
Handler: post_data_handler
Policies: AmazonDynamoDBFullAccess
Events:
Api:
Type: Api
Properties:
Path: /post-breed
Method: post

A cool thing about the above definitions: You don’t actually need to separately define the API Gateway Resources, specifications for those resources will be inferred. Another note: only one resource is made with multiples methods (endpoints).

Okay, so these lambda functions are expecting a DynamoDB. So let’s define one.

DynamoMovieTable:
Type: AWS::Serverless::SimpleTable # if you want to define a more complex table, use AWS::DynamoDB::Table
TableName: catBreedTable
PrimaryKey:
Name: breedId
Type: String
ProvisionedThroughput:
ReadCapacityUnit: 5
WriteCapacityUnits: 5
Tags:
AppType: Serverless

Excellent, now let’s suppose we want to a lambda that deletes the DynamoDB table. Maybe a new list of cat breeds comes out daily and we want our table to have the freshest data.

DeleteBreedTableFunction:
Type: AWS::Serverless::Function
Properties:
Handler: delete_tables_handler
Policies: AmazonDynamoDBFullAccess
Events:
Timer:
Type: Schedule
Properties:
Schedule: cron(0 4 * * ? *) # will run once a day at midnight
Api: # Note: you can define multiple events that will trigger the lambda execution
Type: Api
Properties:
Path: /delete-cache
Method: post

Alright, now suppose you’ve actually built a static frontend for this thing that allows you to query cat breeds. We’re gonna host it on s3 and to do that we need to define a bucket where we will store the code.

FrontendS3CatBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: cat-breeds-frontend-bucket
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: index.html
VersioningConfiguration:
Status: Enabled
DeletionPolicy: Retain
S3FrontendBucketPolicy: # define a managed (managed by the user) policy that makes the bucket readable.
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref FrontendS3BucketDev # a reference to the bucket defined above
PolicyDocument:
Statement:
-
Action:
- "s3:GetObject"
Effect: "Allow"
Resource:
- !Join # this is essentially a string join
- ''
- - 'arn:aws:s3:::'
- !Ref FrontendS3BucketDev
- '/*'
Principal: '*'

Okay one more helpful resource. I want to be able to look at the activity in my Lambdas, process that data, and send an email about it, let’s say.

StatsFunction:
Type: AWS::Serverless::Function
Properties:
Handler: stats_handler
Policies: # default (provided by AWS) IAM policies
- CloudWatchLogsFullAccess
- AmazonSESFullAccess
Events:
Timer:
Type: Schedule
Properties:
Schedule: cron(0 4 * * ? *)

Deploying SAM

We’ve got all our stack definitions for our Lambdas, API Gateway, DynamoDB table, and s3 bucket. So let’s deploy this sucker.

Before we start, make sure you have the aws cli installed and that you’re working in a virtualenv that has boto3 installed.

In the command line:

  1. Create the stack:
    > aws cloudformation create-stack — stack-name cat-app-stack
  2. Package your sam template into a CloudFormation readable template. This will package template.yml into the file serverless-output.yml.
    > aws cloudformation package — template-file template.yml\
    -— output-template-file serverless-output.yml\
    -— s3-bucket cat-app-stack
  3. Actually deploy the template file
    > aws cloudformation deploy — template-file serverless-output.yml\
    -— stack-name cat-app-stack\
    -— capabilities CAPABILITY_IAM

You should be able to view your stack in the AWS console under the CloudFormation stack services page. If deployment failed, the errors will be there too. You can also look at all the resources that have been provisioned and click into them (still get your AWS console time).

I’ll post another article soon with some example lambda code and how to run lambdas locally.

--

--

Responses (4)