close

Windows

Cloud

Microsoft 365

PowerShell

Active Directory

Security

Windows Server

Video

Upcoming FREE Conference on Identity Management and Privileged Access Management

Home

PowerShell

Enhancing HTML Reports with PowerShell

Jeff Hicks

|


I hope you have been enjoying this little series on tips and tricks for doing more with ConvertTo-HTML and creating great looking reports in PowerShell. Today, I want to continue where we left off. I am assuming you have been following along. If not, take some time to start at the beginning or you may be a bit lost.
 

 
Often, I create reports from a number of HTML fragments. Usually, I have multiple pieces of information that I want to display. Perhaps, I want to parse the HTML, so I can do things, such as dynamically add style classes as I did last time. I am going to repeat that process today by reusing the core code from the previous article. In addition to event log information, I want to get some other system information as well. Let’s begin with it empty:

$fragments = @()

The first thing I want to do is embed a graphic file in the document. The easy way would be to use the <IMG> tag and specify the path to a graphic file. However, I like making my HTML files portable or self-contained. The first step is to convert the file to a Base64 string.

$ImagePath = "c:\scripts\db.png"
$ImageBits =  [Convert]::ToBase64String((Get-Content $ImagePath -Encoding Byte))

When I create the IMG tag, I want to include an ALT attribute for the file name. I also need to know the file type.

$ImageFile = Get-Item $ImagePath
$ImageType = $ImageFile.Extension.Substring(1) #strip off the leading .

All that remains is to create the IMG tag.

$ImageTag = "<Img src='data:image/$ImageType;base64,$($ImageBits)' Alt='$($ImageFile.Name)' style='float:left' width='120' height='120' hspace=10>"

The only thing you really need is the src value. You can experiment with style settings or other IMG attributes. Since I want the graphic at the top of the file, I will add it to the array of fragments.

$fragments+= $ImageTag

I know that I am going to include some operating system information, so I might as well add a header for it.

#adjust spacing - takes trial and error
$fragments+= "<br><br>"
$fragments+= "<H2>OS Info</H2>"

I plan on using Get-CimInstance to retrieve operating system information. I only need a few properties converted to an HTML fragment.<p>

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

You should notice that I did something different here. By default, fragments are created as HTML tables. For this section, I wanted a list. You can mix and match lists and tables as you see fit.
Next up is some system information, which will come from this function:

Function Get-SystemInfo {
[cmdletbinding()]
Param([string]$Computername = $env:COMPUTERNAME)
#this function has no real error handling
$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
}

Remember, you always want to convert objects to HTML. In this situation, I want the system information displayed as a list too.

$fragments+= "<H2>System Info</H2>"
$fragments+= Get-systeminfo -Computername $env:COMPUTERNAME | ConvertTo-Html -Fragment -As List

From here, I will reuse my code from last time to get and process event log information, highlighting empty logs with red text. I will also include my standard footer.

$fragments+= "<H2>EventLog Info</H2>"
[xml]$html  = 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}} | 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+= "<p class='footer'>$(get-date)</p>"

The last part is to define my hashtable of parameters for ConvertTo-HTML and create the final document.

$convertParams = @{
  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;
}
</style>
"@
 body = $fragments
}
convertto-html @convertParams | out-file d:\temp\sysreport.htm

I moved the computer name to the report title. Here is the final result.

A mixed HTML report built with PowerShell (Image Credit: Jeff Hicks)

A Mixed HTML Report Built with PowerShell (Image Credit: Jeff Hicks)


Of course, all of this code is something you would put into a script or function to make it easier to use. Not that the example I have been working with is especially compelling, but I hope you will pay more attention to the techniques I used. That is the real takeaway. If you get stuck with your own PowerShell scripts creating HTML reports, I encourage you to use the PowerShell forums.

Before we leave this topic, I want to come back one more time and show you a technique I think you will find useful.

Most popular on petri

Article saved!

Access saved content from your profile page. View Saved