I've recently been working with Azure Functions, Microsoft's functions-as-a-service (FAAS) platform. I'm already quite familiar with AWS Lambda so my perspective is coloured by that experience.

First, the good. Functions Apps are pretty flexible. You can host a single entry point per app if you want but it is often more cost-effective to host several functions within the same app and share some resources. I'm using Javascript so the whole app shares a package.json and a node_modules directory etc. The app also has its own Storage Account, which can host blobs (like S3), queues (like SQS) and tables (similar to DynamoDB but works differently) etc.. These resources are easy to use from any function within the app, making it easy to send a message down a queue to model event-driven workflows and perform multiple actions with the same files or database records.

The runtime of a function app environment is versioned separately from the language and language version. There is a list of supported languages and versions for each version of the runtime.

A Function App is connected to an App Service Plan, which defines the virtual machine billing model. There are options for consumption plans on Windows and Linux or you can reserve an auto-scaling pool of virtual machines. It's fairly flexible but quite awkward to configure for Linux. The key when using Terraform or ARM is to make sure the instance is reserved, as that seems to mean "use Linux".

The inputs and outputs of functions can be bound to HTTP requests/responses or tied directly to other Azure event sources, such as blobs, queues, tables etc. This allows you to avoid writing the code to send a message, write a row to a table or any number of other common integrations. This gives Functions a lot more flexibility than AWS Lambda functions, which only lets you bind the input event source.

The documentation for Linux apps is largely missing and often useless. You have to dig pretty deep to discover that the setting for configuring the version of NodeJS you want only works on Windows instances. There is a hidden setting called LinuxFxVersion in a different place (Site Config) that has no UI in the Azure Portal for configuring it on Linux. Fortunately Terraform has a way to address this undocumented property. It appears in ARM as well but I haven't found a way to edit it from the Azure Portal.

I've seen various weird crashes when deploying and restarting function apps. There are at least 3 different undocumented things that can go wrong, leaving only a cryptic error message. Searching for these issues usually fails to yield any relevant results. I've seen more strange errors with version 3 of the functions app runtime than with version 2.

Another frustration is the Azure CLI. The best case when deploying a function app is the message Operation failed with status: 200 Operation completed successfully. As amusing as that is, more often the tool simply fails with a HTTP 400 error: Bad Request with no explanation. Since the CLI hits the API about 8 times during deployment, there is no way to know how far you got or what state it left your app in. This completely breaks any kind of deployment automation. In production, you'll have to pay somebody to hand-hold the build and make sure changes make it out to your customers.