Loading…

Hosting Polaris in a Windows Service using PowerShell Pro Tools

Polaris is a cross-platform, minimalist web framework for PowerShell. Much like, Universal Dashboard, it takes advantage of ASP.NET Core and the Kestrel to expose a web server from PowerShell. Using the new 4.0.18 version of PowerShell Pro Tools, you can now create Windows Services from PowerShell scripts.

In this post we will look at the steps necessary to host Polaris inside a custom Windows Service.

Creating your first Polaris server

From the Polaris GitHub page you can find the basic hello, world web server.

New-PolarisGetRoute -Path "/helloworld" -Scriptblock {
    $Response.Send('Hello World!');
    $response.SetStatusCode(200)
    return
}

Start-Polaris -Port 1000

Once your Polaris service has started, you can invoke it using Invoke-WebRequest.

C:\Users\Adam> invoke-webrequest http://localhost:1000/helloworld -UseBasicParsing

StatusCode        : 200
StatusDescription : OK
Content           : Hello World!

Packaging Polaris as a Web Service

Using the PowerShell Pro Tools Module and\or VS Code extension, you can create a Windows Service around any PowerShell script.

First, you’ll need to put your Polaris code into the format Merge-Script expects for a Service. Define an OnStart and OnStop function. These functions will be called when the service is started and stopped.

function OnStart()  {
    New-PolarisGetRoute -Path "/helloworld" -Scriptblock {
        $Response.Send('Hello World!');
        $response.SetStatusCode(200)
        return
    }

    Start-Polaris -Port 1000
}

function OnStop() {
    Get-Polaris | Stop-Polaris
}

If you have the PowerShell Pro Tools VS Code extension installed, you can execute the PowerShell: Package as Executable command with the service script open. This will automatically create a package.psd1 file with various settings for your exectuable.

Change the PackageType parameter to Service and run the PowerShell: Package as Executable command again.

@{
    Root = 'c:\Users\Adam\Desktop\service.ps1'
    OutputPath = 'c:\Users\Adam\Desktop\out'
    Package = @{
        Enabled = $true
        Obfuscate = $false
        HideConsoleWindow = $false
        DotNetVersion = 'v4.6.2'
        FileVersion = '1.0.0'
        FileDescription = ''
        ProductName = ''
        ProductVersion = ''
        Copyright = ''
        RequireElevation = $false
        ApplicationIconPath = ''
        PackageType = 'Service'
        ServiceName = "Polaris"
        ServiceDisplayName = "Polaris"
    }
    Bundle = @{
        Enabled = $true
        Modules = $true
        # IgnoredModules = @()
    }
}

Your service will be output to the directory defined in the package.psd1 as service.exe.

You’ll need Polaris installed in the AllUsers scope to allow the service to find it when running under the machine account.

Installing and Running the Service

The service executable supports the --install and --uninstall command line arguments. If you run them from an elevated command prompt, you can install and uninstall your service. Once the service is installed, you can start it and begin invoking web requests against it.

~\Desktop\out> .\service.exe --install

Running a transacted installation.

Beginning the Install phase of the installation.
See the contents of the log file for the C:\users\adam\Desktop\out\service.exe assembly's progress.
The file is located at C:\users\adam\Desktop\out\service.InstallLog.
Installing assembly 'C:\users\adam\Desktop\out\service.exe'.
Affected parameters are:
   logtoconsole =
   assemblypath = C:\users\adam\Desktop\out\service.exe
   logfile = C:\users\adam\Desktop\out\service.InstallLog
Installing service Polaris...
Service Polaris has been successfully installed.
Creating EventLog source Polaris in log Application...

The Install phase completed successfully, and the Commit phase is beginning.
See the contents of the log file for the C:\users\adam\Desktop\out\service.exe assembly's progress.
The file is located at C:\users\adam\Desktop\out\service.InstallLog.
Committing assembly 'C:\users\adam\Desktop\out\service.exe'.
Affected parameters are:
   logtoconsole =
   assemblypath = C:\users\adam\Desktop\out\service.exe
   logfile = C:\users\adam\Desktop\out\service.InstallLog

The Commit phase completed successfully.

The transacted install has completed.
~\Desktop\out> Start-Service Polaris
~\Desktop\out> Invoke-WebRequest http://localhost:1000/helloworld


StatusCode        : 200
StatusDescription : OK
Content           : Hello World!

Debugging a Service

If you encounter any errors with your service, they will be logged to the Application Log under your service name.

~\Desktop\out> Get-EventLog -LogName Application -Source Polaris -Newest 10

   Index Time          EntryType   Source                 InstanceID Message
   ----- ----          ---------   ------                 ---------- -------
   44209 Nov 04 13:15  Information Polaris                         0 Service started successfully.
   44205 Nov 04 13:12  Information Polaris                         0 Service stopped successfully.
   44204 Nov 04 13:12  Error       Polaris                         0 The input object cannot be bound to any paramet...
   44188 Nov 04 13:09  Information Polaris                         0 Service started successfully.
   44187 Nov 04 13:09  Error       Polaris                         0 The term 'Start-Polaris' is not recognized as t...
   44186 Nov 04 13:09  Error       Polaris                         0 The term 'New-PolarisGetRoute' is not recognize...
   44185 Nov 04 13:09  Error       Polaris                         0 The specified module 'Polaris' was not loaded b...
   44184 Nov 04 13:09  Error       Polaris                         0 The specified module 'Polaris.Class' was not lo...
   44183 Nov 04 13:09  Error       Polaris                         0 The specified module 'PolarisMiddleware.Class' ...
   44182 Nov 04 13:09  Error       Polaris                         0 The specified module 'PolarisResponse.Class' wa...

Leave a Reply