Building real-time web apps with PowerShell Universal Dashboard

Today, I’ve released a beta version of PowerShell Universal Dashboard that enables the ability to create real-time web apps using PowerShell.

Install-Module UniversalDashboard -AllowPrerelease

In this post, I will go through the details of how Universal Dashboard worked before this release and how it works now. I have a full example on GitHub of how to use this functionality.

How Existing Universal Dashboards Work

Universal Dashboard is built using React and ASP.NET Core. The React JavaScript single page application (SPA) is downloaded to the client machine when the page is first loaded. The dashboard definition, generated in PowerShell and serialized to JSON, is then downloaded by the SPA to create the elements you see on the screen. Once an element is rendered, it is responsible for loading its own data. Each element calls back over HTTP to the ASP.NET Core service to execute a PowerShell Endpoint to retrieve the data that it intends to render.

The data flow looks something like this.

Some elements don’t require data from the server so they won’t make requests. If AutoRefresh is enabled, elements will refresh their data on an interval. They will make the same HTTP request back to the server to get the updated data. This executes the PowerShell Endpoint again and updates what the user sees on the web page.

The communication is one way. The client is always requesting data from the server and the server is providing a response to update the client. The one outlier here is the AutoReload functionality. This functionality uses a Web Socket to allow the server to send a notification to all connected clients that they need to reload the page because the dashboard has been updated.

Commands like New-UDInput suffer from the one-way communication architecture. When a user clicks an input button, the button calls back to the server and returns an action the client needs to execute. This means that the commands you are executing within PowerShell aren’t happening in real time. They are actually being batched together and returned as JSON to be executed by the client when it gets the response. There is no communication between the server and the client during the execution of the script.

Introducing SignalR

SignalR is library for ASP.NET Core that enables super simple, real-time communication between the server and client. It makes setting up two-way communication very easy by taking care of much of the transport and protocol configuration and management and allowing you to create simple proxies on the client and server to send messages back and forth.

It also manages connection IDs, can group connections together and provide broadcast capabilities that allow a server to communicate with all connected parties very easily.
Universal Dashboard is now leveraging SignalR to allow for extremely customizable, real-time web apps via PowerShell script.

A Real-Time Web App with Universal Dashboard

With the introduction of New-UDElement in the last major version, Universal Dashboard took a step away from pre-packaged components and allowed users to create new elements based on either JavaScript or PowerShell. You can create arbitrary React elements that will render to HTML DOM elements. This allowed you to pass properties down to the client as well as attributes and nest multiple elements to create an HTML structure for your application.

Examples of custom components built using New-UDElement are available on GitHub.

In 1.5.0, the UDElement cmdlets have been expanded to allow you to update those elements on the fly from within your PowerShell Endpoints running in the server. These new cmdlets include:

  • Set-UDElement – Update properties of existing elements
  • Get-UDElement – Get the properties of an existing element within your endpoint
  • Add-UDElement – Add a child element to an existing element
  • Clear-UDElement – Remove all children from an element

Chatroom: A real-time web app built with Universal Dashboard

Using the new cmdlets, we can now create apps that update the state of the client web page while the script is running. To demonstrate this, we will walk through building a web-app for a very simple chatroom.

The first step is to create the structure of our app. We will want a collection of messages and a message box to enter new messages.

In this example, I’ll be using a Materialize CSS Collection to show messages in the chatroom. Using New-UDElement, we can create a new UL tag with the CSS class collection to define a new collection.

Next, we need to create a text box for entering new messages. Again, we can use New-UDElement to create a new input tag with some attributes that will provide what we are looking for.

Finally, we will need a way to submit out messages. With the 1.5.0 release of Universal Dashboard, you can now add ScriptBlock handlers for JavaScript events to your attributes collection. In Materialize, buttons can be defined using the a tag and applying the btn class. You can see below we have assigned a script block to the onClick event. This script block will be executed whenever the button is clicked.

The next step is to define what we want to happen when the onClick event is triggered. We obviously want to send a message to the chat room. To do this, we can append a new element to the chatroom collection. To create the message we want to send, we need to get the value of the message text box where the user has typed their message.

The following code defines a new li element, a list item for the collection with the CSS class collection-item assigned, and gets the content from the message text box. It also formats the user name and date into the message so they appear along side the message text.

Now we can add the message to the chatroom control. We specify the Broadcast switch so that all users connected to the chatroom see the message. If you do not include Broadcast, only the user that clicked this button will see the message.

Finally, let’s clear the message text box so it’s ready for the next message.

Here’s the chatroom in action.

To demonstrate the difference between a broadcast message and a client-specific message, let’s introduce the ability to clear the chatroom room for a user.
Using New-UDElement, we can define another button that, when clicked, will clear the content of the chatroom element. Clear-UDElement is used without the Broadcast switch and will only apply to the user that clicked the button.

Now, if we login as two different users, we can see that clicking the Clear Messages button will only clear the chatroom for one of the users.

The full source for this chatroom is available on GitHub.

Behind the Magic

The secret sauce to this type of functionality is using SignalR to communicate over a web socket between the client and server at any time. It avoids the one-way communication problem by maintaining a two-way websocket channel as soon as the client loads the dashboard.

This means the server can call the client at any time. During the execution of the PowerShell endpoint, the PowerShell script runs and updates the client as it progresses through the script.

SignalR makes it very easy to define a message hub for receiving and sending messages on the server. To send a message to a client, it’s as easy as calling InvokeAsync on the hub. This is an example of a broadcast message sent to all clients.

To receive a message on the server, you define a new method on the hub and it’s automatically called when the client sends a message to the server.

On the client, it’s equally as easy to send messages back and forth. To send a message to the server, you just call invoke on the SignalR HubConnection object in JavaScript.

To receive messages, you define event handlers for each message type. These messages are then published to the different components throughout the client dashboard React SPA.

Other Use Cases

There are numerous other use cases for this functionality. Since you can update the client interface in any Endpoint script, it’s possible to create a REST API method, that when invoked sends a message to the connected clients’ dashboards to update controls when new data is available.
This also opens the doors to creating all kinds of more interactive interfaces. You should be able to create applications similar in functionality to basic Windows Forms and XAML solutions.

What’s next?

This is a beta and there will be bugs. I hope to squash those quickly and roll out new betas when necessary. Additionally, I will be creating a set of open-source New-UDElement wrappers to make it easier to work with this technology. Using New-UDElement correctly requires some knowledge of JavaScript and CSS and I hope to shield users from this by creating controls with simple parameters that are far more discoverable.

I’ve started work on this and some very, very basic examples of what I am building are available on GitHub. These components will continue to be open source and will be included with Universal Dashboard in the final 1.5.0 release.


Existing elements like UDChart and UDMonitor are not yet integrated into the UDElement cmdlets and do not benefit from this update. Look forward to that changing in future releases.

Leave a Reply