AWS Lambda Functions for Dotnet Core 3.1 using a custom runtime

AWS Lambda Functions for Dotnet Core 3.1 using a custom runtime

AWS only support the LTS versions of dotnet core. At the time of writing the latest LTS version is 3.1. AWS, however, have not yet implemented native support for 3.1 although they are working on it.

In the meantime, to use the native runtime you must be running dotnet core 2.1. If you're trying to target shared projects, these must be targeting dotnet standard 2.0 and not standard 2.1. You may be able to downgrade but maybe you can't. To tackle these situations AWS provide what they call a Custom Runtime Function. Using this we're able to use the Lambda functions using the dotnet core 3.1 runtime (or indeed any other runtime version we like).

Create Function in AWS

For this example we'll create the lambda function in the AWS console.

When creating the function select Author from scratch, give the function a name and ensure you select the runtime to be Custom runtime: Provide your own bootstrap

Create AWS Lambda Function
Create AWS Lambda Function

We'll later see how we provide this bootstrap.

Create Lambda Function project

The easiest way to get started is to install the latest version of the Amazon Lambda Templates. Then use these templates to create the boiler-plated Custom Runtime Lambda Function.

dotnet new -i Amazon.Lambda.Templates

Then create the project using the Custom Runtime Function template.

dotnet new lambda.CustomRuntimeFunction --name lambda-dotnet-3.1

This will create the function project with a number of files. These are described below.

Custom Runtime package

The newly created project should include a reference to Amazon.Lambda.RuntimeSupport but in case you’re changing an existing Lambda function project you’ll need to install the Lambda RuntimeSupport NuGet package.

dotnet add package Amazon.Lambda.RuntimeSupport

Bootstrap

#!/bin/sh
# This is the script that the Lambda host calls to start the custom runtime.

/var/task/lambda-dotnet-3.1

Replace lambda-dotnet-3.1 with the name of your lambda project.

Ensure that the bootstrap file is included in the build. Add the following to your .csproj if it hasn't already been included.

<ItemGroup>
  <Content Include="bootstrap">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </Content>
</ItemGroup>

Lambda configuration

aws-lambda-tools-defaults.json is another file that will have been created when we set up the Lambda function project.

The key parts of this file are:

  • framework where we state the runtime we want to use
  • function-runtime which must be set to provided in order for the Lambda function to know to use our Custom Runtime
  • function-handler which does not need to be set in order for the function to successfully run.

Usually for thefunction-handler you can add lambda-dotnet-3.1::lambda-dotnet-3.1.Function::FunctionHandler which allows the Amazon Lambda TestTool (dotnet tool install -g Amazon.Lambda.TestTool-2.1 to install) to run the function locally. However this will not work for custom runtime. Currently the Amazon Lambda TestTool only supports the 2.1 dotnet runtime.

Example file:

{
  "Information": [
    "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
    "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",

    "dotnet lambda help",

    "All the command line options for the Lambda command can be specified in this file."
  ],

  "profile": "default",
  "region": "eu-west-2",
  "configuration": "Release",
  "framework": "netcoreapp3.1",
  "function-runtime": "provided",
  "function-memory-size": 256,
  "function-timeout": 30,
  "function-handler": "not_required_for_custom_runtime",
  "msbuild-parameters": "--self-contained true"
}

Invoking the bootstrap

Here is the boilerplate code that shows how the Custom Runtime is used. Simply add your code into the FunctionHandler.

/// <summary>
/// The main entry point for the custom runtime.
/// </summary>
/// <param name="args"></param>
private static async Task Main(string[] args)
{
    Func<string, ILambdaContext, string> func = FunctionHandler;
    using(var handlerWrapper = HandlerWrapper.GetHandlerWrapper(func, new JsonSerializer()))
    using(var bootstrap = new LambdaBootstrap(handlerWrapper))
    {
        await bootstrap.RunAsync();
    }
}
/// <summary>
/// A simple function that takes a string and does a ToUpper
///
/// To use this handler to respond to an AWS event, reference the appropriate package from
/// https://github.com/aws/aws-lambda-dotnet#events
/// and change the string input parameter to the desired event type.
/// </summary>
/// <param name="input"></param>
/// <param name="context"></param>
/// <returns></returns>
public static string FunctionHandler(string input, ILambdaContext context)
{
    return input?.ToUpper();
}

Project configuration

Here is the complete ‘.csproj’ for the working example.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <LangVersion>latest</LangVersion>
    <AWSProjectType>Lambda</AWSProjectType>
  </PropertyGroup>
  <ItemGroup>
    <None Remove="bootstrap" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="bootstrap">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Amazon.Lambda.RuntimeSupport" Version="1.1.0" />
    <PackageReference Include="Amazon.Lambda.Core" Version="1.1.0" />
    <PackageReference Include="Amazon.Lambda.Serialization.Json" Version="1.7.0" />
  </ItemGroup>
</Project>

Deploying the function

Install the global dotnet cli tool so that we can easily deploy and test our function.

dotnet tool install -g Amazon.Lambda.Tools

or if you already have this installed you can check for updates

dotnet tool update -g Amazon.Lambda.Tools

To deploy the Lambda function. The name is the name you used when you created your function in AWS.

dotnet lambda deploy-function --name lambda-dotnet-3-1

You may need to configure your AWS credentials. See AWS CLI documentation for information.

Then, in order to invoke and test your function when hosted in AWS, use the following.

dotnet lambda invoke-function --name lambda-dotnet-3-1

References

https://aws.amazon.com/blogs/developer/net-core-3-0-on-lambda-with-aws-lambdas-custom-runtime/

https://aws.amazon.com/blogs/developer/announcing-amazon-lambda-runtimesupport/

https://github.com/aws/aws-lambda-dotnet/issues/554