.NET 6 Custom Runtime for AWS Lambda

Want to learn more about AWS Lambda and .NET? Check out my A Cloud Guru course on ASP.NET Web API and Lambda.

Full source code available here

I have a few other posts on running .NET 6 on AWS Lambda.

Introduction

What if you want to use a runtime that AWS Lambda doesn’t currently support? In this post, I’ll show how to get a .NET 6 application up and running in Lambda with a custom runtime.

Amazon.Lambda.Tools will build the zip for you, including all the necessary libraries, and deploy it to a Lambda.

There is one more wrinkle, the Lambda templates do not yet provide a custom runtime template for .NET 6, but that is easy to fix.

Managed vs custom runtimes

With a managed runtime, AWS provides the .NET runtime, makes sure it is kept up to date, and patched as necessary. The zip file you deploy only contains the binaries built from your code.

With a custom runtime, you are bundling the .NET runtime you chose at the time of build and deployment. The zip file you deploy contains the binaries built from your code, and all the required .NET runtime libraries. It is up to you to patch and update as you see fit. It’s very easy to bundle up the runtime, a simple flag to the build command that is already included in the aws-lambda-tools-defaults.json file.

Getting the tools and templates

You need two AWS .NET tools, the Amazon Lambda Tools to build and deploy the lambda, and the Lambda templates, to create the appropriate project.

Run this to install the tools -

dotnet tool install -g Amazon.Lambda.Tools

Run this to install the templates -

dotnet new -i Amazon.Lambda.Templates

At the time of this writing, the latest version is 5.5.0 and does NOT have a .NET 6 template.

Now run dotnet new lambda --list to see all the available templates.

You should see a list like below, note Lambda Custom Runtime Function (.NET 5), that’s the one we want, but it is for .NET 5, so we’ll need a few changes -

Template Name                                         Short Name                                    Language  Tags
----------------------------------------------------  --------------------------------------------  --------  ---------------------
Lambda ASP.NET Core Web API                           serverless.AspNetCoreWebAPI                   [C#],F#   AWS/Lambda/Serverless
Lambda ASP.NET Core Web API (.NET 5 Container Image)  serverless.image.AspNetCoreWebAPI             [C#],F#   AWS/Lambda/Serverless
Lambda ASP.NET Core Web Application with Razor Pages  serverless.AspNetCoreWebApp                   [C#]      AWS/Lambda/Serverless
Lambda Custom Runtime Function (.NET 5)               lambda.CustomRuntimeFunction                  [C#],F#   AWS/Lambda/Function
Lambda Detect Image Labels                            lambda.DetectImageLabels                      [C#],F#   AWS/Lambda/Function
snip..

Create the project

Run -

dotnet new lambda.CustomRuntimeFunction --name LambdaNet6

This will create two projects, one src and one test, for this blog I’m going to ignore the test project.

You’ll see something like -

"The template "Lambda Custom Runtime Function (.NET 5)" was created successfully.".

.NET 5, not what we want!

Navigate to src\LambdaNet6, edit the file LambdaNet6.csproj, change -

<TargetFramework>net5.0</TargetFramework>

to

<TargetFramework>net6.0</TargetFramework>

Changes to aws-lambda-tools-defaults.json

Open aws-lambda-tools-defaults.json, change the framework to net6.0. You’ll notice that the last line of the file is -

"msbuild-parameters": "--self-contained true"

That is what tells the compiler to bundle the .NET 6 runtime in published packages. There is nothing more to building a custom runtime!

The Function.cs file

Update the FunctionHandler method in Function.cs to the following -

public static string FunctionHandler(string input, ILambdaContext context)
{
    var architecture = System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture;
    var dotnetVersion = Environment.Version.ToString();
    return $"Architecture: {architecture}, .NET Version: {dotnetVersion} -- {input?.ToUpper()}";
}

With these changes, the Lambda will tell us what architecture it is running on, and what version of .NET it is running.

Deploying the Lambda

This is where the Amazon Lambda Tools you installed earlier come into use.

Make sure you are in the directory with the source code, if you have been following along it should be something like c:\somewhere\...\src\LambdaNet6 and run -

dotnet lambda deploy-function -fn LambdaNet6

You will see it spit out a bunch of compile/publish info, note that the framework is net6.0 and the runtime is linux-x64.

"C:\dev\aws\...\src\LambdaNet6\bin\Release\net6.0\publish" --configuration "Release" --framework "net6.0" --self-contained true /p:GenerateRuntimeConfigurationFiles=true --runtime linux-x64
Then you’ll be asked which IAM role you want to use, the last number on the list will be for *** Create new IAM Role ***, pick that.

Then enter a name for the role, something like LambdaNet6_role will do.

Finally, you’ll be asked what permissions to grant, select AWSLambdaRole (it is number 9 on my list).

It will then take a moment or two to create the role.

You should then see -

New Lambda function created. 

If you check in the AWS console you should see something like this for the Lambda, note the runtime-

Running the Lambda

To execute the Lambda, run this -

dotnet lambda invoke-function LambdaNet6 --payload "hello lambda"

You will see output that contains -

Payload:
"Architecture: X64, .NET Version: 6.0.0 -- HELLO LAMBDA"

You can also run a test in the AWS console -

There it is, .NET 6 running on in a Lambda! Custom runtimes are very easy to use with Lambda!

In the next post, I’ll show a couple of very small changes to improve cold start times and deployment speed.

Full source code available here

comments powered by Disqus

Related