PowerShell

Adding Style to PowerShell HTML Reports

Over the last few articles, I have demonstrated some HTML tips and tricks for creating HTML reports in PowerShell. We are going to pick up where we left off. If you are just jumping in, take a few minutes to get caught up. I am going to use the same event log data as my source.

 

 

$data = Get-Eventlog -List | 
Select @{Name="Max(K)";Expression = {"{0:n0}" -f $_.MaximumKilobytes }},
@{Name="Retain";Expression = {$_.MinimumRetentionDays }},
OverFlowAction,
@{Name="Entries";Expression = {"{0:n0}" -f $_.entries.count}},
@{Name="Log";Expression = {$_.LogDisplayname}}

In the previous file, I used a CSS that included this entry:
.footer{ color:green; 
  margin-left:25px; 
  font-family:Tahoma;
  font-size:8pt;
}

However, my final files never had any green text. My original intention was that the date information at the end of the report would be the footer. In order for the style to apply, the final HTML code needs to look like this:
<p class='footer'>07/24/2017 15:07:29</p>

Because this is post-content, all I need to do is use a value like this for the -PostContent parameter:
"<p class='footer'>$(get-date)</p>"

Here is the complete hashtable of parameters that I am going to splat to Convertto-HTML:
$convertParams = @{ 
 PreContent = "<H1>$($env:COMPUTERNAME)</H1>" 
 PostContent = "<p class='footer'>$(get-date)</p>"
 head = @"
 <Title>Event Log Report</Title>
<style>
body { background-color:#E5E4E2;
       font-family:Monospace;
       font-size:10pt; }
td, th { border:0px solid black; 
         border-collapse:collapse;
         white-space:pre; }
th { color:white;
     background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px ;white-space:pre; }
tr:nth-child(odd) {background-color: lightgray}
table { width:95%;margin-left:5px; margin-bottom:20px;}
h2 {
 font-family:Tahoma;
 color:#6D7B8D;
}
.footer 
{ color:green; 
  margin-left:10px; 
  font-family:Tahoma;
  font-size:8pt;
  font-style:italic;
}
</style>
"@
}

$data | convertto-html @convertParams | out-file d:\temp\a.htm

The formatted footer (Image credit: Jeff Hicks)
The Formatted Footer (Image credit: Jeff Hicks)

Sponsored Content

What is “Inside Microsoft Teams”?

“Inside Microsoft Teams” is a webcast series, now in Season 4 for IT pros hosted by Microsoft Product Manager, Stephen Rose. Stephen & his guests comprised of customers, partners, and real-world experts share best practices of planning, deploying, adopting, managing, and securing Teams. You can watch any episode at your convenience, find resources, blogs, reviews of accessories certified for Teams, bonus clips, and information regarding upcoming live broadcasts. Our next episode, “Polaris Inc., and Microsoft Teams- Reinventing how we work and play” will be airing on Oct. 28th from 10-11am PST.

That is pretty easy because we can control the final HTML. What about HTML when it is auto-generated? This takes a little more work on your part.

If you recall, I decided that with my report, I wanted any log files with 0 entries to be displayed in red. I might as well show you how to highlight the entire row, as well as just the value. In both cases, I need an HTML fragment for the main body and I am going to instruct PowerShell to treat it as an XML document.

[xml]$html = $data | convertto-html -Fragment

Now the fun part. I need to loop through each table row and check the value of the entry count.

for ($i=1;$i -le $html.table.tr.count-1;$i++) {
  if ($html.table.tr[$i].td[3] -eq 0) {
    $class = $html.CreateAttribute("class")
    $class.value = 'alert'
    $html.table.tr[$i].childnodes[3].attributes.append($class) | out-null
  }
}

I am starting my loop at 1 because the table header is row 0. I will keep looping for every table row in the table. For each row, I check the value of the 4th table column, (starting at 0 remember?) which is the column with my entry count. If the value is equal to 0, I will create the class attribute and append it to the column entry. The final results are in the InnerXML property.

Dynamically modified HTML (Image credit: Jeff Hicks)
Dynamically Modified HTML (Image credit: Jeff Hicks)

I need to make sure I define this style.

$convertParams = @{ 
 PreContent = "<H1>$($env:COMPUTERNAME)</H1>" 
 PostContent = "<p class='footer'>$(get-date)</p>"
 head = @"
 <Title>Event Log Report</Title>
<style>
body { background-color:#E5E4E2;
       font-family:Monospace;
       font-size:10pt; }
td, th { border:0px solid black; 
         border-collapse:collapse;
         white-space:pre; }
th { color:white;
     background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px ;white-space:pre; }
tr:nth-child(odd) {background-color: lightgray}
table { width:95%;margin-left:5px; margin-bottom:20px;}
h2 {
 font-family:Tahoma;
 color:#6D7B8D;
}
.alert {
 color: red; 
 }
.footer 
{ color:green; 
  margin-left:10px; 
  font-family:Tahoma;
  font-size:8pt;
  font-style:italic;
}
</style>
"@
}

I already have the HTML body, so I will not be piping $data to Convertto-HTML. Instead, I will add it as a parameter and splat the hashtable.
$convertParams.add("body",$html.InnerXml)
convertto-html @convertParams | out-file d:\temp\a.htm

Colorized output (Image credit: Jeff Hicks)
Colorized Output (Image credit: Jeff Hicks)

Well, almost. You can see that the 0 entries are now in red but the post and pre-content gets a bit mangled when using this technique. No worries, I wanted to demonstrate the use of fragment anyway.

With this, I can assemble a document from a number of fragments:

$fragments = @()
$fragments+= "<H1>$($env:COMPUTERNAME)</H1>"
[xml]$html = $data | convertto-html -Fragment

for ($i=1;$i -le $html.table.tr.count-1;$i++) {
  if ($html.table.tr[$i].td[3] -eq 0) {
    $class = $html.CreateAttribute("class")
    $class.value = 'alert'
    $html.table.tr[$i].childnodes[3].attributes.append($class) | out-null
  }
}
$fragments+= $html.InnerXml
$fragments+= "<p class='footer'>$(get-date)</p>"
$convertParams = @{ 
  head = @"
 <Title>Event Log Report</Title>
<style>
body { background-color:#E5E4E2;
       font-family:Monospace;
       font-size:10pt; }
td, th { border:0px solid black; 
         border-collapse:collapse;
         white-space:pre; }
th { color:white;
     background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px ;white-space:pre; }
tr:nth-child(odd) {background-color: lightgray}
table { width:95%;margin-left:5px; margin-bottom:20px;}
h2 {
 font-family:Tahoma;
 color:#6D7B8D;
}
.alert {
 color: red; 
 }
.footer 
{ color:green; 
  margin-left:10px; 
  font-family:Tahoma;
  font-size:8pt;
  font-style:italic;
}
</style>
"@
 body = $fragments
}
convertto-html @convertParams | out-file d:\temp\a.htm

To highlight the entire row in red, append the class to the row, instead of the table cell:
#$html.table.tr[$i].childnodes[3].attributes.append($class) | out-null
$html.table.tr[$i].attributes.append($class) | out-null

Highlighted rows (Image credit: Jeff Hicks)
Highlighted Rows (Image credit: Jeff Hicks)

Next time, I still have a few more tricks to show you. For now, I hope you will try these tips out. Let me know what you think

Related Topics:

BECOME A PETRI MEMBER:

Don't have a login but want to join the conversation? Sign up for a Petri Account

Register
Comments (2)

2 responses to “Adding Style to PowerShell HTML Reports”

  1. <p>Hi Jeff</p><p><br></p><p>The XML-Technique, to locate special cells and format them, is absolutely fantastic.</p><p><br></p><p>Thank You so much for this great article.</p><p>I need to rewrite some code :-)</p><p><br></p><p>Thanx</p><p>AJAL</p>

Leave a Reply

External Sharing and Guest User Access in Microsoft 365 and Teams

This eBook will dive into policy considerations you need to make when creating and managing guest user access to your Teams network, as well as the different layers of guest access and the common challenges that accompany a more complicated Microsoft 365 infrastructure.

You will learn:

  • Who should be allowed to be invited as a guest?
  • What type of guests should be able to access files in SharePoint and OneDrive?
  • How should guests be offboarded?
  • How should you determine who has access to sensitive information in your environment?

Sponsored by:

 
Live Webinar: Active Directory Security: What Needs Immediate Priority!Live on Tuesday, October 12th at 1 PM ET

Attacks on Active Directory are at an all-time high. Companies that are not taking heed are being punished, both monetarily and with loss of production.

In this webinar, you will learn:

  • How to prioritize vulnerability management
  • What attackers are leveraging to breach organizations
  • Where Active Directory security needs immediate attention
  • Overall strategy to secure your environment and keep it secured

Sponsored by: