Building Better PowerShell Dashboards

First off, YUUGE props to Flynn Bundy for shining lights on the possibility with his post Making DSC Beautiful and @Neeco of for these gorgeous HTML5 and CSS templates.

If you check out, there are a ton of absolutely beautiful templates, for free! (Well, you have to leave a link to the site, unless you pay $20, then you can edit it to your heart’s content).


Some of them REALLY lend themselves well to a dashboard system for consumption of data.

…you know, PowerShell makes an excellent data collection and processing system.

It even has  native HTML capabilities, as we’ve covered previously in our post: Using ConvertTo-HTML and CSS to create useful web reports from PowerShell.  If you’re lost and don’t even know where to start, begin here.  I’ll bet we could make some REALLY cool looking dashboards using PowerShell and Neeco’s templates!

Let’s make a cool PowerShell Dashboard

So, I’ll start by finding a template that I like.  I choose the gorgeous Phantom, which is also the top one from the list.  Now, you might be asking yourself “FoxDeploy, did you even look at all of the templates first?” to which I would respond: SURE.

Let’s take a look at Phantom.   It’s got a nice set of fonts and a good layout, with a big title glob of text, then a smaller description below it.  It’s followed by a big element or DIV called Tiles, with colored squares inside of it, called articles.

Breaking down the Phantom Template

Let’s take a look into the code and see how this is represented.


A few things jump out at me here.  Looking back at the image of the template itself, I see the first three squares/cards/cubes are red, blue and green.  Going back to the code, I don’t see the colors listed there but I DO see a style, a different one for each.   It looks like the color of the tile is controlled by the style property in it’s declaration, like this:

<article class="style1">								<span class="image">										<img src="images/pic01.jpg" alt="" />									</span>					
<a href="generic.html">
										<div class="content">
											<p>Sed nisl arcu euismod sit amet nisi lorem etiam dolor veroeros et feugiat.</p>

If you see a property like class= or id= within a HTML element, that’s a good clue that the Cascading Style Sheet (cascading meaning you can have a base one for the site, then special sub-sheets for specific pages, and overlap them all in a precise, cascading order) CSS will do some special processing on it when it’s displayed to the user.

What’s CSS?

 If CSS is totally new to you, it’s a great concept that allows us to pull the design and colors out of our HTML webpages.  Instead of specifying what font to use for this section of the page, and what color to make the background, we pull all of that style gunk out and leave behind just the meat and potatoes (the content, that is) of our site in HTML.  All style goes into the Cascading Style Sheet–the CSS file.

As we saw in the screen shot, each of the squares had a different color, and looking at the code, the only real difference between each of the squares in code was that a different style was listed. So, we’ll look into the CSS files and see what it says for coloring.

For this and all web design work, I like to use Visual Studio Code, by the incredible David Wilson [MSFT].  Especially for CSS, it makes finding color assignments super easy, since it depicts the color in a little box next to it, you know, in case you don’t say things like “Wife, your eyes are the most beautiful shade of #7eccfb”

The colors are down near line 2700.  (Hit Control+G to bring up the ‘Go to line’ box, and type in the number.)


So we can see that style1 is red, style2 is blue, style3 is green, etc.  Now I know what I want to do…

Time to Code

I’m going to make a dashboard to show the status of my Hyper-V VMs.

As I tend to do, first I’ll begin with a working PowerShell sample.  I’ll run Get-VM to see all of my VMs.  If the status of the VM is running, I’ll use the Green (style3) indicator.  If it’s Stopped, I’ll use the Red (style1), and if it’s something else, I’ll use style2.  This would include Critical or some other weird state.

$VMS = get-vm | sort State 

ForEach ($VM in $VMS){

    if ($vm.State -eq 'Off'){
        $style = 'style1'
        elseif($vm.state -eq 'Running'){
        $style = &amp;quot;style3&amp;quot;
        #VM is haunted or something else
        $style = &amp;quot;style2&amp;quot;

    #Now we know what state to pick

I know what I need to set for each square, but don’t know how to add my squares to the actual index.html of this page.

And now, to do something unholy to the HTML

I use an unorthodox approach that totally works well.  Once we understand how the HTML in index.html is rendering the page, what we’ll do here will make perfect sense.

Starting at the top of the document, let’s visualize what each chunk of code represents when parsed by a browser…


So, that’s the top part.  After that, beginning with the &lt;section class="tiles"&gt; tag, we have a big repeating structure which gives us all of the squares/tiles.


Finally, beginning with the closing </section> tag, we have the bottom of the page, with it’s contact forms and all of that.


To do this the easy way, let’s just cut it into three files!

I’ll take the core file here (which is index.html) and I’ll break it into two chunks.  Everything from the top of the file including the line <section class=”tiles”> goes into head.html.  Now, start at the bottom of the file and take the last line all the way up to and including the line </section> and save that as tail.html.

Now we need to make our cards

Structure of a card/tile/square

Let’s look into the structure of one of these tiles for a moment.


I can see how this should look.  I’ve already got my code to say what style to use, so when I’m making a card for each VM, I’ll set the style to change the color of the square for On/Off/Other.

Next, instead of ‘Magna’ within the Header2 tags, I want my VM Name.

If the machine is turned on, I’d also like to see it’s CPU usage and RAM pressure.  Finally, when I hover over the tile, a little section of text appears…I think that would be a cool place to list where the machine’s VHD files are, and it’s uptime.

I’ll add another if{} scriptblock, and within this one, I’ll test to see if the VM was online. If it was, I’m going to recast it’s $name property, to add a new line after the name, with RAM and CPU.  I reuse $name, so that no matter if the machine is on or off, I can have the same block of code make a square for me.

#if the VM is on, don't just show it's name, but it's RAM and CPU usage too
    if ($VM.State -eq 'Running'){


         RAM: $($VM.MemoryAssigned /1mb)

         CPU: $($VM.CPUUsage)"

I also want to have a little description of the VM, like where it’s VHD files live, etc. So I’ll set the value of $description like this:

$description= @"
        Currently $($VM.Status.ToLower()) with a 

        state of $($VM.State)

        It was created on $($VM.CreationTime)

        Its files are found in $($VM.Path)

We’ve got all the bits we need to make a card, we can now just drop in the HTML for a card in a here-string, and put the variables we’ve made here in place of the name and descrption.

$tile = @"
<article class="$style">
            <span class="image">
                <img src="images/pic01.jpg" alt="" />
            <a href="generic.html">
<div class="content">



And now, repeat after me…

String concatenation isn’t ALWAYS evil.

Because that’s totally what we’re about to do. We broke the file into three bits. Now it’s time to put it back together. To end the for-each scriptblock for each card, we’ll add the current card to $main.

Then, we build our completed file, by adding $head + $main + $tail, and then we dump that into an HTML file. Easy peasey!

$main += $tile

$html = $head + $main + $tail

$html > .\VMReport.html

Final Touches

Now you’ll probably want to open up head.html and replace the text there with your branding. You’ll also want to add in an image, most likely.

Adding the time

To add in the current time the report was generated, add in a string we can replae when importing the file. I added the string %4 to line 4 in head.html, like so:

<div class="inner">
<h1>FoxDeploy Health Monitoring Dashboard</h1>
At a glance monitoring of status of VMs in Hyper-V updated at %4

 this gives me an easy anchor to replace when I import the file, so I can use -replace %4 with the current time, like this:
$head = (Get-Content .\head.html) -replace '%4',(get-date).DateTime
Auto refreshing the page

I’d like to make the page automatically reload every 30 seconds, so add this line to your head.html page.

<meta http-equiv="refresh" content="20"; URL="path to your report.html">
Run forever

It would also be nice to have this automatically run until the end of time, so I’ll just add an open-ended for loop to the script, and then add a Start-Sleep timeout at the end. This way, the report will generate once every 15 seconds or so, and the browser will auto refresh every 20 seconds, so the two should be mostly in sync.

#Add to first line of the script

#Last line of script
Start-Sleep -Seconds 15}

And the finished productPhantomDashboard

Next Steps

I’ve not completed this part, but a KILLER next step would be to make these buttons work when you click them.  Currently, they all link to generic.html, but instead, you could use this same process to create a page for each VM and name it VMname.html.  Then when you build the card, add the appropriate link and bam, you have a fully functional VM dashboard.

If you go this route, consider adding a Windows Event view, or deeper VM statistics.  You could really go hog-wild here.  Another cool idea is to make use of the images provided in this template, and provide a background image for the tiles.

I’ve got you this far, time for you to make it your own.

I’m just scrolling till I see the word ‘Download’

Here you go, buddy 🙂 Code Download

34 thoughts on “Building Better PowerShell Dashboards

  1. svangulick April 11, 2016 / 11:03 am

    This is pretty neat stuff! I am defenitly looking into this! Thanks for sharing Stephen 🙂

    Liked by 1 person

  2. Tosin Vaithilingam April 11, 2016 / 9:30 pm

    Hi Stephen Unfortunately my company laptop complains to download the JSS folder and its blocked. Is there a way not to use the Dashboard and just get the results on an HTML with the output that I was looking for

    DB01 DB02 DB03
    1,2 GB 2.3GB 4.9 GB


    • FoxDeploy April 11, 2016 / 9:47 pm

      Sure! if you don’t mind, run whichever cmdlets you need to, to get your output of DB01, DB02, etc. Then, pipe that into Export-CLIXml. Put the resulting file on pastebin or somewhere similar (make sure to santize it by removing your company name or anything important). Send me a link to the file and I’ll work on it and let you know what I come up with. I don’t have a similar dataset to work with, so I’ll need ot play with it a bit.


  3. Tosin Vaithilingam April 11, 2016 / 10:35 pm

    Thanks for your quick answer. I am just trying to make the changes accordingly on the vmreport.ps1 and will see the results. In case it does not work then would let you know..


  4. Tosin Vaithilingam April 11, 2016 / 10:51 pm

    I don’t believe it But this worked. I just made the changes to vmreport.ps1 as well as some changes on the Head and removed tail section.

    The only problem is now that the boxes are very big compared to what I want as well as the text.
    There is another problem where the disk space would come up in bytes and would overlap the test if I use /1024 it complains that it cannot convert the 3.75 GB (4,026,531,848 bytes) to System.Int32.
    Thanks again for the brilliant code and your help


    • Tosin Vaithilingam April 11, 2016 / 11:19 pm

      Just to update I fixed the Size issue by splitting it $dbsize.split(‘ ‘)[0]

      So now only things remain is the test size and box size so I can have more rows in 1 column instead of 3


  5. Tosin Vaithilingam April 12, 2016 / 12:48 am

    Hi Stephen

    I was able to take care of the Font Size. If editing the Main CSS there is Font Size which I made it to 8PT. But I cant figure out how to add more boxes in a row instead of 3. I guess that’s the last bit remaining./..Thanks again for the wonderful work…


    • FoxDeploy April 12, 2016 / 8:53 am

      To edit the number of columns which appear, you’ll want to edit main.css, around line 2800, and replace the number 33% with 25% (for four columns) or 12.5% (for eight columns). This percentage controls how many columns appear.


      • Tosin Vaithilingam April 12, 2016 / 9:34 am

        Awesome I will give it a shot and let you know…

        Also how about if we want to add more tabs suppose on Top of Page which can take It to different dashboards…Like a Hyperlink or something….Thanks in advance for all the time you took to help…


  6. Tosin Vaithilingam April 12, 2016 / 2:18 am

    The other Problem is if you send the Html Report on an email the Styling is all gone 😦 I added parameter of Send-Email in the body of the email and the formatting is gone.


    • FoxDeploy April 12, 2016 / 8:44 am

      The formatting of the message comes from CSS, so you need to change the css path to point to a location your e-ail receipient can get to. As the files are today, it’s all relative paths, which will only work on your local PC.

      The easy way to do this is to get a static content webserver like Abyss or Nginx and use that to publish the directory as a web server.


      • Tosin Vaithilingam April 12, 2016 / 9:36 am

        Yeah it a bit messy to send over email with location to servers… I will better host it on IIS and have a link sent . This way they can view the Report whenever they want and no housekeeping of emails is needed…I am just wondering If I can have a tab too to keep the historic reports say for last 4 weeks or so…that will be so cool…


  7. tosshal April 14, 2016 / 1:11 am

    Hi Stephen

    Hope you doing good. I could not see it on line 2800 Is it this one at line 2803

    .tiles article {
    width: calc(33.33333% – 1.25em);
    margin: 1.25em 0 0 1.25em;


      • tosshal April 14, 2016 / 9:30 am

        Thanks I will take a look….Also do you know which one would be for the font size….Regards.. I am trying build up a reporting toolband I guess it will be helpful for others if I succeed….


      • tosshal April 14, 2016 / 8:46 pm

        Hi Stephen. Sorry to bother..I tried to make the change but the HTML page still comes up with 3 columns only… Made it to 12.5%

        .tiles {
        margin: -1.25em 0 0 -1.25em;

        .tiles article {
        width: calc(12.5% – 1.25em);
        margin: 1.25em 0 0 1.25em;


        • tosshal April 18, 2016 / 12:08 am

          I found the one to have more columns its actually at the 2605

          position: relative;
          width: calc(20% – 2.5em);
          margin: 2.5em 0 0 2.5em;
          But it will make the boxes small in order to fit. So need to be careful with the text does not overlaps


        • FoxDeploy April 18, 2016 / 9:32 am

          Alright Tossh, after all of this help, you are now legally required to share with us! 🙂 PLease share a screen shot of how it looks for you


      • tosshal April 19, 2016 / 10:11 am

        Yes Sure..I will share a Screenshot … There have been some issues lately on exchange so from couple days I could not do much..


  8. tosshal April 17, 2016 / 11:28 pm

    Hi Stephen, Hope the weekend was good. Another question that came to my mind since I am building it for Multiple DAGs I was wondering If I can make separate cards according to the DAG name and put a Title Before the Card.

    So suppose in the VM example if I have VMs according to regions Europe, US etc…so i can have a small title before the card starts

    So Example Title DAG01 and then the contents of the card shows, Title DAG02 and the contents of the card shows. i tried to make changes on the card with adding Header but it swill show the header on all Cards except the title..

    I was wondering should I do this under head.html ?? But that wont then have it across the cards and the name is different too.


  9. sanju April 28, 2016 / 9:46 am

    Hi Steve, Hope you good..I will do the screenshot sooner. Been busy with other stuff.. Sorry


  10. Seth September 6, 2016 / 9:53 am

    I love this. Used it to create a printer dashboard for the Service Desk and Desktop Support groups. My one question is about the generic.html page. Is there a way to make it so that clicking on the tile takes you to the web interface of the printer? I can get the information about the IP address of the printer from the portname field, but when I use a variable to place that IP address where the generic.html portion of the create a tile section is, it just tacks it on to the end of the web address.


    • FoxDeploy September 6, 2016 / 11:22 am

      Can you post your full code as a gist or paste bin, and then give me the link + the line number in question?


      • Seth September 6, 2016 / 1:51 pm

        I actually got this part working after fidding around. I’ll have to create a github account and post it up for anyone that wants to use it for printers.


      • Seth September 6, 2016 / 1:52 pm

        $IPAddress=”http://”+$Printer.PortName correct formatting of this line got it working. Sometimes I just need to slow down a bit.


        • FoxDeploy September 6, 2016 / 3:49 pm

          Good work! Did you fix the big side pieces? Whenever I want to find where a setting like that lives in css, I browse to the site in Chrome, and then hit F12 for dev tools. Then you can literally mouse over to the area you’d like to adjust, right click to edit and it will take you to the appropriate element in CSS.


        • Seth September 7, 2016 / 9:06 am

          Still working on the CSS. Once I get it fixed, I’ll put it up on pastebin.


        • Seth September 7, 2016 / 9:51 am

          Finally found it. It was in a weird (to me) place. It’s part of the wrapper, all the way at the bottom.

          Line 5 in the pastebin corresponds with line 3341 (for me) in the main.css file. Just go all the way to the bottom, and you’ll see it. By default it was 68em, and reducing the number makes the margin bigger. I played with it until 90, and it looks good. Imgur isn’t liking my uploads right now, but if that changes, I’ll upload a photo.


  11. Seth September 6, 2016 / 1:51 pm

    Is there a way to reduce the left and right margins for the tiles? Roughly 30% of the side to side space is taken up, and I can’t seem to find where it’s controlled in the css. Here’s an image that shows all the white space:


  12. Seth September 13, 2016 / 12:28 pm


    Here is the image I promised. I have made a few additional changes since this was taken. The dashboard now shows which print server the device is on, and when you click on the tile, it takes you to the web interface of the device.

    I’ll also include a link to the sanitized version of my code, removing company info, and now with comments. At least that way, people like me might be able to follow along with what I did.


    • FoxDeploy September 13, 2016 / 12:50 pm

      This is great! Thanks for sharing!


  13. Josh March 8, 2017 / 9:51 am

    Great post! Helped me knock out some PoC dashboards quickly! 🙂

    If it helps anyone else that encountered this: one thing I noticed was that the styling for the page wouldn’t display in Chrome or Firefox, but would in IE/Edge. It turned out the encoding of the VMReport.html file was different to UTF-8; UCS-2 LE BOM.

    If you change the line: $html > C:\VMReport.html to $html | Out-File C:\VMReport.html -Encoding Utf8 , the encoding of the html file will stay consistent and will display properly in all browsers.




    • FoxDeploy March 8, 2017 / 1:58 pm

      That’s a great find! Thanks for sharing


Have a code issue? Share your code by going to and pasting your code there, then post the link here!

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.