.NET 7 Custom Runtime for AWS Lambda

Full source code available here

Introduction

What if you want to use a runtime that AWS Lambda functions don’t currently support? In this post, I’ll show how to get a .NET 7 application up and running in a Lambda function 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 7, 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 6.2.0 and does NOT have a .NET 7 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 6), that’s the one we want, but it is for .NET 6, so we’ll need a few changes -

Template Name                                         Short Name                                    Language  Tags
----------------------------------------------------  --------------------------------------------  --------  ---------------------
Empty Top-level Function                              lambda.EmptyTopLevelFunction                  [C#]      AWS/Lambda/Serverless
Lambda Annotations Framework (Preview)                serverless.Annotations                        [C#]      AWS/Lambda/Serverless
Lambda ASP.NET Core Minimal API                       serverless.AspNetCoreMinimalAPI               [C#]      AWS/Lambda/Serverless
Lambda ASP.NET Core Web API                           serverless.AspNetCoreWebAPI                   [C#],F#   AWS/Lambda/Serverless
Lambda ASP.NET Core Web API (.NET 6 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 6)               lambda.CustomRuntimeFunction                  [C#],F#   AWS/Lambda/Function
snip..

Create the project

Run -

dotnet new lambda.CustomRuntimeFunction --name LambdaNet7

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 6)" was created successfully.".

.NET 6, not what we want!

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

<TargetFramework>net7.0</TargetFramework>

to

<TargetFramework>net7.0</TargetFramework>

The aws-lambda-tools-defaults.json file

Open aws-lambda-tools-defaults.json, you don’t need to change anything here, but I want to draw you attention to the last line of the file -

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

That is what tells the compiler to bundle the .NET 7 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 function 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\LambdaNet7 and run -

dotnet lambda deploy-function -fn LambdaNet7

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

"C:\dev\aws\...\src\LambdaNet7\bin\Release\net7.0\publish" --configuration "Release" --framework "net7.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 LambdaNet7_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 LambdaNet7 --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 7 running on in a Lambda function! 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