The easiest way to run and debug .NET AWS Lambda locally

.NET developers who are new to AWS Lambda functions often struggle to figure out how to test them locally. The documentation is somewhat sparse or it proposes excessively complicated ways of doing this. For example, there are articles and YouTube tutorials proposing to use AWS Serverless Application Model Command Line Interface (SAM).

Well, there is nothing wrong with using SAM. It is a powerful command line interface (CLI) tool that provides a powerful emulated AWS environment and many other things. But it’s also not the easiest tool to configure and run. For example, we have to install Docker in order to use it. If all we want is to run and test a Lambda function, it might be overkill to use SAM.

Fortunately, there is a much simple way to test our functions locally that doesn’t rely on any external services, such as docker. There is a tool known as Amazon Lambda Test Tool, also known as Mock Lambda Test Tool. Today, you will see how easy it is to set up and use. But first, we will go through some necessary steps to set up our .NET environment for AWS Lamda development.

Initial environment setup

To work with AWS Lambda in .NET, we could just create a class library project and add all necessary NuGet packages. But a much easier thing to do is to add project templates with all the packages pre-installed and all foundational code already provided. To do so in a CLI, just execute the following command (assuming you have .NET SDK installed already):

dotnet new -i Amazon.Lambda.Templates

Once installed, you can see all new AWS Lambda project templates by executing the dotnet new command. But if you are a Windows user and have Visual Studio 2022 installed, the process is even easier.

Enabling project templates in Visual Studio

In Visual Studio 2022, all we have to do is install the following toolkit:

AWS Toolkit for Visual Studio

It’s a complete AWS development toolkit and not just a collection of AWS Lambda templates. It will allow us to build, run, debug, and deploy a wide range of AWS apps, not just AWS Lambda functions and not just in .NET. Once installed, we will be able to find AWS project templates in the “Create new project menu”, as the following screenshot demonstrates:

Also, this toolkit already contains the Amazon Lambda Test Tool. All we have to do is launch any of our AWS Lambda projects from Visual Studio GUI. The project will be built and the tool will automatically run it. Let’s now see how this tool is used.

Using Amazon Lambda Test Tool

The source code and the full documentation of the latest version of the Amazon Lambda Test Tool is available via the following link:

https://github.com/aws/aws-lambda-dotnet/tree/master/Tools/LambdaTestTool

If we want to be able to use it in CLI, we will need to execute the following command to install it:

dotnet tool install -g Amazon.Lambda.TestTool-6.0

Please note that the -6.0 portion at the end of the tool name is the version of .NET that we are working with. At the time of writing, the latest version of the tool is available for .NET 6; therefore we have 6.0 as the version. In the future, however, this will be updated.

To run the tool, we will need to do two things:

  1. Build the project (e.g. by executing the dotnet build command)
  2. Execute the following command from the project folder:
dotnet lambda-test-tool-6.0

If you are using Visual Studio, you don’t even need to do this. Launching the project from GUI will execute both of these commands automatically.

That’s it. Our AWS Lambda app is now running and we are ready to send events to it.

Testing a basic Lambda app

The most basic type of an AWS Lambda .NET project is a class library that is executed by the AWS runtime. Below is an example of the function handler method of such an app. This is what it looks like by default if we create a project from the Empty AWS Lambda template:

using Amazon.Lambda.Core;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace EmptyLambdaApp;

public class Function
{
    
    /// <summary>
    /// A simple function that takes a string and does a ToUpper
    /// </summary>
    /// <param name="input"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public string FunctionHandler(string input, ILambdaContext context)
    {
        return input.ToUpper();
    }
}

Here, we have a method called FunctionHandler. Our runtime will know that it is the function handler method because it will be referenced in the function-handler field of the JSON config file. This file is called aws-lambda-tools-defaults.json by default.

The handler method can have any JSON-serializable data type as its input and output parameters. It can be any inbuilt C# datatype, such as string and int, or a POCO class. The other input parameter is an implementation of the ILambdaContext interface that provides additional information on the event’s context.

When we launch the test tool, the following web page will open:

To invoke the function handler method, all we need to do is fill the Function Input field with appropriate data and click the Execute Function button. If the shape of the input data matches the data type we used as the input parameter, the JSON-serialized output will be displayed on the page.

Please note that the input data needs to be in JSON format. For example, if we are using string as the data type of the input, the text needs to be enclosed in quotes.

Testing a Lambda app with top-level statements

Another project template we can use is an AWS Lambda project with top-level statements. This project type is different from a basic class library. It runs in its own .NET runtime. AWS just hosts the executable. This is what the executable code will look like by default if we choose this template:

using Amazon.Lambda.Core;
using Amazon.Lambda.RuntimeSupport;
using Amazon.Lambda.Serialization.SystemTextJson;

// The function handler that will be called for each Lambda event
var handler = (string input, ILambdaContext context) =>
{
    return input.ToUpper();
};

// Build the Lambda runtime client passing in the handler to call for each
// event and the JSON serializer to use for translating Lambda JSON documents
// to .NET types.
await LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer())
        .Build()
        .RunAsync();

Here, we are dynamically assigning the function handler action to a variable. Then we are launching a process and adding the handler to it.

But because there is no handler method, we cannot point at it from the JSON configuration file. Therefore the process of running such an app becomes different. First of all, before doing anything, we need to make sure that we have the right configuration in the launchSettings.json file we should have in the Properties folder. If not, we can just create the folder and the file. The content of the file should look similar to this:

{
  "profiles": {
    "Mock Lambda Test Tool": {
      "commandName": "Project",
      "commandLineArgs": "--port 5050",
      "workingDirectory": ".\\bin\\$(Configuration)\\net6.0",
      "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe",
      "environmentVariables": {
        "AWS_LAMBDA_RUNTIME_API": "localhost:5050",
        "AWS_PROFILE": "test-profile",
        "AWS_REGION": "us-west-2"
      }
    }
  }
}

We have our standard tool-specific settings in the Mock Lambda Test Tool section. However, this time we have some additional settings in the environmentVariables section. The most important environmental variable is AWS_LAMBDA_RUNTIME_API. It contains the host and the port our app would run on. In our example, since we set the port to 5050 in the commandLineArgs field, we are setting it to 5050 inside this environment variable also. When set, it will allow the .NET process to listen on this port and pick up any events that have been queued up for execution.

Now, here is a very crucial step. Before we can launch our .NET project, we need to launch the Lambda Test Tool on its own. Otherwise, the .NET project will fail to load as there is nothing running at the address set in the environment variable.

To launch this tool on its own, we just need to open CLI inside our project folder, type the name of the test tool without any parameters, and press Enter. Assuming we are using the .NET 6 version of the tool, the name will be dotnet-lambda-test-tool-6.0. Then we can launch the .NET project.

To test the setup, we will need to open the test tool web page the same way we did before. Only this time, we need to select the Executable Assembly tab on the left. The interface should look as follows:

The principles are the same. We can enter the input event data in JSON format into the Function Input field. Then we’ll need to click the Queue Event button. This will place the event on the queue. If our configuration is correct, the application runner should pick the event up and display the output on the screen.

Testing a Serverless AWS App

There is an additional AWS Lambda application type available in .NET. It is represented by the AWS Serverless App template. Testing this type of application is perhaps the easiest and the process would be familiar to anyone who previously worked with ASP.NET Core.

In the context of .NET, a Serverless Lambda application is nothing but a standard ASP.NET Core application that happens to be hosted in the AWS environment. It has a handful of additional dependencies that make it interoperate with AWS runtime. But otherwise, it’s just a standard ASP.NET Core application.

Because it’s it is a standard web app, it can be executed and debugged in the same way we can execute and debug any other ASP.NET Core application. We can just launch it from the IDE or by executing the dotnet run command in the project folder. Once launched, we can use its HTTP controller and Minimal API endpoints the same way we can use it normally. We shouldn’t expect to see any blocking errors just because we don’t happen to run it inside AWS.

Further reading

This article provided just some basic information on how to run and debug AWS Lambda apps locally. However, there is much more than that to learn if you want to become proficient at building such apps. To find out more, you can visit the official documentation for the AWS Lambda .NET SDK.


P.S. If you want me to help you improve your software development skills, you can check out my courses and my books. You can also book me for one-on-one mentorship.