Setting up a PowerShell Universal development environment

PowerShellUniversal Development

August 27, 2020

Note: Consider installing the PowerShell Universal VS Code extension as it takes care of a lot of the items described in this post without any configuration required.

PowerShell Universal is the ultimate platform for building web-based solutions with PowerShell. In this post, we’ll look at how to setup a development environment for Universal. We’ll be configuring Visual Studio Code to make it easier to develop Universal scripts.

Installation

The first step in getting your environment ready is installing Universal. We offer a ton of different options for getting started on your platform.

The script below will download the 1.3.1 ZIP and extract it to the ProgramData directory on Windows.

$Version = '1.3.1'
$Url = "https://imsreleases.blob.core.windows.net/universal/production/$Version/Universal.win-x64.$Version.zip"
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri $Url -outfile "$Env:Temp\universal.zip" -verbose 
Expand-Archive "$Env:Temp\universal.zip" -DestinationPath "$Env:ProgramData\Universal"

Now that the files have been downloaded and extracted, you can start the Universal server. It will be listening on port 5000 by default.

Start-Process "$Env:ProgramData\Universal\Universal.Server.exe"

You can view the administrative console by visit http://localhost:5000 in your web browser.

Start-Process "http://localhost:5000"

You’ll be able to login to the console with the user name admin and any password.

Scripting

You can use the Universal PowerShell module to script the basic setup of Universal rather than using the admin console. You can either install the module from the PowerShell Gallery or load it directly from the Universal installation folder.

Install-Module Universal -Scope CurrentUser -Force

Or

Import-Module "$Env:ProgramData\Universal\Cmdlets\Universal.psd1"

Once the module is installed, you’ll need to get an app token generated to interact with the server. The app token signing key is set in the appsettings.json file within the Universal installation folder. You should change this in your production environment but we will use the default values to create an app token manually.

You can navigate to Settings / Security / AppTokens and click Grant followed by Copy to retrieve a new app token.

Once you have the app token, you can use the Connect-PSUServer command to connect to your server.

Connect-UAServer -ComputerName http://localhost:5000 -AppToken 'eyJhbGc...'

Once you are connected, you can start to issue commands against Universal. For example, you can get all the settings from the server using Get-UASettings.

PS C:\Users\adamr> Get-UASetting

Id                        : 1
LoggingFilePath           :
LogLevel                  : Error
DefaultPowerShellVersion  :
RepositoryPath            : C:\ProgramData\UniversalAutomation\Repository
DatabasePath              : C:\ProgramData\UniversalAutomation\database.db
GroomDays                 : 30
ConcurrentJobLimit        : 1000
Telemetry                 : True
HideApi                   : False
HideAutomation            : False
HideDashboard             : False
DisableAutoReload         : False
SecurityPowerShellVersion :

Visual Studio Code

Now that we have a server running and the basics of scripting setup, we can start to setup our VS Code environment.

By default, the PowerShell scripts for Universal are stored in $Env:ProgramData\UniversalAutomation\Repository. If you open VS Code in that directory, you will see the various configuration files for Universal.

code $Env:ProgramData\UniversalAutomation\Repository

The files in this directory dictate the configuration of Universal. Changes made to the file will be automatically reloaded by Universal.

For example, you wanted to add a dashboard, you could add a new call to New-PSUDashboard in the dashboard.ps1 file.

New-PSUDashboard -Name "Dashboard" -FilePath "Dashboard.ps1" -BaseUrl "/dashboard" -Framework "UniversalDashboard:3.0.1"

IntelliSense

One important feature of VS Code is the ability to use IntelliSense. To enable IntelliSense for Universal and Universal Dashboard commands, you’ll need to import the modules. You can do so by running the following commands.

function Import-UniversalModules {
    param($AppToken, $Dashboard)
    Import-Module "$Env:ProgramData\Universal\Cmdlets\Universal.psd1" -Scope Global

    Connect-UAServer -ComputerName http://localhost:5000 -AppToken $AppToken

    $PSUDashboard = Get-PSUDashboard | Where-Object Name -eq $Dashboard
    Import-Module "$($PSUDashboard.DashboardFramework.Path)\*.psd1" -Scope Global
}

You can now import the modules used by the dashboard by using the Import-UniversalModules function.

Registering an Editor Command

The PowerShell VS Code extension provides a Register-EditorCommand function to allow modules to provide editor commands that run in PowerShell that have been invoked from VS Code. We can register a command to import the modules for us.

Register-EditorCommand -Name 'Import Universal Modules' -DisplayName 'Import Universal Modules' -Function {
    Import-UniversalModules -AppToken $Env:AppToken -Dashboard Dashboard
}

Once that has been registered, open the command palette with Ctrl+Shift+P and search for the PowerShell: Show additional commands from PowerShell Modules. Once you select this, you will see your command

Controlling the state of Dashboard in VS Code

We can also control the state of a dashboard in VS Code. Sometimes it may be necessary to stop and start the dashboard.

function Restart-Dashboard {
    param($Dashboard)

    Get-PSUDashboard | Where-Object Name -eq $Dashboard | Stop-UDDashboard
    Get-PSUDashboard | Where-Object Name -eq $Dashboard | Start-UDDashboard
}

Similar to the IntelliSense command, we can then register it with VS Code.

Register-EditorCommand -Name 'Restart Dashboard' -DisplayName 'Restart Dashboard' -Function {
    Restart-Dashboard -Dashboard Dashboard
}

Setting up a VS Code profile

Like any PowerShell host, VS Code provides a profile. You can edit or create your profile by running this in the VS Code Integrated Terminal.

code $Profile

This profile script is run each time at startup. One thing that we can do to ensure that our functions and commands are registered is to create a workspace profile. VS Code doesn’t support this directly but we can use this workaround.

In my profile script, I have this code.

$WorkspaceProfile = Resolve-Path .\profile.ps1
if (Test-Path $WorkspaceProfile)
{
    . $WorkspaceProfile
}

In the Universal repository folder, I’ve created a profile.ps1 file that that creates the functions and registers the commands.

function Import-UniversalModules {
    param($AppToken, $Dashboard)
    Import-Module "$Env:ProgramData\Universal\Cmdlets\Universal.psd1" -Scope Global

    Connect-UAServer -ComputerName http://localhost:5000 -AppToken $AppToken

    $PSUDashboard = Get-PSUDashboard | Where-Object Name -eq $Dashboard
    Import-Module "$($PSUDashboard.DashboardFramework.Path)\*.psd1" -Scope Global
}

Register-EditorCommand -Name 'Import Universal Modules' -DisplayName 'Import Universal Modules' -Function {
    Import-UniversalModules -AppToken $Env:AppToken -Dashboard Dashboard
}

function Restart-Dashboard {
    param($Dashboard)

    Get-PSUDashboard | Where-Object Name -eq $Dashboard | Stop-UDDashboard
    Get-PSUDashboard | Where-Object Name -eq $Dashboard | Start-UDDashboard
}

Register-EditorCommand -Name 'Restart Dashboard' -DisplayName 'Restart Dashboard' -Function {
    Restart-Dashboard -Dashboard Dashboard
}

With this in place, every time I open the repository folder, it will add the two commands to the VS Code command palette. This will allow me to easily load the PowerShell modules for IntelliSense and restart dashboards.

Conclusion

After playing with this a bit, I think these functions will make it into a future version of Universal to make it easier for working with Universal in VS Code. There are a host of other operations we could also add to make it even easier to work with other features of Universal directly from Code.