Jérôme Decoster

Jérôme Decoster

3x AWS Certified - Architect, Developer, Cloud Practionner

22 Dec 2019

API Gateway + Lambda + AWS CLI

The Goal
  • Create an API Gateway and Lambda from the CLI.

  • Create an API Gateway from the CLI is much more complex than going through the web interface. But if you want to create a script and automate some tasks, you will have to deal with it.

    Install and setup the code

    Get the code from this github repository :

    # download the code
    $ git clone \
        --depth 1 \
        https://github.com/jeromedecoster/aws-apigateway-lambda-cli.git \
        /tmp/aws
    
    # cd
    $ cd /tmp/aws
    

    To setup the project, you must edit the settings file first :

    $ cat settings.sample.sh 
    # Project
    AWS_ID=
    AWS_REGION=eu-west-3
    # Lambda
    LAMBDA_FUNCTION_NAME=aws-apigateway-lamdba-cli
    LAMBDA_FUNCTION_ARN=
    # ...
    

    You can change some values, but the most important thing is to choose your region. The default value is :

    • AWS_REGION : eu-west-3

    After that you can execute the 1-setup.sh script. This will create the settings.sh file :

    # execute the setup
    $ ./1-setup.sh
    

    Create the Lambda

    To create a Lambda we need to :

    1. Create a role
    2. Attach the execution policy to it
    3. Package a zip from the source code
    4. Create the Lambda function

    All the following steps, and other little tricks, are in the 2-create-lambda.sh script.

    You can avoid all of the following copy and paste by running this script :

    # execute all the steps below
    $ ./2-create-lambda.sh
    

    Create a role

    We use the create-role command :

    # get the values
    $ source ./settings.sh
    
    # create the lambda role
    $ aws iam create-role \
        --role-name $LAMBDA_ROLE_NAME \
        --assume-role-policy-document fileb://lambda-role-policy.json \
        --query 'Role.Arn' \
        --output text
    

    This is the trust policy :

    # the policy
    $ cat lambda-role-policy.json
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Service": "lambda.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        ]
    }
    

    Attach the execution policy to it

    We use the attach-role-policy command :

    # attach the `AWSLambdaBasicExecutionRole` policy to the lambda role
    $ aws iam attach-role-policy \
        --role-name $LAMBDA_ROLE_NAME \
        --policy-arn $LAMBDA_POLICY_ARN
    

    Package a zip from the source code

    We use zip :

    # zip the code of the lambda function
    $ zip -9 lambda.zip index.js
    

    The code is very simple :

    # the code
    $ cat index.js
    exports.handler = async (event) => {
        return {
            statusCode: 200,
            body: JSON.stringify('hello')
        }
    }
    

    Create the Lambda function

    We use the create-function command :

    # get the lambda role Arn
    LAMBDA_ROLE_ARN=$(aws iam get-role \
        --role-name $LAMBDA_ROLE_NAME \
        --query 'Role.Arn' \
        --output text)
    
    # create the Lambda
    $ aws lambda create-function \
        --region $AWS_REGION \
        --function-name $LAMBDA_FUNCTION_NAME \
        --runtime nodejs12.x \
        --role $LAMBDA_ROLE_ARN \
        --handler index.handler \
        --zip-file fileb://lambda.zip
    

    Create the API Gateway

    To create an API Gateway and connect it whith the Lambda we need to :

    1. Create the REST API Gateway
    2. Create the resource
    3. Create the POST method
    4. Deploy the API

    All the following steps, and other little tricks, are in the 3-create-apigateway.sh script.

    You can avoid all of the following copy and paste by running this script :

    # execute all the steps below
    $ ./3-create-apigateway.sh
    

    Create the REST API Gateway

    We use the create-rest-api command :

    # create the API Gateway
    $ aws apigateway create-rest-api \
        --region $AWS_REGION \
        --name $API_GATEWAY_NAME \
        --endpoint-configuration types=REGIONAL \
        --description 'A test API'
    

    Now we need to define some variables :

    # get the API Gateway id
    API_GATEWAY_ID=$(aws apigateway get-rest-apis \
        --region $AWS_REGION \
        --query "items[?name=='$API_GATEWAY_NAME'].[id]" \
        --output text)
    
    # get the API Gateway arn 
    API_GATEWAY_ARN="arn:aws:execute-api:${AWS_REGION}:${AWS_ID}:${API_GATEWAY_ID}"
    
    # get the root path id
    API_GATEWAY_ROOT_RESOURCE_ID=$(aws apigateway get-resources \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID \
        --query "items[?path=='/'].[id]" \
        --output text)
    

    Create the resource

    We use the create-resource command :

    # create the `API_GATEWAY_RESOURCE_NAME` resource path
    $ aws apigateway create-resource \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID \
        --parent-id $API_GATEWAY_ROOT_RESOURCE_ID \
        --path-part $API_GATEWAY_RESOURCE_NAME
    
    # get the `API_GATEWAY_RESOURCE_NAME` path id
    $ API_GATEWAY_RESOURCE_ID=$(aws apigateway get-resources \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID \
        --query "items[?path=='/$API_GATEWAY_RESOURCE_NAME'].[id]" \
        --output text)
    

    Create the POST method

    We will associate the POST method with our resource. And we’re going to attach the Lambda function to it.

    We use the put-method command :

    # create the POST method
    $ aws apigateway put-method \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID \
        --resource-id $API_GATEWAY_RESOURCE_ID \
        --http-method POST \
        --authorization-type NONE
    
    # setup the POST method integration request
    $ aws apigateway put-integration \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID \
        --resource-id $API_GATEWAY_RESOURCE_ID \
        --http-method POST \
        --integration-http-method POST \
        --type AWS_PROXY \
        --uri "arn:aws:apigateway:$AWS_REGION:lambda:path/2015-03-31/functions/$LAMBDA_FUNCTION_ARN/invocations"
    
    # add lambda permission
    $ STATEMENT_ID=api-lambda-permission-$(cat /dev/urandom | tr -dc 'a-z' | fold -w 10 | head -n 1)
    $ aws lambda add-permission \
        --region $AWS_REGION \
        --function-name $LAMBDA_FUNCTION_NAME \
        --source-arn "$API_GATEWAY_ARN/*/POST/$API_GATEWAY_RESOURCE_NAME" \
        --principal apigateway.amazonaws.com \
        --statement-id $STATEMENT_ID \
        --action lambda:InvokeFunction
    

    To complete the process, we need to setup the method response.

    We use the put-method-response command :

    # setup the POST method responses (method + integration response)
    $ aws apigateway put-method-response \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID \
        --resource-id $API_GATEWAY_RESOURCE_ID \
        --http-method POST \
        --status-code 200 \
        --response-models '{"application/json": "Empty"}'
    
    $ aws apigateway put-integration-response \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID \
        --resource-id $API_GATEWAY_RESOURCE_ID \
        --http-method POST \
        --status-code 200 --selection-pattern ''
    

    Deploy the API

    We can now deploy the API Gateaway and test it with curl.

    We use the create-deployment command :

    # publish the API, create the `dev` stage
    $ aws apigateway create-deployment \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID \
        --stage-name dev
    
    # it works !
    $ curl --request POST \
        https://$API_GATEWAY_ID.execute-api.$AWS_REGION.amazonaws.com/dev/$API_GATEWAY_RESOURCE_NAME
    "hello"
    

    If we want to clear everything we have created, we have to use these commands :

    # delete the API Gateway 
    $ aws apigateway delete-rest-api \
        --region $AWS_REGION \
        --rest-api-id $API_GATEWAY_ID
    
    # delete the Lambda
    $ aws lambda delete-function \
        --region $AWS_REGION \
        --function-name $LAMBDA_FUNCTION_NAME
    
    # to delete the role, you must detach policy first
    $ aws iam detach-role-policy \
        --role-name $LAMBDA_ROLE_NAME \
        --policy-arn $LAMBDA_POLICY_ARN
    
    # delete the role
    $ aws iam delete-role \
        --role-name $LAMBDA_ROLE_NAME