Register for Semperis' Hybrid Identity Protection (HIP) Conference - June 30 - July 1 Register for Semperis' Hybrid Identity Protection (HIP) Conference - June 30 - July 1
PowerShell

Adding Script to Your PowerShell HTML Reports

I have been having a lot of fun with this series of articles demonstrating a variety of tips, tricks, and techniques to generate killer HTML reports with PowerShell and ConvertTo-HTML. By all means, use as little or as much as you want. If you missed the previous articles, get caught up before continuing. I will not explain the previously covered material.

 

 

I will confess right up front that I am not a web developer or even close. I know some basic HTML techniques and like you, have gleaned ideas from searching the Internet. One of those techniques is the use of javascript. While I probably could not write a javascript function from scratch, I do know how to use it in my PowerShell scripts. At some point in the past, I found some functions to create expandable sections. This is great for a long HTML document because you can collapse one or more sections.

In my header here string, where I define my CSS, I am going to insert this code.

<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js'>
</script>
<script type='text/javascript'>
function toggleDiv(divId) {
   `$("#"+divId).toggle();
}
function toggleAll() {
    var divs = document.getElementsByTagName('div');
    for (var i = 0; i < divs.length; i++) {
        var div = divs[i];
        `$("#"+div.id).toggle();
    }
}

The code assumes you will have a named DIV section that you want to collapse or expand. Let’s revise the example from the last article.


$computername = $env:COMPUTERNAME

$fragments = @()

#insert a graphic
$ImagePath = "c:\scripts\db.png"
$ImageBits =  [Convert]::ToBase64String((Get-Content $ImagePath -Encoding Byte))
$ImageFile = Get-Item $ImagePath
$ImageType = $ImageFile.Extension.Substring(1) #strip off the leading .
$ImageTag = "<Img src='data:image/$ImageType;base64,$($ImageBits)' Alt='$($ImageFile.Name)' style='float:left' width='120' height='120' hspace=10>"

$top = @"
<table>
<tr>
<td class='transparent'>$ImageTag</td><td class='transparent'><H1>System Report - $Computername</H1></td>
</tr>
</table>
"@

$fragments+=$top

Most of this should look familiar to you by now. One thing I am doing differently is putting the graphic in a table with the document title. I do not want this table to follow the same style as other tables. Therefore, I am defining a style called ‘transparent’ that sets the cell background color to the same as the document background color.
.transparent {
background-color:#E5E4E2;
}

Now, let’s add the new stuff. I want to define some section headings that when clicked, will toggle between expanded and collapsed. I also want to include a link at the beginning to toggle all sections. I will add the necessary script code to my array of fragments.
$fragments+="<a href='javascript:toggleAll();' title='Click to toggle all sections'>+/-</a>"

This will insert a link with the text “+/-“. When clicked, this will invoke the toggleAll javascript function. Now, we wll add the first section.
$Text = "Operating System"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"

I am calling the DIV section the same as the text but I need to remove any spaces for the DIV id. After this section heading, I need to insert my content and add the closing DIV tag.
$fragments+= Get-Ciminstance -ClassName win32_operatingsystem -ComputerName $computername | 
Select @{Name="Operating System";Expression= {$_.Caption}},Version,InstallDate |
ConvertTo-Html -Fragment -As List
$fragments+="</div>"

I repeat the process as needed.
$Text = "System Information"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"

$fragments+= Get-systeminfo -Computername $computername| ConvertTo-Html -Fragment -As List
$fragments+="</div>"

$Text = "Disk Information"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"

#highlight low free space in red
[xml]$html = Get-DiskInfo -Computername $computername| ConvertTo-Html -Fragment
for ($i=1;$i -le $html.table.tr.count-1;$i++) {
  if ($html.table.tr[$i].td[-1] -le 20) {
    $class = $html.CreateAttribute("class")
    $class.value = 'alert'
    $html.table.tr[$i].childnodes[3].attributes.append($class) | out-null
  }
}

$fragments+= $html.InnerXml
$fragments+="</div>"

When finished, I can create my final document from the collection of fragments. For your convenience, here is a script with all of my final code.
[cmdletbinding()]
Param(
[string]$computername = $env:COMPUTERNAME,
#the path to the final htm report
[string]$Path = "d:\temp\systemreport.htm",
#set your own default graphic or delete default value
[string]$ImagePath = "c:\scripts\db.png"
)

#region helper functions

Function Get-SystemInfo {
[cmdletbinding()]
Param([string]$Computername = $env:COMPUTERNAME)

$cs = Get-CimInstance -ClassName Win32_computersystem #-ComputerName $Computername  
#this assumes a single processor
$proc = Get-CimInstance -ClassName win32_processor #-ComputerName $Computername 
 
$data = [ordered]@{
TotalPhysicalMemGB = $cs.TotalPhysicalMemory/1GB -as [int]
NumProcessors = $cs.NumberOfProcessors
NumLogicalProcessors = $cs.NumberOfLogicalProcessors
HyperVisorPresent = $cs.HypervisorPresent
DeviceID = $proc.DeviceID
Name = $proc.Name
MaxClock = $proc.MaxClockSpeed
L2size = $proc.L2CacheSize
L3Size = $proc.L3CacheSize

}

New-Object -TypeName PSObject -Property $data

}

Function Get-DiskInfo {
[cmdletbinding()]
Param([string]$Computername = $env:COMPUTERNAME)

Get-CimInstance -ClassName win32_logicaldisk -filter "drivetype=3" -ComputerName $Computername |
Select DeviceID,
@{Name="SizeGB";Expression = {$_.size/1gb -as [int]}},
@{Name="FreeGB";Expression={ [math]::round($_.Freespace/1gb,2)}},
@{Name="PctFree";Expression={[math]::round(($_.freespace/$_.size)*100,2)}}

}

#endregion

$fragments = @()

if (Test-Path $ImagePath) {
    #insert a graphic
    $ImageBits =  [Convert]::ToBase64String((Get-Content $ImagePath -Encoding Byte))
    $ImageFile = Get-Item $ImagePath
    $ImageType = $ImageFile.Extension.Substring(1) #strip off the leading .
    $ImageTag = "<Img src='data:image/$ImageType;base64,$($ImageBits)' Alt='$($ImageFile.Name)' style='float:left' width='120' height='120' hspace=10>"
}
else {
    Write-Warning "Could not find image file $ImagePath"
}
$top = @"
<table>
<tr>
<td class='transparent'>$ImageTag</td><td class='transparent'><H1>System Report - $Computername</H1></td>
</tr>
</table>
"@

$fragments+=$top

$fragments+="<a href='javascript:toggleAll();' title='Click to toggle all sections'>+/-</a>"

$Text = "Operating System"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"

$fragments+= Get-Ciminstance -ClassName win32_operatingsystem -ComputerName $computername | 
Select @{Name="Operating System";Expression= {$_.Caption}},Version,InstallDate |
ConvertTo-Html -Fragment -As List
$fragments+="</div>"

$Text = "System Information"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"

$fragments+= Get-systeminfo -Computername $computername| ConvertTo-Html -Fragment -As List
$fragments+="</div>"

$Text = "Disk Information"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"

#highlight low free space in red
[xml]$html = Get-DiskInfo -Computername $computername| ConvertTo-Html -Fragment
for ($i=1;$i -le $html.table.tr.count-1;$i++) {
  if ($html.table.tr[$i].td[-1] -le 20) {
    $class = $html.CreateAttribute("class")
    $class.value = 'alert'
    $html.table.tr[$i].childnodes[3].attributes.append($class) | out-null
  }
}

$fragments+= $html.InnerXml
$fragments+="</div>"

$Text = "EventLog Info"
$div = $Text.Replace(" ","_")
$fragments+= "<a href='javascript:toggleDiv(""$div"");' title='click to collapse or expand this section'><h2>$Text</h2></a><div id=""$div"">"

[xml]$html  = Get-Eventlog -List -ComputerName $computername  | 
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}} | 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].attributes.append($class) | out-null
  }
}

$fragments+= $html.InnerXml
$fragments+="</div>"

$fragments+= "<p class='footer'>$(get-date)</p>"


$head = @"
<Title>System Report - $($env:computername)</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;
}
.transparent {
background-color:#E5E4E2;
}

</style>
<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js'>
</script>
<script type='text/javascript'>
function toggleDiv(divId) {
   `$("#"+divId).toggle();
}
function toggleAll() {
    var divs = document.getElementsByTagName('div');
    for (var i = 0; i < divs.length; i++) {
        var div = divs[i];
        `$("#"+div.id).toggle();
    }
}
</script>

"@


$convertParams = @{ 
  head = $head 
  body = $fragments
}

convertto-html @convertParams | out-file -FilePath $Path

Get-Item -Path $Path

I have added my default values. You would want to change them for yourself. When I run the script, I end up with a file like this.

A collapsed HTML report (Image Credit: Jeff Hicks)
A Collapsed HTML Report (Image Credit: Jeff Hicks)

I have clicked the toggle all link to collapse all the sections. I can click in the individual section headings to toggle them.

Expanded sections (Image Credit: Jeff Hicks)
Expanded Sections (Image Credit: Jeff Hicks)

What do you think? Is this something you think you would find useful? This is definitely something you want to try for yourself and feel free to experiment. Make sure you try the CSS settings, assuming you are like me, and have limited experience with it.

I hope you found this series of articles enjoyable. If there is some other HTML-related element that you would like to add to your PowerShell reports, ping me on Twitter @jeffhicks.

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 (0)

Leave a Reply

Register for the Hybrid Identity Protection (HIP) Europe Conference!

Hybrid Identity Protection (HIP) Europe 2021 - Virtual Conference

Mobile workforces, cloud applications, and digitalization are changing every aspect of the modern enterprise. And with radical transformation come new business risks. Hybrid Identity Protection (HIP) is the premier educational forum for identity-centric practitioners. At the inaugural HIP Europe, join your local IAM experts and Microsoft MVPs to learn all the latest from the Hybrid Identity world.