Skip to main content

Developing PowerShell modules for REST APIs – Part2

AzureFunctionThis is part 2 of the REST API blogpost. In part1 we successfully setup two REST API endpoints using the UniversalDashboard PowerShell module. In this part we are going to create a simple module that support some CRUD operation against our API. As we are trying to keep things as simple as possible, we will not use any fancy framework (like Plaster) to build our module. We are also going to skip a very important step you should familiarize yourself with, Pester tests. Lets get to it.


The module

We will build a module called FilesAPI. The module folder will look like this:

image

In the functions folder I have already added the 2 helper functions from part 1, Get-AuthorizationHeader and ConvertTo-Base64. The other folders are just placeholders for important stuff like classes, private functions that you do not want to make available for the module consumer and tests for Pester tests. For such a small module that we are going to create, one could argue that it is much easier to just add the functions directly in the module definition file (psm1), however I strongly urge you to create a folder structure for your modules. It is a huge benefit to have each function, test and class in a separate file. If you use the folder structure, Visual Studio shines compared to ISE as your module grows. It will also make the transition to module frameworks like Plaster much easier.

Since we are using a single file for each function, this is what we will put in out module file:

$ModuleScript variable contains the script that will load each function file, class and private function in our folder structure. As we add functions to the folders, it will only export the functions in the functions folder. It is a generic template that I started to use quite recently. The inspiration came from Kieran Jacobsen and his Plaster template. Go ahead and execute the script to create the FilesApi.psm1 module file.


First API function – Get-FileApi

We are going to start with the first simple function and we will name it Get-FileApi. As you might recall from the first part, the API needs an Authorization header for all calls to our API, so we need that as an parameter for the function. It might also be a good idea to add a parameter for Name so we can filter on specific files. Below is my function:

Basically we are just wrapping Invoke-RestMethod supplying the URI, method and the header containing the Authorization key/hashtable. We target the Authorization parameter as mandatory and leave the Name parameter optional. Save this function in the functions directory and name the file Get-FileApi.ps1.


Second API function – Set-FileApi

Secondly we are now going to create a function for the POST method of the API. The endpoint accepts 3 parameters:
  • Authorization
  • FileName
  • Content
Our function needs to implement these parameters. Since this is a function uses the verb Set, we should be nice to the consumers of our module and implement WhatIf, more commonly know as SupportsShouldProcess. I really hate it when a destructive command do not implement the WhatIf parameter, here is looking at you Azure for PowerShell. Again we are marking the Authorization parameter as Mandatory for our function:

Pretty cool, with a few extra lines of code, our functions supports the WhatIf parameter and we have created 2 functions that talk to an API. Copy the function and save it in the functions folder as Set-FileApi.ps1.


Turning it up a notch

Now at this point, we have two working function, however this is not really how APIs work. UniversalDashboards helps us quite a bit since the objects returned from Invoke-RestMethod is converted to real PowerShell objects. Invoke-RestMethod usually returns a HtmlWebResponseObject which contains two important properties. Content and StatusCode. To make it a little more interesting, we will try and get as close as possible to a real API. We are going to update our endpoints to return an object containing 2 properties; Content and StatusCode. Content will contain a JSON string of the object previously returned from the API. StatusCode is an HTTP code indicating the status of our request, which we will set to 200 if authorization is okay. We are also going to add a Name header for our Endpoint to simulate server side filtering. After the modifications, the Endpoint definition looks like this:

Go ahead and register the new endpoint.

Running our Get-FileApi function now is resulting in this output:

image

We can see the statuscode and the Content property containing a JSON string which is what we are after. So we have to check the StatusCode to verify that the request was OK (200) and then we have to convert the content string to an object using ConvertFrom-Json. After the modifications we have the following:


Running the Get-FileApi function gives us a nice object and filtering by Name works to, even with wildcards:

image

Applying the same logic to our CreateFileEndpoint, we update the definition to this:

After registering the new endpoint definition and running the unmodified Set-FileApi function, we get the new respons from our API:

image

Updating the Set-FileApi function to convert the JSON string to an object, it should look something like this:

Trying the Set-FileApi function after updating, we again get a nice object ouput:

image


Final module work

If you have followed along until now, your functions directory should look like this:

image

Your root folder should just have the folders and a single file FilesApi.psm1 (we created that in the beginning):

image

Now we are going to create the FilesApi.psd1 or what is know as the manifest. Here is the script I will be using:

A couple of notes here. If you plan to run this script several times, like if you add more functions, best practice is to “keep” the GUID for the module. If you run this script, the manifest will have a new GUID each time. Also if you are checking the manifest into source control, it is always nice that the manifest is encoded in UTF8. The New-ModuleManifest encodes it in ASCII format, which you probably do not want. In the last two lines I am converting the module to UTF8. After you have created the manifest, you may go ahead and start a new PowerShell session and import the module:

image

Lets take it for a spin, first we need to create an authorization variable:


image

After getting an authorization object, we go ahead and run Get-FileApi with the parameter:

image

Well, I’ll be darned, it works. Who would have thought.


What is missing?

A couple of things really. 

Help
As a best practice you should add help to your functions.

Tests
Test Driven Development, TDD for short, is something you might want to look into. In essence you write your unit tests before you create the function. As I have mentioned previously, you can use Pester for this. It it built-in and included with PowerShell as a module.

Authorization
Different APIs have a couple of ways of adding authentication to the requests. I have only shown one method. 

URL query parameters

I chose to not include it in this guide. If you come a cross such an API like I have, I strongly suggest you add an function that can build the url for you. If the interest it there, I can show the Join-Url function I wrote which will build an URL for you with childpaths and URL query parameters.

BaseURL problem for production/test
In our example, we have hard coded the URL in the functions. It is quite common to have a test/dev environment for the API and a production environment. This requires that your module needs to support 2 different base URL. You can solve this quite easily by adding a module-level variable in the psm1 file ([bool]$script:IsProduction = $false). Then you add 2 new functions to the module, Get-ApiBaseURL and Set-ApiEnvionment. Get-ApiBaseURL contains just a if-block and will return the correct URL for the environment you want to work with. The Set-ApiEnvironment will flip the module level boolean variable to match your environment. You might also want to add a Get-ApiEnvironment function that will return the value of the $Script:IsProduction variable. 

Key takeaway

The goal of creating this guide is twofold: Firstly I wanted you to become aware of the awesome module UniversalDashboard and how easy it is to create an REST API using only PowerShell. Secondly I wanted to show it is quite trivial to create a module that consumes an REST API. Hope you have enjoyed reading or doing the tutorial as much as I did.

You can find part 1 of this tutorial here.

Cheers

Tore

Comments

  1. REST APIs are the way of the future! Great seeing this all done in PowerShell! Thanks!

    ReplyDelete
    Replies
    1. Thanks, always nice to hear :-). Take care!

      Delete

Post a Comment

Popular posts from this blog

Developing PowerShell modules for REST APIs – Part1

Over the years I have developed different PowerShell modules for different web APIs. I thought it would be a good idea to write a 2 series post about how you could go about to do this. This will be a 2 part blog series where we will run through the entire process of building a module for a REST API. I will try my best to keep this as simple as possible and leave more advanced stuff for a follow up post if the interest is there.What you needDepending on your experience with source control and PowerShell in general, you might want to use GIT or some other software repro for the code. In addition we are going to create a test REST API using the splendid UniversalDashboard PowerShell module created by Adam Driscoll. It is available on the PowershellGallery. Other prerequisites are built-in to Powershell. I will assume that you will be following along using at least PowerShell version 5 or greater.
What is HTTP metods for REST API.The primary or most common HTTP verbs used are POST, GET, PU…

Serialize data with PowerShell

Currently I am working on a big new module. In this module, I need to persist data to disk and reprocess them at some point even if the module/PowerShell session was closed. I needed to serialize objects and save them to disk. It needed to be very efficient to be able to support a high volume of objects. Hence I decided to turn this serializer into a module called HashData.



Other Serializing methods

In PowerShell we have several possibilities to serialize objects. There are two cmdlets you can use which are built in:
Export-CliXmlConvertTo-JSON
Both are excellent options if you do not care about the size of the file. In my case I needed something lean and mean in terms of the size on disk for the serialized object. Lets do some tests to compare the different types:


(Hashdata.Object.ps1)

You might be curious why I do not use the Export-CliXML cmdlet and just use the [System.Management.Automation.PSSerializer]::Serialize static method. The static method will generate the same xml, however we …