AWS Developer Tools Blog
Building and Debugging .NET Lambda applications with .NET Aspire (Part 1)
In a recent post we gave some background on .NET Aspire and introduced our AWS integrations with .NET Aspire that integrate AWS into the .NET dev inner loop for building applications. The integrations included how to provision application resources with AWS CloudFormation or AWS Cloud Development Kit (AWS CDK) and using Amazon DynamoDB local for local development. These features can keep .NET developers in the development environments they are most productive in.
AWS Lambda has it’s own programming model for responding to events, and we are frequently asked how to run and debug Lambda functions locally; that is, essentially, how to F5 debug a collection of Lambda functions that make up a serverless application. Today we have released a developer preview for local Lambda development support that provides this feature in a .NET idiomatic experience.
With the new Lambda support, you can use the .NET Aspire app host to orchestrate the Lambda functions that make up your serverless applications. When the app host is launched from Visual Studio, all of the Lambda functions are connected to the debugger. In addition to connecting the Lambda functions to the app host, the functions can also be orchestrated through an API Gateway emulator to provide the full experience of invoking Lambda functions through a REST API.
Developer Preview
Our Lambda features are currently in developer preview. To signify the preview status, the Lambda APIs in our Aspire.Hosting.AWS package have been marked with the RequiresPreviewFeatures .NET attribute. In order to use the Lambda API you need to opt-in to preview features; otherwise, a compilation error will be triggered.
You can opt-in to the Lambda preview features by adding the following line at the startup of the app host’s Program.cs
file.
One known issue with the developer preview is that the Rider IDE from JetBrains is not able to start Lambda functions that are defined in a class library. Lambda functions written as an executable, also referred to as using top level statements, will work with no issue in Rider. The Rider team has made a fix that address the Lambda class-library issue, which will be available in an upcoming release.
Getting Started
To get started using .NET Aspire with Lambda, let’s walk through how you can enable .NET Aspire for an existing .NET Lambda project. In this scenario, say that I have a .NET Lambda project called “LambdaWebCalculator”, which is a collection of Lambda functions that are exposed as a REST API through Amazon API Gateway. The steps used for enabling .NET Aspire for this function can be used for any .NET Lambda project, but for context, here is a sample of the code in the aforementioned .NET Lambda project.
Setting up the .NET Aspire app host project
To get started using .NET Aspire to run and debug our Lambda functions, we need to create a .NET Aspire app host project. This project represents our local orchestration for running the pieces of the distributed application. In our Lambda scenario, the app host orchestrates running all of the Lambda functions.
Use the following steps to create an app host for Lambda.
- In the solution, add a new project using the .NET Aspire App Host project template. For the .NET Aspire version you should choose the latest version. As of this writing the latest .NET Aspire version is 9.1. Common convention is to name the app host project
<solution-name>.AppHost
.
- On the AppHost, add a NuGet reference to Aspire.Hosting.AWS version 9.1.4 or above.
- On the AppHost project, add a project reference to the Lambda project.
- In the
Program.cs
file of the AppHost, we need to add the#pragma
to enable using the preview Lambda APIs from Aspire.Hosting.AWS. Then use theAddAWSLambdaFunction
method to register all the Lambda functions we want to run and debug locally. In my example I have the four Lambda functions for the Add, Minus, Multiple, and Divide features of my REST API calculator. - Make the AppHost project the startup project and push F5 to launch the Visual Studio debugger.
- From the Aspire dashboard, the Lambda functions are added as a resource as well as the Lambda service emulator. To debug a Lambda function, click the debug icon in the Actions column for the Lambda project.
- The debug icon launches the Lambda test page where Lambda events can be invoked. Breakpoints can be set in the Lambda function for debugging.
API Gateway Emulation
In my existing .NET Lambda project, all my Lambda functions were meant to be called from an Amazon API Gateway endpoint. This means that I want my web calculator to be called as a REST API through HTTP clients like web browsers. With our Lambda integration, we have added the ability to orchestrate your Lambda functions in the app host as if they were running in API Gateway.
To enable the API Gateway emulator, use the AddAWSAPIGatewayEmulator
method, and include Lambda functions with routes using the WithReference
method. Note that if your route uses a wildcard path, use the {proxy+}
path variable, similar to the way in which you would define wildcard routes in the CloudFormation template.
Now when the app host is launched, the API Gateway emulator resource is included in the list of resources. The endpoint shown for the API Gateway emulator can be used to make REST requests that invoke the Lambda functions.
In my example I have registered the addFunction
Lambda function with the HTTP verb GET
and the resource path /add/{x}/{y}
. So if I make a request to http://localhost:<apigateway-emulator-port>/add/1/2
, my add Lambda function will be invoked in the debugger where breakpoints will be honored and I’ll get back the response.
Accessing Logs
The logs generated from the Lambda function can be viewed in the .NET Aspire dashboard by clicking on the Console tab and selecting the Lambda function in the Resources dropdown list.
By default, the Lambda function will log at the INFO
level in text format, matching the default behavior of functions deployed to Lambda. Using the LambdaFunctionOptions
class with the AddAWSLambdaFunction
method, the format and log level can be adjusted. The following code sets the log level to debug and the format to JSON.
The Console Logs view will now show the debug statements coming from the Lambda function, and format the messages as JSON documents.
Connecting to AWS
As mention in the previous .NET Aspire blog post for AWS, the configuration that the AWS SDK for .NET should use to connect to AWS can be created using the AddAWSSDKConfig
method. The configuration can be assigned to projects by calling the WithReference
method on the project. A .NET Lambda project is also a project, and the same pattern can be used for .NET Lambda projects.
Conclusion
With the Lambda integration with .NET Aspire, we hope to provide an experience for building .NET Lambda functions that is idiomatic and simple to get started. Once the app host is set up, all that team members of a project need to do is to clone or pull the latest changes from the repo and start debugging immediately. The Lambda integration can be used in combination with our previous integrations for provisioning resources with AWS CloudFormation and having Lambda functions that use Amazon DynamoDB local for the backend.
In this blog post, which is Part 1, you learned how to use .NET Aspire to run and debug .NET Lambda functions locally. In the next blog post, which is Part 2, you will learn how to incorporate .NET Aspire’s programming model for connecting additional components and sharing best practices across Lambda functions.
The development of this feature is happening in our aws/integrations-on-dotnet-aspire-for-aws repository. The following GitHub issue is being used as the main tracker issue for the Lambda integration: https://github.com/aws/integrations-on-dotnet-aspire-for-aws/issues/17. We ask that .NET developers who are building Lambda functions try out this preview and let us know on our repository your success stories and any issues with our Lambda integration.