Weekend Scripter: Color-Coded Uptime Report

Weekend Scripter: Color-Coded Uptime Report

  • Comments 8
  • Likes

Summary: Microsoft Scripting Guy, Ed Wilson, shows a script that produces red for down and green for up.

Microsoft Scripting Guy, Ed Wilson, is here. Well, I have been playing around with this script off and on all week. Last night, my old high school friend John called to discuss plans for our upcoming Kentucky trip – I am teaching a Windows PowerShell class in Georgetown Kentucky, and  I am speaking at the first ever Cincinnati Windows PowerShell User Group. The cool thing about the Cincinnati Windows PowerShell User Group is the Scripting Wife talked to Mike (who is starting the group) when we were in Columbus Ohio during the PowerShell Saturday event. We are both looking forward to helping to launch the Cincinnati Windows PowerShell User Group.

Frankenscript for sure—but it works

Note   This is the fifth blog in a series about using the Convertto-HTML cmdlet and creating a HTML server uptime report. The first blog talked about creating an HTML uptime report, the second one used Windows PowerShell to create a report that displays uptime and disk space. The third blog discusses adding color to the uptime report. The fourth blog talks about creating a scheduled job to run the script. You should read the three previous blogs prior to today’s.

I will admit it. I am not a web developer. Today’s script will prove that beyond any shadow of a doubt. The script works, but it is not pretty; and I am certain that there has to be a better way. In fact, I toyed with adding a bit of J Script to the page to do some logic, but that did not work really well because Internet Explorer blocks active content by default, and it was an extra step. In the end, the script I came up with does not display properly in Internet Explorer any way—at least not in the beta version of Windows 8. But the code works because the page does display properly in Microsoft Word. Strange. Oh well…

The entry point to the script uses two here strings to build the style and the precontent. I left the TD style (for the table detail) because I add that line based on whether the server is up. The code is shown here.

$style = @"

 <style>

 BODY{background-color:AntiqueWhite;}

 TABLE{border-width: 1px;border-style: solid;border-color: Black;border-collapse: collapse;}

 TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:DarkSalmon}

"@

  $precontent = @"

  <h1>Server Uptime Report</h1>

 <h2>The following report was run on $(get-date)</h2>

"@

 $i = 0

For each server that is passed to the script, I call the Get-Uptime function and store the returned object in a $uptime variable. I also create a variable named after the server and an incremental number. I use the PassThru parameter to return the variable that I create. This variable will hold the HTML fragment for later use. The code is shown here.

Foreach( $server in $servers)

  {

    $uptime = Get-UpTime -servers $server

    $rtn = New-Variable -Name "$server$i" -PassThru

Now if the uptime property is equal to down I create add the TD style with the color of red to my style and I create a HTML fragment using that style, and the information returned from the Get-Uptime function. I store the returned HTML fragment in the value property of the variable created with the Server name and the fragment number. This code appears here.

if ($uptime.uptime -eq "DOWN")

      {

        $upstyleRed = $style + "`r`nTD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:Red} </style>"

        $rtn.value = $uptime | ConvertTo-Html -As Table -Fragment  -PreContent $upstyleRed | Out-String

        } 

If the server is up, I add a TD element to the style with a green background, and I create an HTML fragment from the Get-Uptime results and store it in the variable created with the server name and the fragment number.

  else

     { 

        $upstyleGreen = $style + "`r`nTD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:Green} </style>"

        $rtn.value = $uptime | ConvertTo-Html -As Table -Fragment  -PreContent $upstyleGreen | Out-String

        } 

I now add the HTML fragment to the array of fragments as seen here.

 [array]$frags+=$rtn.Name

     $i++

I now need to take the array of fragment names, and expand the value property from the variables. I store the returned HTML code in the $fragments variable.

$fragments = foreach($f in $frags) { Get-Variable $f | select -ExpandProperty value }

I now use the fragments stored in the $fragments variable and the precontent created earlier, and pass it all to the Convertto-HTML cmdlet. This code is similar to code we used earlier in the week.

ConvertTo-Html -PreContent $precontent -PostContent $fragments >> $path

Invoke-Item $path 

As I mentioned at the beginning of the blog, it does not display properly in Internet Explorer, but it does display properly in Microsoft Word. Strange, but oh well. It was a fun script to write, and I hope you enjoy playing around with it.

Keep in mind, the <style> tags are being dropped right now by the Script Repository in the code. So you will need to add them manually until we can get the bug fixed. Hope you have a great week.

I uploaded the complete script to the Script Repository. You can obtain the script here.I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Ed - you have most of the technique for doing this worked out however I can see that there are a couple of bits that can make this easier.

    Here is a quick adjustment. I used a 'class' to apply style by just doing a string replacement on <table> with <table class="down">.

    Here is the result:  www.designedsystemsonline.com/.../RedGreenUpTime.html

    Here is a copy of the adjusted code;  www.designedsystemsonline.com/.../testcode.htm

    The only issue to solve is how to merge the tables.

    What you have learned is why I decided to use XML to edit the HTML.  It eliminates all of the issues that I have found.

    Note that when displayed from a web site the full impact of the DOCTYPE takes effect.  This is IE enforcing the standards.  The DOCTYPE of a default PowerShell generated HTML is set to XHTML 1.0 Strict.  This cause my favorite "AntiqueWhite" to be lost when viewed from the web.  If you copy the page locally then the body color will show.  This is an issue with the way IE is designed to default.  The key is to not use illegal constructs which is why it is hard to hand build HTML.  The tags also need to be all lowercase for complete compliance.  Remember that XML is cases sensitive. HTML is case preserving but allows any case for all elements.  XHTML is case sensitive and requires all lower case.  Vendors, on the other hand, tend to use mixed case for custom elements which is allowed.

    I moved the style sheet into the header.  It applies differently when in the header.  I also added the required CDATA guard around it so that it can be hidden for browsers that don’t accept a style sheet in the header.

    The W3C has a validator page that can be very useful: validator.w3.org

    Even after the fixes there is an error caused by the method of combining fragments which leaves an empty table tag at the end. This error can be ignored but it is useful to remove this if you want to upload the file for validation.

    The only real issue here is that there are multiple tables when there should be one.  How do we fix that?

    Anyway – it was a fun exercise.

  • Actually the fix to this is going back to the original Get-Uptime function!  By having the function do all the work and return an array we can then send the array into ConvertTo-HTML which gives us one table.  Then have to do some string searches and replaces to get the up and down styles.

    Things I added:

    To $head:

    /* add class targeting down server */

           td.down{

               border-width: 1px;

               padding: 0px;

               border-style: solid;

               border-color: black;

               color: red;

               font-weight: bolder;

           }

           /* add class targeting UP server */

           td.up{

               border-width: 1px;

               padding: 0px;

               border-style: solid;

               border-color: black;

               color: green;

               font-weight: bolder;

           }

    td{

               border-width: 1px;

               padding: 0px;

               border-style: solid;

               border-color: black;

               color: black;

               font-weight: normal;

    The Get-Uptime function:

    Function Get-UpTime{

       Param ([string[]]$servers)

    $Result = @()

       Foreach ($s in $servers){

           if(Test-Connection -cn $s -Quiet -BufferSize 16 -Count 1){

               $os = Get-WmiObject -class win32_OperatingSystem -cn $s

               $Result += New-Object psobject -Property @{Computer=$s;

               Uptime = (get-date) - $os.converttodatetime($os.lastbootuptime)}

           }else{

               $Result += New-Object psobject -Property @{Computer=$s; Uptime = "DOWN"}

           }

       }

    Return $Result

    }

    Last, the getting and manipulating of the results:

    $uptime = Get-UpTime -servers $servers

    $UptimeTable = $uptime | ConvertTo-Html -As Table -Fragment | Out-String

    ForEach ($server in $servers)

    { If ($UptimeTable -like "*$server</td><td>DOWN*")

    { $UptimeTable = $UptimeTable.Replace("<tr><td>$server","<tr><td class=""down"">$server")

    }

    Else

    { $UptimeTable = $UptimeTable.Replace("<tr><td>$server","<tr><td class=""up"">$server")

    }

    }

    No easy place to post the whole code but you get the picture.

  • Here is all the code it takes to generate a <b>colorized</b> table.

    $uptime = Get-UpTime -servers $servers | ConvertTo-Html -Fragment

    $body=Set-HtmlRowColor -html $uptime

    ConvertTo-Html -PostContent $body.innerXML -Head $head -PreContent $precontent -Title 'New Report' |

    Out-File $path

    Invoke-Item $path

    Assuming you have the function.

    Here is the function along withthe rest of the code:

    www.designedsystemsonline.com/.../testcode1.htm

    This produces a single table with colorized rows.  It is the easiest and best approach that I have found because we can further enhance this technique to do many, many more interesting things.

    The technique fully leverages the power of Powerhell to get things done with a minimum of handling of text or data.  It overcomes issues of trying to find items deeply buried or the need to have one table and still have it colorized by row.  The HTML generated is XHTML which was developed so it could be edited and generated with XML tools.

    Run the code and you will see how easy it is to generate and alter a table.  Ther are other examples that will detect a value and assign a color or icon based on the value.  This is hard to do even with RegEx.  XML makes it simple.

  • Hi Ed,

    good and hard work!

    I like horror scripts :-)

    I won't be able to do any better and I hardly use HTML directly in my working environment or at home.

    Even if evrything works ... I really don't like to do this on my own ...

    But thanks to evrybody who can do it!

    Klaus

  • @jrv yes it was a fun exercise, and I worked on off and on all week. I like your suggested changes.

    @Martin Awesome.

    @jrv yes, it permits further adaptation. cool!

    @K_Schulte yes, not doing it on ones own is a good approach. But writing from scratch (as I do) is a great way to learn, and to ensure that you know everything you have put into the script. It is also a great way to challenge assumptions  such as, "oh, I know what that does" ... types of things.

  • @Ed

    The plus with using a full style sheet is that it can be built in another tool or pulled in from a file.  We can even link multiple style sheets that put in layers of clsses and defualt settings.  These can be reused over and over.

    I actually prefer sticking them on a web server or a network share and leaving the CSS external to the report.

    HTML and CSS can be a big problem unless you use tools to work with them.  The free Web Developer Express 2010 does an awesome job of building style sheets and templates that we can just build in the GUI tool (IDE) and then inject into the PowerShell reporting process.

    I fully expect HTML 5 and CSS3 to be incorporated into all Microsoft tools including PowerShell as the foundation for a report presentation subsystem.

  • @JRV that is a great point. I like the idea of creating a CSS, storing it on a web server (or server share) and using it as a template for recurring reports. Great idea. The point about using a tool to generate the HTML is also a very good point. That was one reason I did not like HTA's is because there were not any good tools for generating them, and it was an aweful lot of coding for very little real value.

  • Martin -

    A quicker and more coveniet way to update that - similar to your method - is to use the replace operator..

    $UptimeTable=$UptimeTable -replace '<tr><td>','<tr class="up"><td>'

    $UptimeTable -replace '<tr class="up"><td>DOWN','<tr class="down"><td>DOWN'