TL;DR – The source code is here and converting Azure Functions to AWS Lambdas is really fun and easy!
Some background
For the unfamiliar, serverless computing is a way to host code that runs on a cloud provider and can scale resources automatically to handle load. Yes, technically there is a server, but to the developer writing the code he or she need not be concerned about that. This idea of being able to offload the worry and maintenance of servers and focus on developing the exact things a customer wants unleashes all sorts of possibilities. A developer can set up a continuous integration and continuous delivery (CI/CD) pipeline, commit code, and have it deployed in seconds.
Serverless functions are individual units of work. Where developers have, in the past, written what are called “monoliths”, or giant APIs full of endpoints and business logic and database connections (oh my!), functions tend to be much smaller units of work that encapsulate business logic needed for a task. Rather than having an endpoint per database model and HTTP verb, a developer could have a function for each. For instance, instead of a GET, POST, PUT, and DELETE for a widget model, four functions could be written that all handle their own database connection and logic to retrieve, create, update, or delete a widget.
Some simple, one-off examples are a function that takes a black and white image and “colorizes” it using an artificial intelligence library. You could generate a QR code out of some text you pass in. A function could take in a string of text and generate an MP3 using Google text-to-speech API. The sky is truly the limit.
How do we get started?
There are a few major vendors in the “public cloud infrastructure” realm, including but not limited to Microsoft’s Azure, Amazon’s AWS, ZEIT Now, and Google Cloud Platform. Each of these vendors offer what is called “Functions as a Service”, or FaaS, which is a platform developers can use to create, run, and manage these individual units of work.
There are also do-it-yourself ways to host functions such as OpenFaaS, but we’re going to stick with Azure and AWS for this post. Side note: I’m a big fan of OpenFaaS if you’re looking for a “get your hands dirty” sort of project. Alex and the team that run it are great and super helpful!
Whether it’s a client swapping from one hosting provider to another, price and use, or something else there are times where a change is necessary. I’d like to show how easy it is to convert an Azure Function to an AWS Lambda. The example I provide below is a simple one, but it demonstrates what is required to transform existing Azure Function code into something that can easily and reliably run in AWS Lambda.
Let’s get started!
Signatures and packages
Check out the below code:
[FunctionName("FunctionDemo")]
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
int value = string.IsNullOrWhiteSpace(req.Query["value"]) ?
DateTime.Now.Year :
int.Parse(req.Query["value"]);
var output = new { value = value, factors = GetFactors(value) };
return (ActionResult)new OkObjectResult(output);
}
private static int[] GetFactors(int value)
{
return Enumerable.Range(1, value).Where(i => value % i == 0).ToArray();
}
This is the basic output from generating a new function in Visual Studio Code, which a minor modification to the body to calculate the factors of the value passed via the query string (or using the current year if a value isn’t passed in).
Another thing to check out is the csproj file included when generating a function, as per below:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AzureFunctionsVersion>v2</AzureFunctionsVersion>
<RootNamespace>FunctionDemo</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.29" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
In order for a function to work in both Azure and AWS, it must target .NET Core 2.1. Azure actually lets you target something higher but AWS does not (yet).
To convert this to an AWS Lambda, there are very few changes required. We’re going to remove the `Microsoft.NET.Sdk.Functions` NuGet package and add 2 AWS specific ones, add a couple of elements to the csproj, and then change the function signature. Check those out below:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="1.1.0" />
<PackageReference Include="Amazon.Lambda.Serialization.Json" Version="1.7.0" />
</ItemGroup>
<ItemGroup>
<None Update="02272020_meeting.runtimeconfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
[LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
public static object FunctionHandler(ILambdaContext context)
{
int value = DateTime.Now.Year;
var output = new { value = value, factors = GetFactors(value) };
return output;
}
For the AWS version, I’m not getting a query string parameter; rather, I’m just using the current year. I’ll leave that up to you to do.
You can run a Clean and Build in Visual Studio to generate the appropriate bin folder and binaries. (Spoiler alert: in a future post I’ll show you how easy it easy to automate using GitHub Actions!) Zip the netcoreapp2.1 located in either Debug or Release and then upload that to your AWS function (click Upload and select your .zip file):
One of the trickiest parts of uploading a C# zip file is specifying the Handler (to the bottom right in that image). Click the “Info” link for more information on how to specify your the signature of your function handler.
What did you think? Think that was easy enough? I have a very similar pipeline setup for a personal project using GitHub Actions. Would you like to learn about that next?