Bundling Go Lambda Functions with the AWS CDK
Tags: aws, cdk, lambda, go
Published: 2023-12-22 01:38:03

---
title: "Bundling Go Lambda Functions with the AWS CDK"
published: true
tags:
  - aws
  - cdk
  - lambda
  - golang
canonical_url: https://bacchi.org/posts/cdk-bundling-golang-functions
cover_image: https://bacchi.org/images/wind-turbines.jpg
---

Recently the Lambda Go runtime has changed from using the Go 1.x managed runtime to using the [provided runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-golang.html#golang-al1) which have been historically used for custom runtimes (i.e. Rust.) The former `go1.x` runtime is being deprecated on January 8, 2024 (quite soon) and the new runtimes `provided.al2023` or `provided.al2` are expected to be used.

With the introduction of these new runtimes, all of our Go binaries must [now be called](https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html#golang-handler-naming) `bootstrap` and be located at the root of the zip file used to deploy the function.

If we have two functions built by separate function code, in the `go1.x` runtime the naming convention would allow us to name them differently (i.e. `create` vs. `list`). Now we must name both function handlers `bootstrap`, potentially causing confusion during the bundling process.

In today's blog post, we'll describe how to bundle two different Go functions with the same name in a single CDK stack.

## AWS CDK Deployment Using Typescript

I'll be using Typescript as the language for the AWS CDK deployment. It might be strange to mix languages for CDK deployment (Typescript) and the Lambda function (Go), but CDK uses Typescript as its main language and most examples are written in that. The reason for using Go in this example is because many deployments need to use compiled languages for task duration and performance (not necessarily to improve cold start time.)

## Source Organization

As described earlier, in the past we could have kept the names of the Go function source files in the same directory and compiled them separately to different file names. Now that the handler file name has to be the same (`bootstrap`), they can't be output to the same directory any longer.

The layout of the files now looks like the following, with each function in its own subdirectory in the `lib/functions` directory:


![Source code layout ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6eegcesf1ika7qppsphk.png)


## Bundling Go Functions

Now that we have the function code in separate directories, we can bundle them using the Alpha CDK construct for a [Go Function](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-lambda-go-alpha-readme.html), shown below.

```typescript
export class BasicGoLambdaStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const table = new TableV2(this, 'Table', {
        partitionKey: { name: 'id', type: AttributeType.STRING },
        removalPolicy: RemovalPolicy.DESTROY,
        });

    const createFunction = new GoFunction(this, 'CreateFunction', {
      entry: 'lib/functions/create',
      environment: {
        "DYNAMODB_TABLE": table.tableName
      },
      logRetention: RetentionDays.ONE_WEEK,
    });

    const listFunction = new GoFunction(this, 'ListFunction', {
      entry: 'lib/functions/list',
      environment: {
        "DYNAMODB_TABLE": table.tableName
      },
      logRetention: RetentionDays.ONE_WEEK,
    });
...
```

The above configuration is obviously fairly straightforward. But before I found the [aws-lambda-go-alpha](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-lambda-go-alpha-readme.html) module, I attempted (mostly unsuccessfully) to use the [aws-lambda-function Function construct](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html) which required using the [Code.fromAsset](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Code.html#static-fromwbrassetpath-options) method with the [ILocalBundling interface](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.ILocalBundling.html). This was extremely uncooperative, I found [multiple blogs](https://github.com/aws-samples/cdk-lambda-bundling/blob/main/lib/cdk-bundling-lambda-stack.ts) and suggestions on how to configure this, and in the end I did get it working, but the solution I came up with was ugly and actually packaged the Go source file in the zip file with the bootstrap handler. Not cool.

It's great that the CDK now supports Go Lambda functions as a full blown construct. While [NodeJS functions](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html) have been supported for much longer it makes sense to have Go supported the same way, because the aggravation I endured packaging these Go handlers with the [standard Lambda Function construct](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html) was a serious pain in the ass.

## Verifying the Output

Now that we have these defined, we can package the artifacts using `cdk synth` and verify that the `bootstrap` binary was created for both functions.

We can see here that the first messages output from the `cdk synth` command are the two functions being bundled:

```shell
$ cdk synth
Bundling asset BasicGoLambdaStack/CreateFunction/Code/Stage...
Bundling asset BasicGoLambdaStack/ListFunction/Code/Stage...
Resources:
...
```

And when we look in the `cdk.out` directory which is where our intermediate CDK files are placed during packaging, we see that the two Lambda Function output directories itemized in the stack asset JSON file (called `BasicGoLambdaStack.assets.json`) have binaries in them (ignore the third .js asset, that's for the cloudwatch logs):

```shell
$ tree cdk.out/
cdk.out/
├── asset.4e26bf2d0a26f2097fb2b261f22bb51e3f6b4b52635777b1e54edbd8e2d58c35
│   └── index.js
├── asset.b0bae29696f98febe5e6a655c4f61466ad71ebfa0071b46a15e917a1d307333c
│   └── bootstrap
├── asset.f153e459ffb70033083e7507aaa06c00f4b13a792fad71433c11b5f050078e74
│   └── bootstrap
├── BasicGoLambdaStack.assets.json
├── BasicGoLambdaStack.template.json
├── cdk.out
├── manifest.json
└── tree.json

4 directories, 8 files
```

After running `cdk synth` to verify things, we can deploy with `cdk deploy`.

## Success

Now we can execute the functions and see them working as expected:

```shell
$ curl -X POST -H "Content-Type: application/json" https://someurl.execute-api.us-west-2.amazonaws.com/todos --data '{ "title": "stuff", "details": "really some stuff"}'
{"id":"80cf566a-937b-4aef-98ef-48b84ee75c01","title":"stuff","details":"really some stuff"}

$ curl -X GET https://someurl.execute-api.us-west-2.amazonaws.com/todos
[{"id":"80cf566a-937b-4aef-98ef-48b84ee75c01","title":"stuff","details":"really some stuff"}]
```

## Summary

The new requirements for using the provided runtimes for Go Lambda Functions poses some minor challenges (especially if you have to migrate already running Go 1.x Lambda Functions) but thankfully the [aws-lambda-go-alpha](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-lambda-go-alpha-readme.html) module makes it much more straightforward than bundling these manually.

I hope you've gotten a good understanding of how to bundle Go Lambda functions by reading this. I know I learned a lot, and this will come in handy as I use Go for Lambdas going forward.

---

Cover photo by [Jesse De Meulenaere](https://unsplash.com/@jessefotograaf) on [Unsplash](https://unsplash.com)