How to Write Simple, Scalable HTML Powershell Graphic User Interfaces for managing Rapid Recovery (1 of 3)

Part two of this series may be found here.

Part three of this series may be found here.

Over the years I have written hundreds of PowerShell scripts for RapidRecovery/AppAssure Management and Automation. One of the constant requests I have heard from customers was to embed some kind of GUI in the scripts that were NOT supposed to be run on a regular basis using the same parameter values over and over again (in these cases a simple .bat file is enough).  I have done my best to deliver.

The easiest approach was to do some kind of menu placed in a loop – something reminiscing of the old DOS days – the kind of text menu where you choose “1” to perform an action, “2” to perform another action and “Q” to quit. Since this does not really work for even minimally complex situations, I moved to Windows Forms – which proved to be too much work to get them right -- and later to XAML (WPF) – which allow better looks but even more unjustifiable work. In both cases Visual Studio helps creating the interface but, making it running with PowerShell requires to modify the code and the result is hard to manage, especially for more complex scripts.

As such, I tried to embark in a quest for productivity by settings the specs for a GUI easy to create, modular and scalable. I daresay that I believe I am on the right path. I know it as I have prepared a GUI for a script using this method a few months ago and, recently, after forgetting all the "gotchas" and fine points, I was able to create a new (albeit simple) one in less than an hour (including the code that does the underlying job) using the modular functions I already created. Practically, I copied and pasted modules, have given the appropriate values and before I knew it (and without fully understanding what I was doing) I had a working PowerShell application featuring a nice GUI. As a bonus, I was able to wrap everything in an .exe with the help of the free excellent PowerGui tool (still available for download in many web locations despite being discontinued).

In this blog series, I will walk you through this process in a, hopefully, organized manner.

Let's begin with the background. The customer I mentioned above was not able to read the Rapid Recovery Windows Log in the events viewer (creatively named DELL.evtx) as another Dell related application was creating a folder called DELL at the same location. Due to the name conflict, only the DELL folders was shown in the Windows Events MMC.

The script we are going to write together allows getting the log files known to the Windows eventslog service (including the DELL.evtx), displaying and filtering the events and exporting the result to a csv file for future processing.

It is a very simple application but at the same time a perfect match to illustrate the point at hand.

Oh, and since I kept you waiting, we will build the GUI using HTML with a whiff of JavaScript (I dare you to notice it) and displaying it in Internet Explorer. All the GUI elements can be encapsulated in functions and the script can be divided in sections. At last but not at least, we will take advantage of the DOM structure. In the end, the GUI is built in a declarative manner.

It is hard to believe how fast the development goes if following this path – well, in the case of the SECOND attempt and subsequent ones as the first try requires some uphill charging until all the minutia is under control.

Now, I am going to get into the substance of this article. As I go along, I will discuss the various blocks needed to build the application. To better understand how the elements I present below do work and tie together, readers may want to use the Powershell Integrated Scripting Environment (ISE) or any other similar tool to re-create and run the code .  

I begin with a "tough" statement:

"If I run a script with a GUI, I want to be able to launch it by double-clicking it, not from an elevated PowerShell console. "

It goes without saying that the traditional way of launching a PowerShell Script that does something useful is to open an elevated PowerShell console first, navigating to the script and launching it. Double-clicking the icon of the script file will launch the script in a non-administrative mode and thus the most powerful features of the script will be blocked by lack of permissions.

This is an issue that needs to be examined. To solve it the following steps are needed:

* Determine if the script is launch with administrative permissions:

* If it is not (which is most likely), get the name of the script:

* When done, re-launch the script with admin permissions:

 Everything can be neatly packed (with the usual flourishing) in a set-once and forget function:

To launch this function, copy and paste it in a file (called for consistency RRLOGSBLOGEXEAMPLE.ps1 -- if you work this along with me) and launch it either by double-clicking or by right-click -> run with PowerShell after adding the lines below.

You will see the initial PowerShell Console coming up, then launching the second Powershell Console which has admin permissions. The initial PowerShell Console which is non-Admin will close and the new admin PowerShell Console will still stay open a few more seconds (as dictated through the “start-sleep 15” commandlet).

Time to go to the next step! Let’s put something of interest in the new Admin Powershell Console! Let’s create an Internet Explorer Object and put it in a simple function as well.

Just for the record, Internet Explorer can be invoked through a com object. In PowerShell, this is quite simple:
Once we got the com object, we can set up a few properties and we are ready to go. The function I am using is shown below. It takes two parameters ($height and $title) and returns the Internet Explorer object ($ie). No error handling is added (on purpose as we take advantage of this "oversight" next).
 
To show Internet Explorer as shaped above, the following lines need to be added to the code, after the “Use-RunAsAdmin” line:
 
Running the script will return a nice empty Internet Explorer Window. Encouraged by the result, you move your nascent script from your local workstation to your RR core where you want to continue the development.
 
You are in for a nasty surprise. The Internet Explorer window does not show up (red error text does) and after troubleshooting, you find out that the $ie.document.title is the issue. (Don’t just remove it to eliminate the issue; there are some more important properties that are missing as well).
 
You run the script on yet another core and surprise, this time it works. You notice that since you have done an Exchange Restore on that Core, Outlook is installed. Fortunately, the issue is already solved and I am going to explain below.
 
It looks that Microsoft does not ship Windows with (lack of a better word) the full version of the Internet Explorer com object. However, installing any Office component will do it. Fortunately, there is no need to install Office to get the Internet Explorer object to behave. There is only one .dll file that needs to be copied on the core server from a machin on which MS.Office is installed and, to make things right, we can prepare a function to do it with minimal user effort.
 
Basically, the function mentioned above checks the presence of the dll in the GAC and if it does not find it, it directs the user where to get it on a machine (with Office installed), to copy it to a location of choice on the core server and from there it places it automatically in the required location. To smooth things out the messages used by this function are created via Windows Forms as we are coding a GUI.
 
Moreover, to remain faithful to the modularization mentioned before, we will write another function to create a Windows “Open File Dialog”.
 
Since the Open File Dialog function is very simple, there is not much to explain (the comments in the code should be enough). Basically, we create the object and assign properties. If the dialog is cancelled or the entered filename does not point to a real file, the function exits.
 
 
 Now everything is ready to write the function that unlocks Internet Explorer.
 
First step is to load System.Windows.Forms namespace assembly for the benefit of various message boxes and the Open File Dialog we need later.
 
Please note the Out-Null redirection to avoid unnecessary messages.
 
We continue by defining the path in the GAC to copy the DLL and checking the existence of the DLL we need.
 
 If it is missing a Dialog asking if we want to proceed pops up and the “Yes”/”No” options are explored:
 
If the answer is “No” the script shows a Pop-Up Message and exits.
 
If the answer is "Yes", instructions are shown and the necessary GAC directory is created and the .dll file is retrieved via the open file dialog and copied in the appropriate location.
 
 
To put everything together, the get-GAC function looks like this:
To run the code we have put together until now, we need to transfer it on a server without an Office installation, adjust it as below and run it.
 
 
You will be walked through all the necessary steps to have the Internet Explorer object ($ie) running properly on that machine.
 
At this point all the prerequisites are taken care of and we are ready to move toward the substance of this series.
 
However, this is going be done in the next installment…
About the Author
Tudor.Popescu
Tudor Popescu, Technical product Specialist, is a member of the RapidRecovery/AppAssure team in Reston, VA. Tudor has focused on improving efficiency and simplifying the administrative tasks of our largest...