Diving into Powershell Azure Functions

Azure Functions are Microsoft’s “serverless” attempt to answer Amazon Lamda Function. I’ll avoid the fluff and simply say it’s a quick-and-dirty way to get some code on a publicly facing website. An ideal use case, for example, would be to process webhooks (i.e. consuming callback events).

Azure Functions support an array of languages, but we’ll focus primarily on their new support for Powershell (and some comparison to their C# .csx support).

At the time of this writing, powershell support was still labeled experimental and documentation was incomplete. This post attempts to serve as the hidden manual in the interim.

Function App

Function apps are a Azure Resource which will host your Azure Functions. Essentially, each function is an endpoint.

https://<FUNC_WEB_APP>.azurewebsites.net/api/<FUNC_NAME>?code=<API_KEY>

This blog post covers the general setup of Function Apps pretty well.

The Server in Serverless

Your Function App is essentially a Windows Azure Website WebApp under the hood with some glue to help the various languages gain access to some host data/function parameters.

  • Query Parameters (e.g. <URL>/?code=<CODE>&foo=bar&thing=1) – Are encoded as magic variables $REQ_QUERY_<parameter> such as $REQ_QUERY_FOO
  • AppSettings – Once you store AppSettings in your Function App, all functions (endpoints) will have access to them via environment variables  $ENV:APPSETTING_<setting> such as  $ENV:APPSETTING_APIKEY
  • Request (Body) is written to a TEMP file (which in Azure is SSD disk as opposed to network drives). The path is given to you as the variable $req which contains the JSON payload.
  • Response will be taken from the temp file located at $res (in JSON format) after your script exists.
The way requests and responses are serialized will probably change given a conversation I had with a PM of the team that works on Azure Functions. I believe they do have plans to do all the serialization of requests and responses in memory — similar to how the C# .csx scripts work.

You can explore this yourself by creating a new function that dumps out the various variables.

And simply call your function URL with a simple Invoke-RestMethod.

Request Authorization

By default, functions are secured with API keys (code) — which can be set on the Function App or more commonly the Function level.

You can generate new function keys using the keys link on the right. Selecting a key will change the Function URL to include the full ink with the code as a parameter. The Function App will handle authorization for you, but you will also have access to the key via $REQ_QUERY_CODE.

When I tested out Azure Functions, using non-Default function keys did not work as intended. Hopefully this was fixed. In any case, I would test your function with the default key and then with newly generated keys.

Powershell Modules

The long and short of it this: I would avoid modules short of the few included (e.g. Azure Storage Module). This SO answer covers a few salient points about the underlying infrastructure:

  1. Modules are loaded at runtime
  2. Modules need to be uploaded using KUDU and have to be in a flat structure

Writing to the Log

At the time, the only way to write to the console was using Write-Output.  The usual suspects like Write-Verbose and Write-Host do not have direct support as of yet.

I filed a Github Issue to at least override the common functions to avoid confusion.

Accessing The Backend with KUDU

You have limited access to the IIS path (HOME) using the KUDU Console.

You can access this under Function App Settings > Deploy : Go To Kudu

Performance and Internal Mechanics

I had assumed that the powershell performance would suffer compared to the C# Rosyln script hosts. However, due to a few reasons, the difference is negligible when I compared similar scripts.

  • The internal Azure WebJobs keep the powershell runspace hot as opposed to spawning new powershell.exe per request. [PowershellFunctionInvoker.cs]
  • I had assumed C# .csx tasks would suffer from not having the bytecode IL written ahead of time. It turns out that the team actually has their own implementation which does essentially compile the bytecode like a normal C# app. [CSharpCompiler.cs]

Using the on-demand tier and accounting for potentially hitting servers that weren’t warm, response times on both C# and Powershell tasks ranged anywhere from 350ms to 1500ms for similar request, parsing, and response scripts.

When To Use Azure Functions

Azure Functions come with a low cost of entry, low cost of maintenance, and all the gotchas that come with things that are easy. So, generally, I’d say use them with you have low commitment.  I would say the rule of thumb would be to keep it simple.

  • Automation Endpoints – When you need to kick off a Azure Worker (or Hybrid Worker) job via a RESTful endpoint. Azure Functions come with hooks into Azure Storage and Azure API out of the box to make it easy — though they do recommend using Azure Storage/Tables for intercommunication rather than inter-connected HTTP calls.
  • Cloud Webhook Glue – Let’s say Service A and Service B don’t have direct ways to consume each other’s webhooks or APIs. I’d argue this is a scenario that is perfect for Azure Functions. For example, posting Octopus Deploy webhook events to chat channels.

 

Leave a Reply

Your email address will not be published. Required fields are marked *