How-to-use-AWS-SAM-to-simplify-Serverless-Python-applications-development

How to use AWS SAM to simplify Serverless Python development

AWS Serverless Application Model (SAM) is an open-source framework that allows you to simplify the development of Lambda functions for your serverless applications in AWS Cloud, where you need fine-grained control of your cloud infrastructure components.

SAM concept is very simple: it helps you to build and test your Lambda Functions, generate a CloudFormation template for your AWS infrastructure, and deploy everything. The central point of everything is an API Gateway, which is usually called your Lambda Functions in response to its queries.

In this article, you’ll see how to use SAM to do the same thing we did in the previous article, but with the help of a serverless framework.

Install AWS SAM

The installation process for all platforms is covered in official AWS documentation. As a Mac user, I’ll note the basic steps here:

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

Alternatively, you can install AWS SAM to your Python development environment:

pip install aws-sam-cli

Create Python Lambda Function

Project init setup is very simple and straightforward:

sam init --name my-sam-app --runtime python3.7 --app-template hello-world

SAM will ask additional interactive questions if you do not specify any of those parameters.

For Python lovers, there’re three template projects available:

We’ll use the first one for the beginning.

Let’s take a look at what’s inside our project:

cd my-sam-app
tree
.
├── README.md
├── events
│   └── event.json
├── hello_world
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── template.yaml
└── tests
    └── unit
        ├── __init__.py
        └── test_handler.py

So, we have here:

  • The basic template of the README.md file.
  • events folder contains a sample of JSON requests for testing the hello_world Lambda function.
  • hello_world folder contains the actual Lambda function code.
  • tests – no comments
  • template.yaml – is similar to the CloudFormation template, which supports higher-level entities, like AWS::Serverless::Function, AWS::Serverless::LayerVersion, or AWS::Serverless::HttpApi, for example.

Let’s use the following simple Lambda function code:

import json
import logging
import time
import traceback
import requests
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
def lambda_handler(event, context):
  try:
    LOGGER.info('Event structure: %s', event)
    response = requests.get('https://ifconfig.co/json')
    return {
        'statusCode': 200,
        'body': "{}".format(
            response.text
        )
    }
  except Exception as e:
    traceback.print_exc()
    response_data = {
        'statusCode': 500,
        'error': str(e)
    }
    return response_data

This function will send a request to https://ifconfig.co/json using requests (external Python library), get your IP address, and return it to you in JSON structure.

So, as requests is an external library, let’s put it to requirements.txt:

echo "requests" >> requirements.txt

Note: don’t forget to unit test the Lambda function code.

Build and test the Lambda function

To build our Lambda function, just run:

sam build

Testing Lambda function

To test our Lambda function locally, let’s run

sam local invoke HelloWorldFunction \
    --event events/event.json

Testing API Gateway integration

To test Lambda <-> API Gateway integration locally, you need to run to start local API Gateway:

sam local start-api

Now you may use curl to test your API call:

curl http://127.0.0.1:3000/hello

Deploy Lambda function

As soon as the Lambda function is tested, you may deploy it. To do that, you need to have an S3 bucket created, where your Lambda function distribution will be uploaded.

Service S3 bucket

Let’s create such a bucket (we’ll use Python one-liner to make the bucket name random):

aws s3 mb "s3://my-sam-app-$(python -c "import random, string; print(''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(5)))")"
make_bucket: my-sam-app-6rz4m

For more information about managing Amazon S3 using AWS CLI, check out our How to use AWS CLI to manage Amazon S3 article.

Package project

Now we need to package all SAM project artifacts and compile the final CloudFormation template (my-sam-app-compiled-template.yaml)

sam package --output-template-file my-sam-app-compiled-template.yaml --s3-bucket my-sam-app-6rz4m

To this point in time, you’ll have a randomly named lambda function distribution file in your S3 bucket:

aws s3 ls s3://my-sam-app-6rz4m
2020-03-19 20:26:29     532327 73d2d8baa2978842e922f95f3ca1dce0

And generated my-sam-app-compiled-template.yaml file:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: 'my-sam-app
  Sample SAM Template for my-sam-app
  '
Globals:
  Function:
    Timeout: 3
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: s3://my-sam-app-6rz4m/73d2d8baa2978842e922f95f3ca1dce0
      Handler: app.lambda_handler
      Runtime: python3.7
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
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

Deployment

Let’s deploy our function:

sam deploy --template-file my-sam-app-compiled-template.yaml \
    --stack-name my-sam-app-stack \
    --capabilities CAPABILITY_IAM

This will deploy:

  • Lambda function role.
  • API Gateway.
  • Lambda Function.

Test deployment

Let’s query our lambda function using API Gateway:

aws cloudformation \
  describe-stacks \
  --stack-name my-sam-app-stack \
  --query "Stacks[0].Outputs[?OutputKey=='HelloWorldApi'].OutputValue" \
  --output text
https://0rppnkrtgg.execute-api.us-east-2.amazonaws.com/Prod/hello/
curl https://0rppnkrtgg.execute-api.us-east-2.amazonaws.com/Prod/hello/

Update Project

Imagine we changed our mind, and instead of getting our IP address, we want to send our users randomly generated passwords. Let’s use our python one-liner as an example and change our Lambda function.

Here’s the new content of our app.py file:

import json
import logging
import random
import string
import traceback
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
def lambda_handler(event, context):
  try:
    LOGGER.info('Event structure: %s', event)
    random_password = ''.join(
        random.SystemRandom().choice(
            string.ascii_lowercase + string.digits
        ) for _ in range(20)
    )
    return {
        'statusCode': 200,
        'body': json.dumps({
            'random_password': f"{random_password}"
        })
    }
  except Exception as e:
    traceback.print_exc()
    response_data = {
        'statusCode': 500,
        'error': str(e)
    }
    return response_data

Deploy Update

To deploy an update, all you need to do is to build (if your Lambda functions have been changed) and package the project:

sam build
sam package \
    --output-template-file my-sam-app-compiled-template.yaml \
    --s3-bucket my-sam-app-6rz4m

And finally, deploy the update:

sam deploy --template-file my-sam-app-compiled-template.yaml \
    --stack-name my-sam-app-stack \
    --capabilities CAPABILITY_IAM

And we may get our random password by curl-ing our API Gateway:

curl https://0rppnkrtgg.execute-api.us-east-2.amazonaws.com/Prod/hello/

Cleaning up

As soon as you’re done, you may clean up everything using the following command to delete the CloudFormation stack:

aws cloudformation delete-stack --stack-name my-sam-app-stack

And the following command to delete your bucket and artifacts:

aws s3 rb s3://my-sam-app-6rz4m --force

Summary

After looking through this article, I hope you better understand the development workflow where SAM is involved.

If you found this article useful, please, help us spread it to the world.

Stay tuned!

Similar Posts