0

Now that .Net Core 3 has dropped and AWS announced Custom Runtimes, I'm looking to take advantage of some of the new .Net Core 3 features. Unfortunately, I'm coming up short when trying to find information on how to do this using the Serverless Framework. Has anyone out there done this yet? And if so, is there a good online resource on how to do so?

2
  • I would recomend you do further research from what I hear "serverless" tends to cost more and deliver less. You'd be better off with a well programmed Kubernetes orchestration Commented Oct 1, 2019 at 18:49
  • @Felype I appreciate the suggestion. Unfortunately, we've gone all in on Serverless until we can formally decide to change directions. My question still stands as a result. Commented Oct 2, 2019 at 21:10

1 Answer 1

3

The short answer is 'yes'.

Amazon state:

The Lambda team’s policy is to support Long Term Support (LTS) versions of a runtime so .NET Core 3.0 will not be natively supported on AWS Lambda.

That doesn’t mean you can’t use .NET Core 3.0 on Lambda today though. With the Amazon.Lambda.RuntimeSupport NuGet package you can use any version of .NET Core including 3.0. This is possible because one of the great features of .NET Core is the ability to package up an application as a completely self contained deployment bundle. The following blog post shows how to use Amazon.Lambda.RuntimeSupport. https://aws.amazon.com/blogs/developer/announcing-amazon-lambda-runtimesupport/

We have successfully deployed a .Net Core 3.0 Web Api to AWS Serverless.

Here's how I did it:

  1. Added NuGet packages:
 Amazon.Lambda.AspNetCoreServer 
 Amazon.Lambda.RuntimeSupport
  1. Added Lambda entry point
    public class LambdaEntryPoint :
        // When using an ELB's Application Load Balancer as the event source change 
        // the base class to Amazon.Lambda.AspNetCoreServer.ApplicationLoadBalancerFunction
        Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction
    {
        /// <summary>
        /// The builder has configuration, logging and Amazon API Gateway already configured. The startup class
        /// needs to be configured in this method using the UseStartup<>() method.
        /// </summary>
        /// <param name="builder"></param>
        protected override void Init(IWebHostBuilder builder)
        {
            builder.UseStartup<Startup>();
        }
    }
  1. Updated Main function
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Amazon.Lambda.Core;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.RuntimeSupport;
using Amazon.Lambda.Serialization.Json;

namespace CustomRuntimeAspNetCore30
{
    public class Program
    {

        public static void Main(string[] args)
        {
            if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME")))
            {
                CreateHostBuilder(args).Build().Run();
            }
            else
            {
                var lambdaEntry = new LambdaEntryPoint();
                var functionHandler = (Func<APIGatewayProxyRequest, ILambdaContext, Task<APIGatewayProxyResponse>>)(lambdaEntry.FunctionHandlerAsync);
                using (var handlerWrapper = HandlerWrapper.GetHandlerWrapper(functionHandler, new JsonSerializer()))
                using (var bootstrap = new LambdaBootstrap(handlerWrapper))
                {
                    bootstrap.RunAsync().Wait();
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}
  1. Added serverless.template file:
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.",
  "Parameters": {},
  "Conditions": {},
  "Resources": {
    "AspNetCoreFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Handler": "not-required",
        "Runtime": "provided",
        "CodeUri": "",
        "MemorySize": 256,
        "Timeout": 30,
        "Role": null,
        "Policies": [
          "AWSLambdaFullAccess"
        ],
        "Environment": {
          "Variables": {
            "LAMBDA_NET_SERIALIZER_DEBUG": "true"
          }
        },
        "Events": {
          "ProxyResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/{proxy+}",
              "Method": "ANY"
            }
          },
          "RootResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/",
              "Method": "ANY"
            }
          }
        }
      }
    }
  },
  "Outputs": {
    "ApiURL": {
      "Description": "API endpoint URL for Prod environment",
      "Value": {
        "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
      }
    }
  }
}
  1. Added aws-lambda-tools-defaults.json
{
  "Information": [
    "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
    "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
    "dotnet lambda help",
    "All the command line options for the Lambda command can be specified in this file."
  ],
  "profile": "default",
  "region": "",
  "configuration": "Release",
  "s3-prefix": "CustomRuntimeAspNetCore30/",
  "template": "serverless.template",
  "template-parameters": "",
  "msbuild-parameters": "--self-contained true /p:AssemblyName=bootstrap",
  "framework": "netcoreapp3.0",
  "s3-bucket": "",
  "stack-name": "CustomRuntimeAspNetCore30"
}
  1. Turned on Visual Studio deployment wizard.

Edit the project file to include the AWSProjectType with a value of Lambda in the PropertyGroup collection.

<PropertyGroup>
  <TargetFramework>netcoreapp3.0</TargetFramework>
  <AWSProjectType>Lambda</AWSProjectType>
</PropertyGroup>

You can then deploy the ASP.NET Core project to Lambda by right-clicking the project in Visual Studio and selecting 'Publish to AWS Lambda...'

More info here:

https://aws.amazon.com/blogs/developer/net-core-3-0-on-lambda-with-aws-lambdas-custom-runtime/

Note that this process may error if the webapi project you are publishing has references to other assemblies. This is because in the deployment (step 5), it attempts to rename all assemblies to 'bootstrap'. then you will need to:

Rename your project's assembly name in the csproj file to 'bootstrap' (without quotes). Modify aws-lambda-tools-defaults.json so that the line:

"msbuild-parameters": "--self-contained true /p:AssemblyName=bootstrap",

becomes

"msbuild-parameters"  : "--self-contained true",

Note also that logging seems to have changed from .Net 2.1.

I tried to implement logging as per .Net Core 2.1. That is:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
       Host.CreateDefaultBuilder(args)
         .ConfigureLogging(logging =>
         {
            logging.AddAWSProvider();
            logging.SetMinimumLevel(LogLevel.Debug);
         })
         .ConfigureWebHostDefaults(webBuilder =>
         {
            webBuilder.UseStartup<startup>();
         });

(and similar code in LambdaEntryPoint.cs)

However, this doesn't work. Looking at the code, it is trying to find the IConfiguration ImplementationInstance and this is null.

According to this https://github.com/aspnet/AspNetCore/issues/14400, this breaking change is by design.

To get logging working in .Net Core 3, you need to add code to Startup.Configure():

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
        {
            var loggingConfigSection = Configuration.GetAWSLoggingConfigSection();
            var awsLoggerProvider = new AWSLoggerProvider(loggingConfigSection);
            loggerFactory.AddProvider(awsLoggerProvider);

            // rest of code
        }
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the very detailed post. I was actually referring to the Serverless Framework, therefore the use of serverless.yml when deploying lambdas.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.