.NET on AWS Blog
Building Serverless .NET Applications with AWS Lambda and the SAM CLI
When building serverless .NET applications in AWS, the AWS Serverless Application Model (AWS SAM) provides a shorthand syntax to model resources such as AWS Lambda functions, Amazon API Gateway, Amazon DynamoDB tables, and event source mappings. AWS SAM helps you to build, test, and deploy your cloud-based .NET applications quickly in the cloud.
This blog post shows how you can build .NET applications with the AWS SAM Command Line Interface (CLI). We will use an example of a serverless API using Amazon API Gateway and AWS Lambda. Then, we’ll show how native ahead of time (AOT) compilation with .NET 7.0 can help improve the overall performance of the Lambda function.
Overview of Solution
You can deploy a serverless application using the AWS SAM CLI in just a few simple steps.
- Running the
sam init
command gives you a guided experience to initialize a new SAM project from a set of predefined templates. - Edit the
template.yaml
file, which contains the definition of the resources to create for the serverless application. - Run the
sam build
command to compile the .NET application code and prepares the deployment template. These artifacts are output to the “.aws-sam/build” folder. - Use the
sam deploy
command to upload the template file and compiled function code from the build folder to Amazon S3,
The sam deploy
command uploads your template and function code from the ./aws-sam/build
folder and starts a deployment using AWS CloudFormation
Prerequisites
To get started building .NET with AWS SAM, there are only a few prerequisites to get started.
- The .NET 6.0 SDK, which will allow you to build the Lambda function code. If you are going to use the native AOT functionality available in .NET 7.0, you will need the .NET 7.0 SDK.
- The AWS SAM CLI tool, to build and deploy your SAM application.
- An Integrated development environment, such as Visual Studio Code.
- An AWS Account that you can use to deploy your serverless application, along with an AWS Credentials profile allowing you to access that account.
- Docker Desktop configured to run Linux containers. Only required if you are running the build process within a container.
Initialize a New Project
To get started, create an empty directory for your project. Navigate there in a terminal (such as PowerShell on Windows, or a bash terminal on a Mac). Run the command “sam init”. This will start the simple guided experience to create the project. Select the following values:
- Which template source would you like to use?
Enter 1 (AWS Quick Start Templates). This will give you a selection of predefined templates that you can use to get started. - Choose an AWS Quick Start application template
Enter 7 (Hello World Example With Powertools). This is a basic template that includes the AWS Lambda Power Tools for .NET. - Which runtime would you like to use?
Enter 1, which is .NET 6.0. Note: when you select .NET 6, the SAM CLI will automatically make several additional selections for you. It will choose “Zip” as the packaging format (as opposed to container image). - Choose N when prompted to enable X-Ray tracing and CloudWatch Application Insights.
In a production environment, you are encouraged to use observability tools such as AWS X-Ray and Amazon CloudWatch Application Insights to facilitate troubleshooting and better understand your application’s behavior and performance. - Project Name
Enter dotnet-with-sam. This is simply the name of your project.
As the project initializes, SAM generates the application structure in a new folder named dotnet-with-sam
. Some of the important items that are present include (Figure 1):
- A
src
directory contains the ‘HelloWorld’ Lambda function project. - A
test
directory contains a sample unit test project. - The
template.yaml
template file, which defines the AWS infrastructure for this application. - An
events
directory that contains a test event file. This file simulates an API request from API Gateway, which triggers the sample Lambda function and responds with the Lambda execution environment’s IP address.
Figure 1: SAM Directory Structure
The template.yaml
file contains the resource definitions for your serverless application. AWS SAM provides a syntax that is an extension of AWS CloudFormation created to facilitate serverless development. In the provided sample, the Lambda function is defined with the necessary configuration. The Globals section applies additional configuration to all functions in the template.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
Sample SAM Template with AWS Lambda Powertools for dotnet-with-sam
Globals:
Function:
Timeout: 10
MemorySize: 256
Runtime: dotnet6
Api:
TracingEnabled: true
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
Handler: HelloWorld::HelloWorld.Function::FunctionHandler
CodeUri: ./src/HelloWorld/
Description: Hello World function w/ AWS Lambda Powertools for .NET
Architectures:
- x86_64
Tracing: Active
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Environment:
Variables:
POWERTOOLS_SERVICE_NAME: PowertoolsHelloWorld
Note the “Events” section of the “HelloWorld” function. This section of each function defines the events that can trigger the Lambda function. AWS SAM supports a range of event sources. When an event source is added to a function, SAM will automatically generate the minimum required AWS Identity and Access Management (IAM) permissions necessary. This allows the developer to focus on defining application structure, rather than the specific underlying permissions. For this example, SAM will automatically create a new API Gateway REST API because the “Type” is set to “Api”. It will create the “/hello” route and set up the integration between API Gateway and the Lambda function. It will also add the necessary resource policy to the Lambda function configure to allow the API Gateway to invoke it.
Built in .NET Build Support
AWS SAM ships with the aws-lambda-builders library, which enables SAM to compile your .NET application as a part of the build project. The AWS SAM .NET Lambda builder uses the dotnet lambda package command from the Amazon.Lambda.Tools .NET Global CLI package. The dotnet lambda package command runs within the folder specified in the “CodeUri” property of each function defined in the template.yaml file. On completion, the SAM CLI extracts the compiled application files into the ./.aws-sam/build folder before deleting the generated ZIP artifact.
Build the SAM Application
Start the build process by running the following command from the directory containing the template.yaml
file.
sam build
The build process will then build each Lambda function in the template sequentially. As a result, in the ./aws-sam/build/ directory, the compiled artifacts will each be included in a separate directory, ready for deploy (Figure 2).
Figure 2: Example build directory contents
Besides the compiled application code, the process produces a transformed template.yam
l file with the CodeUri property updated to reference the folder containing the compiled application artifacts.
Build the SAM Application in a Container
AWS SAM includes functionality to let you run the build process to run inside a Docker container. This enables the compilation of the serverless applications independently of the local workstation. This is useful when running SAM within CI/CD pipelines, where the development tools may not be installed.
Before starting a build process inside a container, ensure you have Docker running. Start the build process by running this command from within the directory containing the template.yaml
file.
sam build --use-container
By default, the build process uses container images maintained in the aws/aws-sam-build-images GitHub repository. AWS SAM pulls the container image depending on the runtime defined in the template. In this example, it pulls the public.ecr.aws/sam/build-dotnet6:latest-x86_64
image, because the .NET 6.0 runtime is selected. The folder specified in the CodeUri property is mounted on the container and SAM runs the default build process inside the container. The compiled artifacts are output to the ./aws-sam/build
folder on the local file system.
If you require a more customized approach, you can create your own custom build image. When running the sam build command, you can pass in the custom build-image name to specify the build container image to use:
sam build --use-container --build-image dotnet/custom-build-image
When using a custom build image, ensure that you install all the required build dependencies so that the build will fully succeed.
Deploy the Serverless .NET Application
Start the deployment process by running the following command from the directory containing the template.yaml
file.
sam deploy --guided
The –guided flag walks you through a wizard to configure each aspect of the deployment.
Note: when you deploy with this command, it will use the default credentials profile. If you want to use a named profile, run the command “sam deploy –guided –profile credentials-profile-name” instead, substituting your desired profile name.
For this deployment:
- Stack Name
Enter dotnet-with-sam, which is the name of the project. This will be used as the CloudFormation stack name during deployment. - AWS Region
Enter the region to deploy your application to. By default, this will select the region “us-east-1”. If you want to deploy to a different region, specify it. - Confirm changes before deploy
Enter N. This removes requirement for you manually confirm during the deploy. - Allow SAM CLI IAM role creation?
Enter Y. This allows SAM to create IAM roles for the application on your behalf. - Disable Rollback?
Enter N. In the event of a deploy failure, the entire stack will be rolled back to the previous state. - HelloWorldFunction may not have authorization defined. Is this okay?
Enter Y. This acknowledges that the API gateway that will be created will allow anonymous access. - Save arguments to configuration file. Enter Y.
SAM configuration file: Press Enter to select the default config file name.
SAM configuration environment: Press Enter to keep the default value.
Saving the values to the configuration file allows you to re-deploy with the same settings without requiring re-keying all of this information. By using the configuration environment, you can deploy the same project to separate AWS environments using the same code.
AWS SAM will zip and upload the function code and template.yaml to an Amazon S3 bucket that it creates before initiating the CloudFormation deployment. Once the deploy has completed, the outputs from the SAM deployment, including the newly created API Gateway URL, will be output to the terminal window.
Increase Developer Velocity with SAM Accelerate
SAM Accelerate synchronizes changes between your project and the deploy serverless application in AWS quickly and automatically. It will monitor your local file system for changes to the application code, automatically re-compile and sync the changes to AWS using the fastest method possible. Depending on the type of change, this will either use direct AWS API calls (e.g. aws lambda update-function-code) or a full CloudFormation re-deployment.
You can use SAM Accelerate by running the following command in the directory containing template.yaml.
sam sync –watch
You will be prompted with a message asking you to confirm that you are synchronizing a development stack. Enter Y.
AWS SAM will now watch the local file system for changes and automatically re-deploy. Open up the Function.cs
file in the ./src/HelloWorld
directory and update the message code on line 56 from
{ "message,", "hello world" },
to
{ "message", "hello .NET Developers!" },
When you save the file, SAM CLI will compile and re-deploy the changes. If you navigate to the API Gateway URL, you will immediately see the updated output.
Deploy pre-compiled artifacts without using sam build
In a scenario in which you have a build process that is highly customized, or you need to use a previously established build process, you can still use AWS SAM to deploy your serverless applications.
Here, you need to change the CodeUri property of the affected functions to point at the directory containing the pre-built application files rather than the directory containing the .csproj file and source code.
If all the functions in the template are pre-compiled, you can skip the sam build step and jump straight to sam deploy. However, if your application contains a mix of functions that SAM needs to compile as well as pre-compiled functions, then add the SkipBuild flag to the Metadata section of the template for each function that does not need to be built.
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
# Removed for brevity
Metadata:
SkipBuild: true
Using native AOT to improve performance
AWS SAM contains built in support for using native ahead of time compilation included with .NET 7.0, which can improve the performance of your .NET Lambda functions. Creating a project that can use native AOT with .NET 7.0 requires some different selections when running the “sam init” command:
- Which template source would you like to use?
Enter 1 (AWS Quick Start Templates). This will give you a selection of predefined templates that you can use to get started. - Choose an AWS Quick Start application template
Enter 1 (Hello World Example). - Use the most popular runtime and package type? (Python and zip)
Enter N. - Which runtime would you like to use?
Choose 1, which is “aot.dotnet7”. This will select a template that is preconfigured for .NET 7.0 AOT.
The rest of the selections as are the same as the previous, .NET 6.0 example.
To use native AOT with SAM first ensure your .NET application code is written to support native AOT. More details on the application code can be found in this blog post. You must also ensure Docker is running on the build machine, since the build architecture will need to match the destination architecture.
In the template.yaml
file, note that there are some differences in the HelloWorld function definition.
- The BuildMethod flag has been added to the MetaData section of the function definition, indicating to SAM that it should build this function using .NET7 with AOT.
- The Handler has been updated to “bootstrap”. This is because .NET 7.0 is not a managed runtime in AWS Lambda, so the function will use a custom runtime.
- The Runtime is set to “provided.al2”, indicating to the Lambda function the runtime is provided by the customer, and should use the Amazon Linux 2 operating system when running this function..
HelloWorldFunction:
Type: AWS::Serverless::Function
Metadata:
BuildMethod: dotnet7
Properties:
CodeUri: ./src/HelloWorld/
Handler: bootstrap
Runtime: provided.al2
Architectures:
- x86_64
MemorySize: 256
…
Native AOT compilation uses the same process as building using a container image. A Docker image is downloaded and the CodeUri path is mounted on the running container. The compiled application files are then written to the ./aws-sam/build
folder.
Cleanup
To cleanup any deployed resources, run the below command from the folder containing the template.yaml
file.
sam delete
You will need to run this for each serverless .NET SAM application that you deployed. If you deployed the example both with .NET 6.0 and .NET 7.0, this command will need to be run for each to clean up.
Conclusion
This blog post shows how to build .NET applications with the AWS SAM CLI. You learned how to get started with AWS SAM, how to quickly define event sources, how to build and deploy your serverless applications as well as adding native AOT support for high performance .NET Lambda functions. For more examples and benchmarks of .NET on Lambda, visit the serverless-dotnet-demo GitHub repository.
To learn more, dive deep into the AWS SAM documentation. For more serverless learning resources, visit Serverless Land.