Python Lambda functions and API Gateway

Introduction

The most common way to run an AWS Lambda function from outside AWS is through an AWS API Gateway. The API Gateway is accessible via a standard http post. This post could come from a website or an external service like Slack.

Setting up the Lambda function

The easiest way I've found to create a Lambda function and an API Gateway and connect them is with Serverless.

Serverless

Serverless is a framework that allows you to simply define the behavior of a Lambda function with a YAML configuration file and the Lambda function code. A single or group of Lambda functions that work together are referred to as a service.

Installation

Installation of serverless is quite simple:

npm install -g serverless

Basic Setup

To begin your service, you'll use the create command:

serverless create --template aws-python --path myService

This will create a new folder for your service called myService or whatever you specified in the serverless create command. Within that folder, you will find two files that have been preconfigured, handler.py and serverless.yml.

Configuring our Lambda function with an API Gateway

The Lambda function, along with its trigger are defined in the serverless.yml file. This file has three main sections, service, provider, and function.

Service

The service section simply defines the name of your service, so you should see a line in your generated serverless.yml file:

service: myService

Provider

The next section in the serverless.yml file is the provider section. In your generated serverless.yml file you should see the following:

provider:
    name: aws
    runtime: python2.7

There are a number of other configuration options in the provider section. For example, you might want to specify a region, with:

    region: us-west-2

And if your Lambda function will be using any other AWS features, you will probably need to specify iamRoleStatements that give your Lambda function access to those AWS features. For example, to give full access to AWS SNS, you might include:

    iamRoleStatements:
        - Effect: "Allow"
        Action:
        - "sns:*"
        Resource: "arn:aws:sns:us-west-2:*:*"

Functions

The last main section of the serverless.yml file is the functions section. This section defines the name of the function, the python function that is called, any environment variables, and the events that trigger the Lambda function.

For example, to define a function named deploy, triggered by an AWS API Gateway, with an environment variable SLACK_TOKEN defined, you might include:

functions:
    deploy:
        handler: handler.deploy
        environment:
            MY_ENV: gIkuvaNzQIHg97ATvDxqgjtO
        events:
            - http:
                path: deploy
                method: post

For security purposes, you can avoid saving credentials in your code by using local environment variables. For example, you could replace the environment subsection with:

        environment:
            MY_ENV: ${env:MY_ENV}

This would look for a local environment variable defined as MY_ENV.

Writing the Lambda function

Now that our Lambda function has been configured, we are ready to write the code to handle the incoming http post. We've defined the handler to be handler.deploy which translates to the deploy function within the handler.py file.

We start with the following:

import urlparse

def deploy(event, context):
    data = event['body']
    args = dict(urlparse.parse_qsl(data))
    parameter1 = args.get('parameter1', "")
    parameter2 = args.get('parameter2', "")

The function deploy will be called when the Lambda function is triggered, and the parameters event and context will be passed in. You can retrieve the data that was posted from the event parameter:

    data = event['body']

This can then be broken down into the individual posted parameters with:

    args = dict(urlparse.parse_qsl(data))

After the parameters have been processed, you'll want to return the results to the caller. This will need to be in a form that the caller can process. It can be useful to generalize the formatting of the return data:

def get_response(text):
    response = {
        "statusCode": 200,
        "body": text
    }
    return response

You could then return a text response from your deploy function with:

    return get_response("This is my return text.")

Dealing with Python modules

If you need to use external Python modules you have a couple choices. If it's just a single file module, you can just drop it into your project folder next to handler.py. Serverless will install it automatically. You could do the same with a folder or group of folders containing code, but if you are dealing with more complex modules, the easiest thing to do is use the serverless module serverless-python-requirements, following their directions for installing and using. One note on installation of this module is that you probably want to install it globally, in which case you would install it with:

npm install -g serverless-python-requirements

rather than their suggested:

npm install --save serverless-python-requirements

which will install it in the lambda function project directory.

Deploying the Lambda function and API Gateway

Deploying your Lambda function and API Gateway is very easy with Serverless. Simply run:

serverless deploy

This will return the URL of the API Gateway that it created.

Comments