how.wtf

Lambda Bash custom runtime with AWS CDK

· Thomas Taylor

A bald character looking at the Bash logo for deploying custom runtimes with AWS CDK

For this post, I will extend the AWS documentation for ‘Building a custom runtime’ by additionally using the AWS CDK in Python to deploy the Bash custom runtime for AWS Lambda.

How to deploy a Lambda custom runtime using AWS CDK

Follow the instructions below to deploy an AWS Lambda Function with a Bash custom runtime!

Create a /handler folder with a bootstrap file

AWS provides a bootstrap file that creates the custom runtime. Here is that script:

 1#!/bin/sh
 2
 3set -euo pipefail
 4
 5# Initialization - load function handler
 6source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"
 7
 8# Processing
 9while true
10do
11  HEADERS="$(mktemp)"
12  # Get an event. The HTTP request will block until one is received
13  EVENT_DATA=$(curl -sS -LD "$HEADERS" "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
14
15  # Extract request ID by scraping response headers received above
16  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
17
18  # Run the handler function from the script
19  RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
20
21  # Send the response
22  curl "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"  -d "$RESPONSE"
23done

The tutorial explains what this script does in-depth, but here’s the summary:

  1. Initializes by loading the handler file provided by the lambda
  2. Grabs the event data from the invocation event
  3. Grabs the request id from the invocation header
  4. Runs the handler, then passes the response to the lambda runtime API

For more information about the runtime process, check out the AWS documentation here that describes the process and the API.

I created a folder named /handler and added the bootstrap file into it.

Create a requirements.txt

For this tutorial, version 2.117.0 of the AWS CDK was used.

1aws-cdk-lib==2.117.0
2constructs>=10.0.0,<11.0.0

Install the dependencies

1pip3 install -r requirements.txt

Add index.sh file to /handler directory

1handler() {
2  EVENT_DATA=$1
3  echo "$EVENT_DATA" 1>&2;
4  RESPONSE="Echoing request: '$EVENT_DATA'"
5  echo $RESPONSE
6}

Create stack.py with the lambda function

 1from os import path
 2
 3import aws_cdk as cdk
 4import aws_cdk.aws_lambda as lambda_
 5
 6
 7class BashFunctionStack(cdk.Stack):
 8    def __init__(self, scope: cdk.App, construct_id: str, **kwargs) -> None:
 9        super().__init__(scope, construct_id, **kwargs)
10
11        lambda_function = lambda_.Function(
12            self,
13            "Function",
14            runtime=lambda_.Runtime.PROVIDED_AL2023,
15            handler="index.handler",
16            code=lambda_.Code.from_asset(path.join(path.dirname(__file__), "handler")),
17        )
18
19        cdk.CfnOutput(self, "FunctionName", value=lambda_function.function_name)

Create app.py

1import aws_cdk as cdk
2
3from stack import BashFunctionStack
4
5app = cdk.App()
6BashFunctionStack(app, "BashFunctionStack")
7
8app.synth()

Create cdk.json

1{
2  "app": "python3 app.py"
3}

The directory structure should look like this:

1project/
2├── app.py
3├── cdk.json
4├── handler
5│   ├── bootstrap
6│   └── index.sh
7├── requirements.txt
8└── stack.py

Deploy the stack

1cdk deploy

Because of the CfnOutput, the function name is an output:

1Outputs:
2BashFunctionStack.FunctionName = BashFunctionStack-Function76856677-TiMb7dPSe1lE

Invoke the function

If everything worked correctly, we can invoke the function using the AWS CLI:

1aws lambda invoke response.txt \
2    --function-name FUNCTION_NAME \
3    --payload '{"text":"Hello"}' \
4    --cli-binary-format raw-in-base64-out

and receive the following output:

1{
2    "StatusCode": 200,
3    "ExecutedVersion": "$LATEST"
4}

with a response.txt of:

1Echoing request: '{"text":"Hello"}'

#aws   #aws-cdk   #serverless   #bash   #python  

Reply to this post by email ↪