.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.

  1. Running the sam init command gives you a guided experience to initialize a new SAM project from a set of predefined templates.
  2. Edit the template.yaml file, which contains the definition of the resources to create for the serverless application.
  3. 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.
  4. 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.

SAM Directory Structure

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).

Example build directory contents

Figure 2: Example build directory contents

Besides the compiled application code, the process produces a transformed template.yaml 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.

  1. 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.
  2. 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.
  3. 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.