So, you’ve got a load of PowerShell code that does useful stuff, and because you’re doing Modern Windows Ops now, you want to port that code into some kind of web-based API, so you can scale it out and make your epic PowerShell code accessible from more devices and environments.
You don’t want to rewrite it all in C# as an ASP.NET Web Api – for an ops engineer that seems like terrible overkill. Besides, who’s got time for that nonsense? Modern Ops teams are busy as hell, even though they’ve got automation everywhere. You could get devs to do it, but then you have to manage requirements and specs and Jira tickets oh god not Jira tickets. Please anything other than Jira tickets NOOOOOOOOO!
Ahem *cough* excuse me. Where was I?
If only there was a way you could take your existing PowerShell code and turn it into an HTTP API quickly and easily.
Well, it turns out there are lots of options now. Check below the fold for three I’ve used recently.
Option 1: PoSHServer
I found PoSHServer a few years back when I was scouring round for an easy way to hook up AWS SNS notifications to Octopus Deploy.
PoSHServer is literally a small HTTP daemon written in PowerShell. It’s trivially easy to set up, and you simply drop a powershell script into the http folder to get started. I took my existing run-on-a-schedule cleanup script, added some code to parse the incoming SNS JSON post, took the InstanceID from that post and used it to remove terminated deployment targets in Octopus. It took a matter of minutes to set up, and it was pretty much painless.
So I invested a little more time, added a few more endpoints for Slack and Github webhook integration, got the whole thing into an Octopus Continuous Delivery chain and before long I was adding new PowerShell API functionality with a simple git merge.
Well, first, PoSHServer doesn’t deal well with load. It’s great for small hobby projects, but when I wired it up to our Sleepytime process (which terminates several hundred instances in a matter of a minute or two, each causing its own SNS message), it started to fray at the edges.
I also had to chuck in a couple of pull requests to let it respond with non-200 status codes, and debugging troublesome scripts wasn’t particularly easy, especially out in the cloud. It does IP restrictions, so you can lock it down a little, and it does basic auth options. You can also write full-blown web pages, and it can run the PHP engine, which is an added bonus if you like that sort of thing.
There’s also not a lot of existing example code you can just go grab to hash together a quick app. Implementing an OAuth flow, for instance, took a day or two because I had to write it from scratch, rather than just pulling a nuget package and carrying on.
Lastly: there’s no native SSL/TLS, though I solved that by using an AWS Elastic Load balancer to terminate my connections
Option 2: PowerShell.REST.API
The guys at Dimension Data’s Sydney CBU offer a rather nice alternative. Written in C# using OWIN, PowerShell.REST.API is much more robust than PoSHServer.
It’s more strict, in that it’s specifically designed to create RESTful APIs rather than web pages, and to add a new API endpoint you need to edit the XML config file and restart the service, rather than just dropping it into a folder as with PoSHServer.
But the far superior robustness was a big selling point, and led to it taking over from PoSHServer for many of our webhook and bot functions. This engine now underlies several Slack bot functions, a lot of SNS-type hook responses, and a bunch of custom AWS API stuff. It also accompanies and monitors Deckard, our WinOps constant testing suite, which runs a continuous loop of Pester tests against our infrastructure, hunting for rogue robots twenty-four hours a day.
Another very nice little feature is that you can hit an endpoint and have it trigger a long-running job, quickly returning a jobID to the client and letting the process run in the background. Being able to return a snappy response is important, especially when you consider that PowerShell lacks the raw speed of a “real” .NET API.
As with PoSHServer I put in a little time and got this into a Continuous Delivery pipeline, this time one which installs everything needed from a bare Windows Server Core box right up to the last PowerShell script. So I have robust, self-recovering PowerSHell-based autoscaling APIs
Downsides: To be honest, not many if you’re willing to put in some yards to automate the install and configure portions. It has authentication, but currently lacks a way to extract useful stuff from request headers or push stuff into response headers. A little harder to get started than PoSHServer, but not brain-explodingly tough by any means.
As for PoSHServer, SSL was handled by AWS Load Balancers – it’s an important consideration, and I don’t think SSL capabilities are there by default at the moment, though I’ll stand corrected if they are, of course.
For both of these, you could potentially terminate your SSL at an IIS website and use ARR to reverse-proxy the API – or maybe run Nginx for Windows – as a cheaper option than using a load balancer. As always, YMMV.
Option 3: Azure Functions
Now here’s the real king of the hill. Near-infinitely scalable endpoints written in PowerShell, hosted on a serverless architecture in one of the world’s leading Cloud providers. They run on native Windows infrastructure, and you’re billed per invocation at a vanishingly tiny dollar rate, with a load of free invocations included. You can even upload your own modules as part of your package, so you have access to all the functionality you could need. In theory, you could even cram the AWS Tools for PowerShell in there, and manage AWS from Azure, if you felt so inclined. And believe me, I feel like doing it just for giggles.
There’s even a skills challenge you can go and try out, if you want to get your feet wet now.
What’s not to love?
Well, they’re a fair bit more involved to deploy and manage, and you need to put in a lot more thought during the initial phase to make sure your code is correctly composed or decomposed, that it doesn’t rely on local state and that it can execute within the published limits of Azure Functions.
Each endpoint is a separate function, so you can’t just deploy a monolith as you can with PoSHServer and PowerShell.REST.API – which might be more work, and can be a bit of a departure in thinking, if you’re not already familiar with serverless frameworks.
Or it might not be.
If you get your deployment story right early on, you could be deploying from a merge or a single click in no time. I’m hoping to open source some Azure function deployment bits soon, so keep an eye on github.
Oh, and this time, SSL is there out of the box, if you’re happy using an Azure-provided DNS name. Setting up a custom name is also possible, obviously, but for simple APIs that are consumed by machines, is there really a need?
So, which option should you choose?
As ever, depends what you want to achieve. For my needs, PowerShell.REST.API won out over PoSHServer because of the robustness under load. Azure functions weren’t initially an option due to the rest of our environment being in AWS and nobody wanting to fragment things, but the world moves on and now Azure is an option again.
If I had my time over, Azure Functions would win straight away, though there’s no denying the usefulness of the other two options. Try them out.