Lambda Bash custom runtime 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:
- Initializes by loading the handler file provided by the lambda
- Grabs the event data from the invocation event
- Grabs the request id from the invocation header
- 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"}'