loading...

AutoRest.PowerShell

omiossec profile image Olivier Miossec Updated on ・7 min read

Few years ago, API, RestFull and webservices was a buzz word for Ops, something they don’t have to care. But today, you can’t live without them. If you use a cloud service, a firewall or any hardware interface you need to consume a Rest Full API. API are everywhere!

Using one method or path in a webservice is not a big deal. There is a cmdlet inPowerShell, invoke-RestMethod let you to deal with that with no or little effort. But it’s only for one method, one path, not for the whole API. If you need to deal with a whole API, it’s another story. You will need to build a client, deal with parameters for all methods and create a custom PowerShell object for each object for each response.

It means, for every method in the API, a function, a return object, an acceptance and an unit test must written and a communication tool need to be created. It’s a time consuming and error prone task. More, it’s only to use the API, you will also need to add your own logic, your own code to build your API based module.

Installing AutoRest.PowerShell

AutoRest is the tools to automate this task, it creates a function for every method, with a custom object for each returned object. This application is built in Node.js and it let you build a client library from a RestApi specification file, a swagger or OpenApi File. AutoRest is used to generate clients from Azure API. Il help to manage updates and new features. And now this tool is available for PowerShell users.

AutoRest.PowerShell enable PowerShell users to create a PowerShell module to deal with an API by using a specification file as input.

As the tools work with Node.js, the first step is to install Node.js 10.15. You will also need to get PowerShell Core and .net core SDK 2.2. On a Windows platform I use the Chocolatey package manager to deploy and update tools.

choco install powershell-core
choco install node-lts
choco install dotnetcore

Now we can install AutoRest, but to get PowerShell support we need to install the beta version.

npm install -g autorest@beta

What if AutoRest is already installed? In this case you can reset AutoRest with this command.

autorest -reset

Using AutoRest

Once installed, we can start to build our first PowerShell API client. We need an API, but more we need a document describing this API. For RestFull API, this document is a Swagger or OpenApi file. It’s a simple YAML or JSON document describing the service. It includes paths or resources, operations or verbs, parameters and responses representations.

This is the only file you need to create the client from AutoRest. It doesn’t try to connect to the API or anything else. It just read the document.

To start creating a PowerShell module from a swagger file the command is simple (be sure to use PowerShell Core):

autorest --powershell --input-file:PathToJson-Yaml-File

AutoRest

This will generate a module in a folder named “Generated”, if you need to change the folder name or the folder path you can use the --OutPutFoler: parameter to use the folder you want.

If you navigate into the generated folder you will not find the classic script module.

Generated Folder

There is a test folder containing Pester files. There is a custom folder that can be used to add other PowerShell function to the module. You will find several folders and files. If you open the .psm1 file, you will find something like.

# Load the private module dll 

$null = Import-Module -Name (Join-Path $PSScriptRoot './bin/xxx.private.dll') 

# Get the private module's instance 

$instance = [xxxx.Module]::Instance 

The generated module is a binary module with a .Net framework assembly in a DLL that contain the cmdlet. You can find the source code in the generated folder.

If you are looking for the bin folder inside the generated folder, you will not find the it. The module needs to be compiled. For this task, you don’t need to have Visual Studio or to type complexes dotnet commands. There is a build script for this task, build-module.ps1

You can run the script without any parameter, but you can also use a switch to perform some tasks:

  • Run. This will generate the module, create a PowerShell Runspace and load the module so you can use it in isolated console

  • Test, it will run the pester scripts in the test folder and build the module only if all the test pass

  • Doc, this will create the documentation by running new-markdownhelp cmdlet in generate-help.ps1 script. If this task run without error it will also build the module

  • Code, this will generate the module and open VsCode

  • Pack, it will generate and pack the module

  • Release, it will build the module and compile the code in release mode.

To run the script, open a PowerShell Core console and type:

.\generated\build-module.ps1 -run  

The compilation may fail, it depends on the swagger file or the AutoRest version. In this case you may have to open the source code inside the generated\api\Models folder to start a debug session.

If the build success you will enter in an isolated console where you can test your module.

get-command -module yourModuleName

This command will list all the public cmdlet you can use from the generated module.

But the module is just in debug mode and it’s not ready for production. More, the module only contains a function per path in the API, how can add our own code and logic? Last thing, how can we deal with API authentication?

To add our custom logic, we need to use the custom folder. It contains a module file, ModuleName.custom.psm1, that will be loaded during the build process to create cmdlets in the export folder. It’s possible to use traditional script files and/or C# cmdlets. But only script cmdlet let you use function and cmdlet from generated from the swagger specifications.

But in both cases, we will need to know the module namespace. The default name is "Sample.Api". But we can change it with the namespace parameter.

autorest --powershell --namespance:MyNameSpace 

Creating a script file is not enough, in order to work with the module, you will have to add some parameters.

[ValidateNotNull()] 

    [frpsug.Runtime.SendAsyncStep[]] 

    ${HttpPipelineAppend}, 

    [Parameter(DontShow)] 

    [ValidateNotNull()] 

    [frpsug.Runtime.SendAsyncStep[]] 

    ${HttpPipelinePrepend}, 

    [Parameter(DontShow)] 

    [System.Uri] 

    ${Proxy}, 

    [Parameter(DontShow)] 

    [ValidateNotNull()] 

    [System.Management.Automation.PSCredential] 

    ${ProxyCredential}, 

    [Parameter(DontShow)] 

    [System.Management.Automation.SwitchParameter] 

    ${ProxyUseDefaultCredentials} 

Of course, PowerShell standard guideline apply here, you should name your script with the name of the function, you should apply the verb-noun pattern and you should write, at least, one Pester test per function in the test folder. And be sure to mock any function generated by AutoRest. If not, you will not be able to perform any test before building the module.

Script cmdlets are processed during the build. Adding a file after the build in the custom folder produce nothing. You need to build the module each time you add a custom cmdlet.

In order to test the module, you can use invoke-pester directly inside the test folder. You can also use the build script to perform your tests.

.\generated\build-module.ps1 -test

It will run the tests and stop here if Pester return an error while running your tests it will stop the build process.

You can also add a c# cmdlet, but it’s discouraged by AutoRest.PowerShell Team. The cmdlet will be compiled during the build process and put inside the DLL. They must use the same namespace as the module.

You can also use the custom folder to change the module behavior. Many API use some kind of security, a token in the http header or an APIKEY. Currently, AutoRest.PowerShell cannot deal with the situation and the module may not work.

But we can leverage Partial Class to change how the way the module connects to the API. A Partial class is simply the same definition of a class split into different files. These files are combined during the compilation.

In our case we need to extend the class named Module in our code to add an Apikey for each http request. But how can we pass the apikey to the module. It’s not possible to add it as parameter for every path. But we can get it from other sources. We can use an environment variable instead.

Here’s the code to surcharge HttpResponseMessage and add the ApiKey nammed api_key from an environment variable.

namespace MyNameSpace
{
    using Runtime;
    using System.Collections.Generic;
    using System.Net.Http;
    using System.Threading;
    using System.Threading.Tasks;
    /// <summary>A class that contains the module-common code and data.</summary>
    /// <notes>
    /// This is a modified version from https://github.com/Azure/autorest/blob/master/docs/powershell/samples/timeswire/generated/custom/Module.cs
    /// </notes>
    public partial class Module
    {
        partial void CustomInit()
        {

            this._pipeline.Append(AddApiKey);

            this._pipelineWithProxy.Append(AddApiKey);
        }
        protected async Task<HttpResponseMessage> AddApiKey(HttpRequestMessage request, IEventListener callback, ISendAsync next)
        {
            // check if the query already contain a ?
            var sepChar = string.IsNullOrEmpty(request.RequestUri.Query) ? "?" : "&";

            // add on the api_key
            request.RequestUri = new System.Uri(
                request.RequestUri.AbsoluteUri +
                sepChar +
                "api_key=" +
                System.Environment.GetEnvironmentVariable("nasaapikey")
                );

            return await next.SendAsync(request, callback);
        }
    }
}

The partial class will be compiled with the module and add the api_key parameter for each http query made by the module.

You will need to build the module again by using the build-module.ps1 script

After adding our custom cmdlet and the authentication module, after performing unit tests and generate the documentation we need to pack the module.

First, we need to create a release.

.\generated\build-module.ps1 -release 

After that we need to modify the .nuspec file at the root of the generated folder. Open the file and add change version, authors, description, projectUrl and licenseUrl field.

We can now use pack-module.ps1 script to create a nuget package for our module.

All these tasks can be automated using a pipeline. With Azure DevOps pipeline you will have to make sure to use PowerShell core and not the Windows version.

You need two tasks, one to generate the module, and other one to build and test it.

But remember, AutoRest.PowerShell is still in beta. You may find some bug and some limitations.

But you can always post an issue on https://github.com/Azure/autorest.powershell/issues

Discussion

pic
Editor guide