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"
23doneThe 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.0Install the dependencies
1pip3 install -r requirements.txtAdd 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.pyDeploy the stack
1cdk deployBecause of the CfnOutput, the function name is an output:
1Outputs:
2BashFunctionStack.FunctionName = BashFunctionStack-Function76856677-TiMb7dPSe1lEInvoke 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-outand receive the following output:
1{
2 "StatusCode": 200,
3 "ExecutedVersion": "$LATEST"
4}with a response.txt of:
1Echoing request: '{"text":"Hello"}'