How to Write Multiple AWS Lambda Handlers in a .NET Core Project

Kyle Galbraith - Jan 18 '20 - - Dev Community

In a lot of my side projects, I make use of AWS Lambda for a large variety of things. But the most common thing I use it for is to automate a lot of the mundane and repetitive tasks I often have to do. Don't get me wrong, serverless is great for a lot of things. But it's fantastic for turning those one-off tasks, that are never really a one-off, into easy to run bits of code.

My longest-running side project is Ultimate Fantasy Supercross. I won't go into a big long intro about the project because you can read about that here. But suffice it to say that I make use of .NET Core and AWS Lambda quite a bit in this project.

A lot of the one-off tasks and cron jobs associated with UFSX, I have turned into Lambda functions. It has been handy for turning those one-off tasks into repeatable pieces of code that I can run again and again.

Until recently, a lot of those tasks have been one to one. Meaning one task could be one .csproj file and thus one AWS Lambda function.

But the more of these projects I started to create, the more it felt a bit odd. Did each Lambda function need its own project file? I found myself having separate projects for things that had very similar functionality. Combining these into one project made a lot of sense.

How the problem emerged

Because UFSX was built up using .NET, I have used the AWS Toolkit for Visual Studio quite a bit. For those not familiar, these tools provide handy shortcuts right in Visual Studio. Below is one that shows how you can publish directly to AWS Lambda.

Publish to AWS Lambda via AWS Toolkit

This shortcut has made developing and deploying my one-off Lambda functions a breeze. But, the shortcut, out of the box is a bit limiting. The shortcut only allows you to deploy to one AWS Lambda function per project.

This is how I ended up with the model I was in, one Lambda function per project. A tool I was using pushed me in a direction that may or may not be the right fit. Not to worry the AWS folks already thought of this and I just never did the research.

Multiple AWS Lambda functions per .NET Core project

It turns out having multiple AWS Lambda functions in one .csproj is doable with a bit of AWS CloudFormation.

To define multiple serverless functions in one project we first add a serverless.template to our project. You can do this by using the AWS Toolkit as well. Right-click the project with your AWS Lambda function. Then navigate down to the "Add" section and select AWS Serverless Template.

Add AWS Serverless Template to project

The initial serverless.template has quite a few things in it.



{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Description": "Starting template for an AWS Serverless Application.",
  "Parameters": {},
  "Resources": {
    "DefaultFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Handler": "<ASSEMBLY-NAME>::<TYPE-FULL-NAME>::<METHOD-NAME>",
        "Runtime": "dotnetcore2.1",
        "CodeUri": "",
        "Description": "Default function",
        "MemorySize": 256,
        "Timeout": 30,
        "Role": null,
        "Policies": ["AWSLambdaFullAccess"],
        "Events": {
          "ProxyResource": {
            "Type": "Api",
            "Properties": {
              "Path": "/{proxy+}",
              "Method": "ANY"
            }
          }
        }
      }
    }
  },
  "Outputs": {
    "ApiURL": {
      "Description": "API endpoint URL for Prod environment",
      "Value": { "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" }
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

We're not going to go through everything you can configure in here. But this is a CloudFormation template so the options are almost limitless. For the purposes of this blog post, let's look at how you can use this template to define multiple AWS Lambda functions in a single .NET Core project.

Under Resources we see the DefaultFunction. That is the config for that Lambda function. If we want to define two Lambda functions in one project file, we need to define another block in the same way.



{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Description": "Starting template for an AWS Serverless Application.",
  "Parameters": {},
  "Resources": {
    "FunctionOne": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "FunctionName": "FunctionOne",
        "Handler": "AWS.Example.Project::AWS.Example.Project.Function::FunctionOneHandler",
        "Runtime": "dotnetcore2.1",
        "CodeUri": "",
        "Description": "",
        "MemorySize": 1024,
        "Timeout": 180,
        "Role": "<arn-for-your-role>",
        "Policies": ["AWSLambdaFullAccess"]
      }
    },
    "FunctionTwo": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "FunctionName": "FunctionTwo",
        "Handler": "AWS.Example.Project::AWS.Example.Project.Function::FunctionTwoHandler",
        "Runtime": "dotnetcore2.1",
        "CodeUri": "",
        "Description": "",
        "MemorySize": 1024,
        "Timeout": 180,
        "Role": "<arn-for-your-role>",
        "Policies": ["AWSLambdaFullAccess"]
      }
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

Above we now have two functions defined in our serverless.template, FunctionOne and FunctionTwo. An important thing to note here is the path in the Handler field. This is the full path to the function handler in your project. It follows the form <ASSEMBLY-NAME>::<TYPE-FULL-NAME>::<METHOD-NAME>.

So how do you deploy this?

Now that we have a template that can deploy multiple Lambda functions from a single project, how do we actually deploy it?

Well if the GUI is your tool of choice, the AWS Toolkit can handle this for you. When we select "Publish to AWS Lambda...", we get presented with a different workflow when our project contains a serverless.template file.

Deploy AWS Serverless Application

Instead of receiving a dialog for uploading a Lambda function we now get a dialog for publishing an AWS Serverless Application. This is because we have a template file defining multiple resources.

For those that prefer working on the command line there is an option there as well.

As long as you have the AWS Toolkit installed for Visual Studio, anything you can do there you can also do from the command line. To deploy our project containing multiple Lambda functions, we need to run this from the folder containing the project.



$ dotnet lambda deploy-serverless


Enter fullscreen mode Exit fullscreen mode

That's all there is to it, the call will build your project, upload the source to Lambda, and update the CloudFormation stack.

Conclusion

This is a handy solution that has come in handy as I am adding more and more serverless pieces to Ultimate Fantasy Supercross. Because I now have a workable solution using this approach I have started to put similar Lambda functions in the same project file.

When it comes to doing this, be careful.

The more functions you start adding into a single project, the more dependencies per function you could be introducing. Additionally, your function deployment packages could become bigger and thus cause performance problems.

For simple cron job types of serverless workloads, this works well. But it might not be the right fit for other types of workloads.

Want to check out my other projects?

I am a huge fan of the DEV community. If you have any questions or want to chat about different ideas relating to refactoring, reach out on Twitter or drop a comment below.

Outside of blogging, I created a Learn AWS By Using It course. In the course, we focus on learning Amazon Web Services by actually using it to host, secure, and deliver static websites. It's a simple problem, with many solutions, but it's perfect for ramping up your understanding of AWS. I recently added two new bonus chapters to the course that focus on Infrastructure as Code and Continuous Deployment.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .