Last Update: Sep 04, 2024 | Published: Aug 04, 2017
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:scriptsdb.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:tempsysreport.htm
I moved the computer name to the report title. Here is the final result.
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.