.NET 7 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
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>net6.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
*** 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