.NET on AWS Blog

.NET Workflows for arm64 with Amazon CodeCatalyst: Part 1

Overview

Amazon CodeCatalyst is a cloud-based collaboration space for software development teams, including creating CI/CD workflows to automatically build and deploy software. CodeCatalyst recently added support for arm64 compute, using AWS Graviton, for workflows. Graviton is the AWS arm64 compute platform. Arm64 is supported in both CodeCatalyst compute models: on-demand and pre-provisioned. Versions of .NET starting with .NET 5 included numerous performance optimizations for arm64, and AWS customers typically see price-performance gains of up to 40% compared to x86-64 compute. In this blog post, I’ll show you how to use CodeCatalyst to build, test and deploy a serverless ASP.NET Core application to Lambda on arm64. In part 2, I’ll do the same for deploying to Amazon ECS with AWS Fargate on arm64.

.NET on Arm64

New .NET versions are released annually, and at the time this blog was published, .NET 6 is the current LTS (long-term support) version, while .NET 7 is the current STS (standard-term support) version. Most .NET applications built on these versions (or later) will run fine without changes on arm64 processors, but it’s possible that some 3rd-party libraries or packages could have hard dependencies on x64. For this reason, even though .NET supports cross-compiling for arm64 from an x64 platform, for production workflows you should aim to build and test your code on the same platform on which it will run.

Modernizing legacy .NET Framework applications

Running .NET applications on Linux with Graviton arm64 compute is the most cost-effective option for most multi-threaded .NET applications. Many existing applications are built on .NET Framework, which runs on Windows only. AWS offers tools to help you migrate those applications to Linux by porting the code from .NET Framework to cross-platform .NET (previously called .NET Core):

Deploy .NET Serverless Application to Lambda on Graviton

Prerequisites

Walkthrough

For this walkthrough, you will use the existing .NET serverless application blueprint, and then modify it to build on arm64 compute, and then deploy to a Lambda running on arm64. Starting in any CodeCatalyst space, create a new project. Choose the .NET serverless application blueprint, then select Next. The CodeCatalyst console will display a readme file on the right side with information about this template.  Scroll down to the “Connections and permissions” section to review how CodeCatalyst will access resources in your AWS account. Then follow the steps below to create the project.

  1. Provide a name for your project, and choose an AWS account connection. The connection you choose represents the AWS account to which your serverless application will be deployed.
  2. For “Deployment role”, choose the AWS IAM role that you configured when you linked your AWS account. At the time this blog was written, the default role name is of the form “CodeCatalystPreviewDevelopmentAdministrator-xxxx
  3. For AWS Lambda Project Type, choose ASP.NET Core (C#).
  4. Expand the Code node, and choose the AWS region you will deploy to, then select Create project.
  5. After the project is created, the default workflow that runs the included unit tests and deploys the application will kick off, deploying to Lambda running on x86-64 (which this blueprint uses by default).

Now you will add some code to the project that outputs the CPU architecture, to validate that it runs on arm64 after customizing the workflow. Then, you will get the URL for the deployed ASP.NET Core application to view it in a browser.

  1. On the left menu, expand the Code menu node, and choose Source repositories.
  2. From the repository list, click on the repository name that you used earlier to show the files in the repo.
  3. In the Files tree view, navigate to /src/ServerlessAspNetOnArm/Startup.cs (replacing “ServerlessAspNetOnArm” with the project name you choose) and select the file.
  4. Choose Edit (at the top right) to switch to editing mode.
  5. In the file, replace this line:
    await context.Response.WriteAsync("Welcome to running ASP.NET Core on AWS Lambda");
    With this:
    await context.Response.WriteAsync("Welcome to running ASP.NET Core on AWS Lambda on "
    + System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString());
  1. Select Commit (at the top right) to commit your change directly to the main branch. After committing the change, the main workflow that deploys the application will run again, so let’s verify that the Lambda is currently running on x86-64.
  2. On the left menu, expand the CI/CD menu node, and choose Workflows.
  3. In the workflow list, under the main workflow, expand the Recent runs expander node, then select the most recent run of the workflow to show the details for that run in the visual layout view. You should see three actions, similar to the following:

Visual layout view of the latest workflow run showing three completed actions.

  1. Select the Deploy_To_AWS action to show the details of the latest run of this action, which will display on the right.
  2. On the action details pane on the right, under the Logs tab, you can see each command that was run for this action. Find the step that runs the command, dotnet lambda deploy-serverless, and expand that node to view the logs for this command.
  3. Scroll down through the logs to the last few lines, where you should see that the deployment outputs the URL of the API Gateway API for your serverless application in this format:
Output Name          Value
-------------------- --------------------------------------------------
ApiURL               https://abcdef0a1b.execute-api.us-west-2.amazonaws.com/Prod/
  1. Copy the URL shown under Value into a browser.  You should see the message: “Welcome to running ASP.NET Core on AWS Lambda on X64”. Don’t lose the URL, since you will need it again after changing to arm64.

Finally, follow the steps below to modify the workflow to build the serverless application on arm64, and deploy it to Lambda on arm64.

  1. While still viewing the workflow, select Edit workflow at the top right to enter edit mode.
  2. Select the Visual mode if it’s not already selected (your workflow may be in YAML mode by default).
  3. In the visual layout mode, select the Build_And_Test action to show the configuration options for this action on the right.
  4. On the right pane, select the Configuration tab to show the options for compute type.
  5. Under Compute fleet – optional, choose an Arm64 instance, such as “Linux.Arm64.Large” as in the example below.

The Compute type options in the build action configuration tab.

  1. Next, click on the Deploy_To_AWS action to show its configuration options.
  2. On the Configuration tab, select an Arm64 instance type for the compute for this action also.
  3. Validate that the workflow is  still valid by selecting Validate at the top right. After  validating, select Commit to commit your changes.

Note that the changes are actually to the /.codecatalyst/workflows/main.yaml file, and that this commit will kick off another workflow run.

  1. Finally, navigate back to the files repository, to the serverless.template file inside the project folder in /src/.  Enter edit mode for this file.
  2. Find the first Resources node in the file, and add a Properties node to the JSON with the arm64 architecture specified, as shown below (the bold section is what you need to insert):
"Resources": {
    "AspNetCoreFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {         "Architectures": [           "arm64"         ],
        "Handler": "ServerlessAspNetOnArm::ServerlessAspNetOnArm.LambdaEntryPoint::FunctionHandlerAsync",
        "Runtime": "dotnet6",
  1. Navigate back to “main” workflow in CI/CD, and wait for the workflow to finish deploying (you can click on the current run to track its progress).
  2. Once the workflow has completed, in a browser, navigate again to the URL you visited earlier (or refresh the browser tab). You should now see the message, “Welcome to running ASP.NET Core on AWS Lambda on Arm64”

Congratulations! You have modified the serverless .NET blueprint to build on and deploy to arm64.
If you don’t want to keep the serverless application in your AWS account, locate the CloudFormation stack in your AWS account’s console, and delete the stack to remove all its resources.

Cleanup

In order to avoid incurring ongoing charges for your application, you can tear down all the resources by deleting the AWS CloudFormation stack that the CDK used to provision them. In the AWS Management Console for the AWS account you deployed to, navigate to the CloudFormation console, locate the stack, and delete it. Unless you changed it when you created the project, the stack name will be of the form, “serverless-stack-xxxx”. If you wish to deploy the application again, you can run the CodeCatalyst workflow again, and it will re-provision the resources.

Conclusion

AWS Graviton arm64 processors offer compelling price-performance for .NET workloads in the cloud. In this post, you learned how to create a CodeCatalyst workflow for Lambda functions that builds the code and runs unit tests using Graviton compute in CodeCatalyst, and then deploy to Lambda on Graviton. Get started building your own .NET workflows in Amazon CodeCatalyst today, and learn more about CodeCatalyst workflows and blueprints in the documentation. In part 2 of this blog, to be published soon, we’ll deploy an ASP.NET Core application to ECS Fargate on Graviton, with the build and test running on arm64 compute.