DEV Community

loading...
Cover image for a first look at aws sam

a first look at aws sam

anthony-campolo
full stack web dev
・9 min read

AWS Serverless Application Model (SAM) is an open-source framework for building serverless applications. It provides shorthand syntax to express functions, APIs, databases, and event source mappings. Much like AWS CDK, SAM transforms and expands its own syntax into CloudFormation syntax during deployment.

Comparison to CDK

  • SAM uses a declarative, template-based syntax with JSON or YAML similar to CloudFormation but syntactically simpler
  • CDK uses common languages such as JavaScript, TypeScript, or Python to imperatively provision resources similar to Pulumi

SAM CLI is currently used to build locally, test, and package serverless applications defined using AWS CloudFormation or SAM templates. However, there is currently early support for using the SAM CLI to build and test with the CDK.

01-yo-dawg-meme

Setup

The code for this article can be found on my GitHub.

Configure AWS CLI with aws configure

Make sure you have the AWS CLI installed and an AWS account. For general use, aws configure is recommended as the fastest way to set up your AWS CLI installation.

aws configure
Enter fullscreen mode Exit fullscreen mode

When you enter this command, the AWS CLI prompts you for four pieces of information:

  • Access key ID
  • Secret access key
  • AWS Region
  • Output format

Go to My Security Credentials to find your Access Key ID, Secret Access Key, and default region. You can leave the output format blank.

AWS Access Key ID: <YOUR_ACCESS_KEY_ID>
AWS Secret Access Key: <YOUR_SECRET_ACCESS_KEY>
Default region name: <YOUR_REGION_NAME>
Default output format [None]: 
Enter fullscreen mode Exit fullscreen mode

Install SAM CLI

There are numerous ways to install the SAM CLI depending on your development environment and operating system. I followed the instructions for installing with Homebrew.

brew tap aws/tap
brew install aws-sam-cli
Enter fullscreen mode Exit fullscreen mode

Check version

sam --version
Enter fullscreen mode Exit fullscreen mode

Output:

SAM CLI, version 1.23.0
Enter fullscreen mode Exit fullscreen mode

Initialize project with sam init

sam init
Enter fullscreen mode Exit fullscreen mode

Output:

SAM CLI now collects telemetry to better understand customer needs.
You can OPT OUT and disable telemetry collection by setting
the environment variable SAM_CLI_TELEMETRY=0 in your shell.

Thanks for your help!
Learn More:
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html
Enter fullscreen mode Exit fullscreen mode

Select a template

Which template source would you like to use?
    1 - AWS Quick Start Templates
    2 - Custom Template Location
Enter fullscreen mode Exit fullscreen mode

Select AWS Quick Start Templates.

Select package type

What package type would you like to use?
    1 - Zip (artifact is a zip uploaded to S3)  
    2 - Image (artifact is an image uploaded to an ECR image repository)
Enter fullscreen mode Exit fullscreen mode

Select Zip.

Select runtime for your language of choice

Which runtime would you like to use?
    1 - nodejs14.x
    2 - python3.8
    3 - ruby2.7
    4 - go1.x
    5 - java11
    6 - dotnetcore3.1
    7 - nodejs12.x
    8 - nodejs10.x
    9 - python3.7
    10 - python3.6
    11 - python2.7
    12 - ruby2.5
    13 - java8.al2
    14 - java8
    15 - dotnetcore2.1
Enter fullscreen mode Exit fullscreen mode

Select nodejs14.x.

Select project name

Project name [sam-app]: ajcwebdev-sam
Enter fullscreen mode Exit fullscreen mode

Give your project a name. I named my project ajcwebdev-sam.

Select example application

Cloning app templates from https://github.com/aws/aws-sam-cli-app-templates

AWS quick start application templates:
    1 - Hello World Example
    2 - Step Functions Sample App (Stock Trader)
    3 - Quick Start: From Scratch
    4 - Quick Start: Scheduled Events
    5 - Quick Start: S3
    6 - Quick Start: SNS
    7 - Quick Start: SQS
    8 - Quick Start: Web Backend
Enter fullscreen mode Exit fullscreen mode

Select Hello World Example.

Output:

-----------------------
Generating application:
-----------------------
Name: ajcwebdev-sam
Runtime: nodejs14.x
Dependency Manager: npm
Application Template: hello-world
Output Directory: .

Next steps can be found in the README file at ./ajcwebdev-sam/README.md
Enter fullscreen mode Exit fullscreen mode

Project code

ajcwebdev-sam
  ├── .gitignore
  ├── README.md
  ├── template.yaml
  ├── events
  │   └── event.json
  └── hello_world
      ├── .npmignore
      ├── app.js
      ├── package.json
      └── tests
          └── unit
              └── test_handler.js
Enter fullscreen mode Exit fullscreen mode

template.yaml

This example app uses a single Lambda function along with API Gateway.

02-stack-lambda-api-gateway

These are defined in template.yaml which includes a SAM template for specifying your application's AWS resources.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  ajcwebdev-sam

  Sample SAM Template for ajcwebdev-sam

Globals:
  Function:
    Timeout: 3

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

Outputs:
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn
Enter fullscreen mode Exit fullscreen mode

hello-world

The hello-world directory contains code for the application's Lambda function inside app.js and a package.json file for the necessary dependencies and scripts needed for our build process.

app.js

Contains your actual Lambda handler logic.

// hello-world/app.js

let response;

/**
 *
 * @param {Object} event - API Gateway Lambda Proxy Input Format
 * @param {Object} context
 * @returns {Object} object - API Gateway Lambda Proxy Output Format
 * 
 */
exports.lambdaHandler = async (event, context) => {
  try {
    response = {
      'statusCode': 200,
      'body': JSON.stringify({
        message: 'hello world',
      })
    }
  } catch (err) {
    console.log(err);
    return err;
  }
  return response
};
Enter fullscreen mode Exit fullscreen mode

package.json

You specify dependencies in a manifest file that you include in your application. Since our example is using Node.js functions, our manifest file is package.json. This file is required for sam build.

{
  "name": "hello_world",
  "version": "1.0.0",
  "description": "hello world sample for NodeJS",
  "main": "app.js",
  "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs",
  "author": "SAM CLI",
  "license": "MIT",
  "dependencies": {
    "axios": "^0.21.1"
  },
  "scripts": {
    "test": "mocha tests/unit/"
  },
  "devDependencies": {
    "chai": "^4.2.0",
    "mocha": "^8.2.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

test-handler.js

The hello-world directory also contains a tests directory with a unit directory for unit tests and a test-handler.js file.

// hello-world/tests/unit/test-handler.js

'use strict'

const app = require('../../app.js')
const chai = require('chai')
const expect = chai.expect
var event, context

describe('Tests index', function () {
  it('verifies successful response', async () => {
    const result = await app.lambdaHandler(event, context)

    expect(result).to.be.an('object')
    expect(result.statusCode).to.equal(200)
    expect(result.body).to.be.an('string')

    let response = JSON.parse(result.body)

    expect(response).to.be.an('object')
    expect(response.message).to.be.equal("hello world")
  })
})
Enter fullscreen mode Exit fullscreen mode

The events directory contains invocation events. There are a lot of events in event.json, so we'll take a look at each key and its corresponding value.

body

HTTP body containing data associated with the request (like content of an HTML form), or the document associated with a response.

"{\"message\": \"hello world\"}"
Enter fullscreen mode Exit fullscreen mode

resource

Sets a proxy in front of the resource.

"/{proxy+}"
Enter fullscreen mode Exit fullscreen mode

path

Sets a path for the proxy to send the request.

"/path/to/resource"
Enter fullscreen mode Exit fullscreen mode

httpMethod

The HTTP POST method sends data to the server. The type of the body of the request is indicated by the Content-Type header.

"POST"
Enter fullscreen mode Exit fullscreen mode

isBase64Encoded

false
Enter fullscreen mode Exit fullscreen mode

queryStringParameters

{
  "foo": "bar"
}
Enter fullscreen mode Exit fullscreen mode

pathParameters

Proxies the request to our resources.

{
  "proxy": "/path/to/resource"
}
Enter fullscreen mode Exit fullscreen mode

stageVariables

{
  "baz": "qux"
}
Enter fullscreen mode Exit fullscreen mode

headers

I definitely know what all of these do. Totally.

{
  "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
  "Accept-Encoding": "gzip, deflate, sdch",
  "Accept-Language": "en-US,en;q=0.8",
  "Cache-Control": "max-age=0",
  "CloudFront-Forwarded-Proto": "https",
  "CloudFront-Is-Desktop-Viewer": "true",
  "CloudFront-Is-Mobile-Viewer": "false",
  "CloudFront-Is-SmartTV-Viewer": "false",
  "CloudFront-Is-Tablet-Viewer": "false",
  "CloudFront-Viewer-Country": "US",
  "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
  "Upgrade-Insecure-Requests": "1",
  "User-Agent": "Custom User Agent String",
  "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
  "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
  "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
  "X-Forwarded-Port": "443",
  "X-Forwarded-Proto": "https"
}
Enter fullscreen mode Exit fullscreen mode

requestContext

The requestContext object is a map of key-value pairs. In each pair, the key is the name of a $context variable property, and the value is the value of that property.

{
  "accountId": "123456789012",
  "resourceId": "123456",
  "stage": "prod",
  "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
  "requestTime": "09/Apr/2015:12:34:56 +0000",
  "requestTimeEpoch": 1428582896000,
  "identity": {
    "cognitoIdentityPoolId": null,
    "accountId": null,
    "cognitoIdentityId": null,
    "caller": null,
    "accessKey": null,
    "sourceIp": "127.0.0.1",
    "cognitoAuthenticationType": null,
    "cognitoAuthenticationProvider": null,
    "userArn": null,
    "userAgent": "Custom User Agent String",
    "user": null
  },
  "path": "/prod/path/to/resource",
  "resourcePath": "/{proxy+}",
  "httpMethod": "POST",
  "apiId": "1234567890",
  "protocol": "HTTP/1.1"
}
Enter fullscreen mode Exit fullscreen mode

Build application with sam build

The sam build command will build your serverless application and prepare it for subsequent steps in your workflow, like locally testing the application or deploying it to AWS.

sam build
Enter fullscreen mode Exit fullscreen mode

Output:

Building

codeuri: /Users/ajcwebdev/projects/ajcwebdev-sam/hello-world
runtime: nodejs14.x
metadata: {}
functions: ['HelloWorldFunction']

Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided
Enter fullscreen mode Exit fullscreen mode

This command builds any dependencies that your application has, and copies your application source code to folders under .aws-sam/build to be zipped and uploaded to Lambda.

Deploy to AWS with sam deploy

sam deploy --guided
Enter fullscreen mode Exit fullscreen mode

Configuring SAM deploy

Looking for config file [samconfig.toml] :  Not found

Setting default arguments for 'sam deploy'

Stack Name [sam-app]: ajcwebdev-sam
AWS Region [us-west-1]: us-west-1
Confirm changes before deploy [y/N]: N
Allow SAM CLI IAM role creation [Y/n]: Y
Enter fullscreen mode Exit fullscreen mode

For the question, "HelloWorldFunction may not have authorization defined," AWS SAM is informing you that the sample application configures an API Gateway API without authorization. When you deploy the sample application, AWS SAM creates a publicly available URL.

HelloWorldFunction may not have authorization defined
Is this okay? [y/N]: Y
Save arguments to configuration file [Y/n]: Y
SAM configuration file [samconfig.toml]: samconfig.toml
SAM configuration environment [default]: default
Enter fullscreen mode Exit fullscreen mode

Looking for resources needed for deployment

Found!

  Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-1234
  A different default S3 bucket can be set in samconfig.toml

Saved arguments to config file
Running 'sam deploy' for future deployments will use the parameters saved above.
The above parameters can be changed by modifying samconfig.toml
Learn more about samconfig.toml syntax at 
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
Enter fullscreen mode Exit fullscreen mode

Deploying with following values

Stack name                   : ajcwebdev-sam
Region                       : us-west-1
Confirm changeset            : False
Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-1234
Capabilities                 : ["CAPABILITY_IAM"]
Parameter overrides          : {}
Signing Profiles             : {}
Enter fullscreen mode Exit fullscreen mode

CloudFormation stack changeset

Operation                     LogicalResourceId             ResourceType                  Replacement                 
--------------------------------------------------------------------------------------------------------------------------
+ Add                         HelloWorldFunctionHelloWorl   AWS::Lambda::Permission       N/A                         
                              dPermissionProd                                                                         
+ Add                         HelloWorldFunctionRole        AWS::IAM::Role                N/A                         
+ Add                         HelloWorldFunction            AWS::Lambda::Function         N/A                         
+ Add                         ServerlessRestApiDeployment   AWS::ApiGateway::Deployment   N/A                         
                              47fc2d5f9d                                                                              
+ Add                         ServerlessRestApiProdStage    AWS::ApiGateway::Stage        N/A                         
+ Add                         ServerlessRestApi             AWS::ApiGateway::RestApi      N/A                         
--------------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:us-west-1:1234:changeSet/samcli-deploy1620000970/1234
Enter fullscreen mode Exit fullscreen mode

CloudFormation events from changeset

ResourceStatus                ResourceType                  LogicalResourceId             ResourceStatusReason        
--------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS            AWS::IAM::Role                HelloWorldFunctionRole        Resource creation Initiated 
CREATE_IN_PROGRESS            AWS::IAM::Role                HelloWorldFunctionRole        -                           
CREATE_COMPLETE               AWS::IAM::Role                HelloWorldFunctionRole        -                           
CREATE_IN_PROGRESS            AWS::Lambda::Function         HelloWorldFunction            -                           
CREATE_IN_PROGRESS            AWS::Lambda::Function         HelloWorldFunction            Resource creation Initiated 
CREATE_COMPLETE               AWS::Lambda::Function         HelloWorldFunction            -                           
CREATE_IN_PROGRESS            AWS::ApiGateway::RestApi      ServerlessRestApi             -                           
CREATE_IN_PROGRESS            AWS::ApiGateway::RestApi      ServerlessRestApi             Resource creation Initiated 
CREATE_COMPLETE               AWS::ApiGateway::RestApi      ServerlessRestApi             -                           
CREATE_IN_PROGRESS            AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   -                           
                                                            47fc2d5f9d                                                
CREATE_IN_PROGRESS            AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   Resource creation Initiated 
                                                            47fc2d5f9d                                                
CREATE_IN_PROGRESS            AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   Resource creation Initiated 
                                                            dPermissionProd                                           
CREATE_IN_PROGRESS            AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   -                           
                                                            dPermissionProd                                           
CREATE_COMPLETE               AWS::ApiGateway::Deployment   ServerlessRestApiDeployment   -                           
                                                            47fc2d5f9d                                                
CREATE_IN_PROGRESS            AWS::ApiGateway::Stage        ServerlessRestApiProdStage    -                           
CREATE_IN_PROGRESS            AWS::ApiGateway::Stage        ServerlessRestApiProdStage    Resource creation Initiated 
CREATE_COMPLETE               AWS::ApiGateway::Stage        ServerlessRestApiProdStage    -                           
CREATE_COMPLETE               AWS::Lambda::Permission       HelloWorldFunctionHelloWorl   -                           
                                                            dPermissionProd                                           
CREATE_COMPLETE               AWS::CloudFormation::Stack    ajcwebdev-sam                 -                           
Enter fullscreen mode Exit fullscreen mode

CloudFormation outputs from deployed stack

Key                 HelloWorldFunctionIamRole                                                                       
Description         Implicit IAM Role created for Hello World function                                              
Value               arn:aws:iam::1234:role/ajcwebdev-sam-HelloWorldFunctionRole-1234               

Key                 HelloWorldApi                                                                                   
Description         API Gateway endpoint URL for Prod stage for Hello World function                                
Value               https://y4mpitesn5.execute-api.us-west-1.amazonaws.com/Prod/hello/                              

Key                 HelloWorldFunction                                                                              
Description         Hello World Lambda Function ARN                                                                 
Value               arn:aws:lambda:us-west-1:1234:function:ajcwebdev-sam-HelloWorldFunction-1234   

Successfully created/updated stack - ajcwebdev-sam in us-west-1
Enter fullscreen mode Exit fullscreen mode

Copy the URL contained in the Value for HelloWorldApi and send a request with your API tool of choice such as cURL, Postman, or Insomnia.

03-insomnia-request

You can also just use a good ol' fashion web browser.

04-browser-request

Since this is a simple hello world application with an unsecured API endpoint, you should consider tearing the project down unless you intend on adding addition security features. To delete the sample application that you created:

aws cloudformation delete-stack --stack-name ajcwebdev-sam
Enter fullscreen mode Exit fullscreen mode

If you send another request to the endpoint you will receive a 500 error message.

05-delete-stack

Discussion (0)

Forem Open with the Forem app