Sunday, July 22, 2012

PowerShell: Dynamically Color PosH Generated HTML. - Part 1

PowerShell: Dynamically Color PosH Generated HTML:Part 1

PowerShell: Dynamically Color PosH Generated HTML:Part 2
PowerShell: Dynamically Color PosH Generated HTML:Part 3

PowerShell is excellent at dumping data into HTML tables.  We can gather ‘fragments’ of tabled data and combine these tables into a web page to create very complex reports. With PowerShell tools we can do this almost effortlessly and with little knowledge of HTML.  So why is this not used more often?  How come we keep seeing scripters generating hand gathered html written line by line into a file?

The best way to manage HTML is through the DOM (Document Object Model).  In IE we can access the DOM through the ‘window.document’ object.  Why not just load the document into IE and create or modify reports?

There are issues with doing this. Besides being cumbersome it does not play well when the script is run unattended under the Task Scheduler.  We also may only want to set a color or two then send the HTML as an email.  IE is overkill in most cases.

PowerShell has the ConvertTo-Html CmdLet which can do many things including generating fragments and combining fragments into a page.Here is a link to a great two-part article on how to generate HTML multi-table reports:

http://blogs.technet.com/b/heyscriptingguy/archive/2012/06/04/powershell-in-depth-part-1.aspx
http://blogs.technet.com/b/heyscriptingguy/archive/2012/06/04/powershell-in-depth-part-2.aspx

The strength in the above-linked approach is that it allows us to gather all manner of data into simple tables and throw them onto a page.  The page display can be modified via the injected CSS with one drawback.  If we need to send the HTML by email much of the power of CSS2 will be lost as most email HTML supports only a subset of CSS1.

Here are a couple of simple ideas and techniques that can help to extend the usefulness of the PowerShell HTML CmdLet.

  1. Use the XML DOM to edit the HTML fragment.
  2. Add an ID to each table fragment which will allow you to control the style of individual tables independently.
  3. Use XPath to select rows and set color based on the row’s value.

Here are the techniques to accomplish the above items.

Load HTML Fragment into XML DOM
# get some data for demo and generate a fragment
$html=gwmi win32_logicaldisk |
Select-Object deviceid,@{N='PercentFree';E={[math]::Round($_.Freespace/$_.Size * 100,0)}} |
ConvertTo-Html -Fragment
# load HTML into XML DOM
$xml=[xml]$html

That’s it. Just generate the output to the HTML converter and capture in a variable.  Send the variable through the XML type accelerator and save it in the variable $xml.

But you are thinking it is HTML and not XML right?  Well it is really HTML. PowerShell generates XHTML compliant HTML which is also XML.  While we cannot load a full page because of the ‘DOCTYPE’ header line we can load a fragment because it is raw and legal XML.

The HTML we just loaded looks like this:

<table>
<colgroup>
<col/>
<col/>
</colgroup>
<tr><th>deviceid</th><th>PercentFree</th></tr>
<tr><td>A:</td><td></td></tr>
<tr><td>C:</td><td>3</td></tr>
<tr><td>D:</td><td></td></tr>
<tr><td>E:</td><td>87</td></tr>
<tr><td>F:</td><td>89</td></tr>
</table>

Perfectly legitimate XML because all tags are closed correctly and there is no conflicting DOC header.

Now we can modify the HTML very easily using out XML editing tools.

Add an ID to the Table

First let’s put an ID on the table. We will call the table id=”diskTbl”.  To add an ID we need to add an attribute to the table tag and set its value.  We do this by using the XML CreateAttribute method.

$attr=$xml.CreateAttribute('id')
$attr.Value=’diskTbl’

Next we get the table tag and append the new attribute.

$xml.table.Attributes.Append($attr)

That is it.  The table now has an attribute called ‘id’ with our value.  Want to see?

<table id="diskTbl"><colgroup><col /><col /></colgroup><tr><th>deviceid</th><th>PercentFree</th></tr><tr><td>A:</td><td
></td></tr><tr><td>C:</td><td>3</td></tr><tr><td>D:</td><td></td></tr><tr><td>E:</td><td>87</td></tr><tr><td>F:</td><td
>89</td></tr></table>

See.  The attribute gets nicely tucked into the table tag and no playing with messy strings and broken HTML.  Ok.  The HTML is no longer ‘pretty’ printing.  We can fix that later.  HTML and XML don’t care what they look like so we can change it around using our XML Stream Writer later.

So boys and girls, that is the story of how to manipulate HTML in the DOM.  You can now add a tag “id” into your CSS and make every table a different color.

Add Other Attributes The Same Way

#diskTbl { background-color: blue; }

You could also use the technique to add a class attribute and add multiple cascading classes to your table.

$attr=$xml.CreateAttribute('class')
$attr.Value=’red box wrap’

Now our CSS can look like this:

,red { background-color: red; }
.blue {…. }
.box { border-collapse: collapse; border-style: solid; border-width: 1px; }
.nobox {…..}
.wrap { ….}
.nowrap {….}

Using that technique we can add additive styles to an object from a general purpose style sheet.  Now ConvertTo-Html is becoming potentially very useful.

We can use the returned html to send an HTML mail message

Send Colorized Email With PowerShell
$html=gwmi win32_logicaldisk | 
Select-Object deviceid,@{N='PercentFree';E={[math]::Round($_.Freespace/$_.Size * 100,0)}} |
ConvertTo-Html -Fragment
$xml=[xml]$html
$attr=$xml.CreateAttribute('id')
$attr.Value=’diskTbl’
$xml.table.Attributes.Append($attr)
$html=$xml.OuterXml|Out-String
$style='<style type=text/css>#diskTbl { background-color: blue; }</style>'
$body=ConvertTo-Html -head $style -body $html -Title "Disk Usage Report"|Out-String
$msg=@{
To=$to
From=$from
Subject="Disk usage report for $([datetime]::Now)"
BodyAsHTML=$true
Body=$body
}
Send-MailMessage @msg

Of course this only shows one table but, if we can color one, we can color as many as we like.  The important thing is that we can set style elements for individual table.  We can also add additive style for convenience.  We have also done this without dumping to and from a file and without playing with strings.

More?

How about coloring individual lines?  Can we use this to set a lines color depending on the value of a column? Of course.  Next time I will show how easy it is to do that although I have already given you almost all of the pieces for doing it.  We just need about three more lines of code and some knowledge about how XML works and how to make HTML do some of the work for us.

Part 2: http://tech-comments.blogspot.com/2012/07/powershell-dynamically-color-posh_23.html

3 comments:

  1. I've been looking for a way to output one LARGE table to an html file using a floating header line. While this can be done in a multitude of ways using CSS or Javascripting, I have had difficulties getting used to tagging objects to accommodate a css approach to using a floating header for a table. Many approaches also use DIV's for this as well. Is there any way you could try an approach to this and post online?

    ReplyDelete
    Replies
    1. I recommend getting this to work in an HTML page first. Once you can master the HTML 5 for this then you can use PowerShell to generate data into your template.

      Delete
    2. Here is a link to some samples of ways to anchor a table header to the top of the window.

      http://cmcqueen1975.bitbucket.org/htmlFloatingTableHeader/tables.html

      Delete