PowerShell GUI with HTML - Part 3

The Series

If you have been following along with the previous two parts to this blog, I know what you have been saying. When will we get to the GUI part! Everything up to this point has been all web servers and web technology. Which could come in handy but we just want to make a simple GUI in HTML that runs PowerShell. The browser thing is cool but you don’t want to pop open a browser every time you want to run some scripts. Integrating that into the pipeline would be difficult too right?

Today, we finally get to the good stuff!

Step 1 - Build our own web browser (and just not tell people it is a browser)

So, what if we take the code just below which launches a super simple XAML GUI with just a webbrowser object in it, in another runspace immediately before we run our code from the last post.

Start-Sleep -Seconds 15 [void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') [xml]$XAML = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="PowerShell HTML GUI" WindowStartupLocation="CenterScreen"> <WebBrowser Name="WebBrowser"></WebBrowser> </Window> '@ #Read XAML $reader=(New-Object System.Xml.XmlNodeReader $xaml) $Form=[Windows.Markup.XamlReader]::Load( $reader ) #=========================================================================== # Store Form Objects In PowerShell #=========================================================================== $WebBrowser = $Form.FindName("WebBrowser") $WebBrowser.Navigate("http://localhost:8000/") $Form.ShowDialog()

Voila! We have our own PowerShell web browser displaying our simple form but doesn’t look anything like a web browser! It looks like a regular old windows desktop application.

Now with this we can do some pretty amazing things but we still have a few problems to solve.

How do we know when to shut down the web server?

If you were just going to get a single request from the browser you can close it as soon as you receive and process the request from the browser. If not you can simply build in a URL that will stop the server for example:

while($SimpleServer.IsListening) { $Context = $SimpleServer.GetContext() # Creating a friendly way to shutdown the server if($Context.Request.Url.LocalPath -eq "/kill") { $Context.Response.Close() $SimpleServer.Stop() break } ... }

How do I eliminate the Start-Sleep and launch my GUI as soon as the server is ready?

You may come up with your own better method for doing this but I came up with this little function. Which will continually attempt to access the URL until it gets a response and then it will continue to load the xaml:

function Wait-ServerLaunch { try { $url="http://localhost:8000/" $Test = New-Object System.Net.WebClient $Test.DownloadString($url); } catch { start-sleep -Seconds 1; Wait-ServerLaunch } }

How do I hide the PowerShell window so end users won’t know it is there?

You would obviously want your PowerShell console showing when you are debugging but if you are using your script client-side you may not want them to know that it is there. DexterPosh had a great post on this awhile back. Check it out here.

How do I include modern web technologies in this GUI? It seem to work in IE8 by default.

To make your GUI look pretty using magic like materializecss or Office UI Fabric you need the following meta tag in the header of your HTML:

<meta http-equiv= "X-UA-Compatible" content= "IE=edge" />

This will force it to use IE Edge. For more information on that magic line see here.

How do I build a complete UI that collects input and returns / displays results using the UI?

Here is my code for this solution. A usage example would be like this:

Start-PoshWebGUI -ScriptBlock { $Parameters = $Context.Request.QueryString switch ($Context.Request.Url.LocalPath) { "/showProcesses" { "<a href='/'>Main Menu</a><form action='/filterProcesses'>Filter:<input Name='Name'></input></form>$(Get-Process | select cpu,name | ConvertTo-Html -Fragment | Out-String)" } "/filterProcesses" { "<a href='/'>Main Menu</a><form action='/filterProcesses'>Filter:<input Name='Name'></input></form>$(Get-Process $Parameters["Name"] | select cpu, name | ConvertTo-Html -Fragment | Out-String)" } "/showServices" { "<a href='/'>Main Menu</a><form action='/filterServices'>Filter:<input Name='Name'></input></form>$(Get-Service | select Status,Name,DisplayName | ConvertTo-Html -Fragment | Out-String)" } "/filterServices" { "<a href='/'>Main Menu</a><form action='/filterServices'>Filter:<input Name='Name'></input></form>$(Get-Service $Parameters["Name"] | select Status,Name,DisplayName | ConvertTo-Html -Fragment | Out-String)" } default { @" <h1>My Simple Task Manager</h1> <a href="/showProcesses"><h2>Show Running Processes</h2></a> <a href="/showServices"><h2>Show Running Services</h2></a> "@ } } }

A not too bad chunk of code and we get this:

Awesome Right!

Full code for Start-PoshWebGUI

*NOTE: I do plan on putting this into a module fairly soon hopefully with help and examples. If you want to help out hit me up or fork the repo on GitHub.