Monday, July 23, 2012

PowerShell: Dynamically Color PosH Generated HTML:Part 2

PowerShell: Dynamically Color PosH Generated HTML:Part 1

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

In the first part of this article I discussed using CSS styles, table ids and classes to colorize individual tables.  I ended with a question about how we might be able to colorize individual rows of a table in a way that would show up in an email message.

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.

Here is the code that will colorize the individual rows based on a columns value in that row.

If you remember from before we loaded the HTML fragment into an XML DOM for manipulation.  We will use the same DOM to find and colorize the rows.

$rows=$xml.table.selectNodes('//tr')
for($i=1;$i -lt $rows.count; $i++){
$value=$rows.Item($i).LastChild.'#text'
if($value.Length -gt 0 -and [int]$value -le $threshhold){
Write-Host "Candidate found - setting color to red at $value" -fore green
$attr=$xml.CreateAttribute('style')
$attr.Value='background-color: red;'
[void]$rows.Item($i).Attributes.Append($attr)
}
}

As you can see we are just adding an attribute to the row.

First we find all of the row objects which are represented by the ‘tr’ tag:

$rows=$xml.table.selectNodes('//tr')

This is the XPath that will return all rows of a table.  Next I enumerate the rows but skip the first row as it is the header. I use an explicit ‘for’ loop because we need the index to return the row object. The index is $i and is incremented in the ‘for’ statement.  The index allows us to dereference the ‘#text’ node of the ‘last child’ which is the end column of the row.

$value=$rows.Item($i).LastChild.'#text'

Now that I have the value of the column it is time to test the value against our ‘threshold’.  We have to cast it to an integer for this to work correctly. We also need to filter out all nodes that may be blank so I check for zero length.

if($value.Length -gt 0 -and [int]$value -le $threshhold){

Once we find a column that meets our criteria we can add our attributes and set it as a color style:  This is done by creating an attribute named ‘style’ and assigning its value to be ‘background-color: red;’  just like we would type it into the HTML tag

$attr=$xml.CreateAttribute('style')
$attr.Value='background-color: red;'

There we have successfully edited HTML without playing with strings again.  Creating and modifying HTML this way prevents breaking the HTML because the XML DOM will never generate broken XML or broken XHTML.

Send Email Table With Rows Colored to Show Alerts
$threshhold=10
$html=gwmi win32_logicaldisk |
Select-Object __SERVER,deviceid,@{N='PercentFree';E={[math]::Round($_.Freespace/$_.Size * 100,0)}} |
ConvertTo-Html -Fragment
$xml=[xml]$html
$rows=$xml.table.selectNodes('//tr')
for($i=1;$i -lt $rows.count; $i++){
$value=$rows.Item($i).LastChild.'#text'
if($value.Length -gt 0 -and [int]$value -le $threshhold){
Write-Host "Candidate found - setting color to red at $value" -fore green
$attr=$xml.CreateAttribute('style')
$attr.Value='background-color: red;'
[void]$rows.Item($i).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
}

This is what the email looks like:

image

Of course we can do more but look at what has been accomplished in only a couple of lines.  We can use theses two techniques to add more tables all colorized and alerted or highlighted in any manner we choose and all without playing with strings. We can be as fancy as we like now because the data is being stuffed into html fragments for us and we can then decorate the data in a very controlled fashion.

What is next?

Next time I will attempt to combine these techniques into a web page with multiple tables and FTP it to a public web site.  I will also show how to add external style sheets and how to inject VBScript or JavaScript into the pages generated by PowerShell.  Maybe we can even look at PowerShell generated ASP.NET pages.

Until next time….

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

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