Learn What IT Pros Need to Know About Windows 11 - August 24th at 1 PM ET! Learn What IT Pros Need to Know About Windows 11 - August 24th at 1 PM ET!
PowerShell

PowerShell Problem Solver: Create a Grouped HTML Report with PowerShell

I always talk about “objects in the pipeline” when writing or training about PowerShell. We shouldn’t be thinking about trying to parse text or even be too concerned about individual items. Most of the time we can simply let the PowerShell pipeline do all of the work for us. With that said, you can take control, too. One such situation might be when you want to look at the results of PowerShell expression, where it would help to see results grouped together. Taking this scenario a step further, you may want to create an HTML report using grouped output. If you look at help for Convertto-HTML, you won’t see a –Group parameter or anything that looks like it will help. As I’ll show you, it isn’t too difficult to achieve.

First, let’s look at creating a nice formatted group report in the console. We’ll begin with some data.

​
The variable $data should contain all the files in my Scripts folder. Let's say I want to create a formatted report that shows files grouped by directory.
​
With the Format-Table cmdlet, you can specify a property name to group by. I recommend you sort on that same property first as I've done.

Create a Grouped HTML Report with PowerShell

This isn't too difficult because PowerShell has built-in instructions that tell it how to format a table of file objects. What I want next is to duplicate this in an HTML file. To accomplish this, I'll need an intermediate step to group the data accordingly.
​
Technically, I didn't have to sort before grouping. But the result will be that each group of related files is now sorted first by directory and then length,

Each group of related files is now sorted first by directory and then length. (Image Credit: Jeff Hicks)
Each group of related files is now sorted first by directory and then length. (Image Credit: Jeff Hicks)
I now have a new set of objects. The file objects are in the Group property for each grouped entry. To create the HTML report, I will need to use fragments. Instead of piping directly to Convertto-HTML, I'm going to create my own body. I'll start with a blank string.
​
Now for the fun part. I'm going to enumerate each grouped entry in $grouped. I'm going to use the Name property as my heading with an H2 HTML tag. Next, I'm going to send the collection of files and create an HTML table fragment. A table is the default, so I didn't have specify the –As parameter, but I wanted to demonstrate it in case you needed a list instead.
​
You have to remember that Convertto-HTML will look at any incoming objects and convert all properties, not just the ones you see on the screen. That's why I'm selecting a few objects first. With the body complete, I'm now ready to create the final document.
​
I'm adding style from an external CSS file and including a footer of sorts with –PostContent displaying the current date and time. This is what I end up with:



You could follow the same process for any type of object. Because this is likely to be a repeatable process, this is something you want to turn into a PowerShell tool. For those of you panicking thinking you can't do it, relax. Here's my version of a tool that I call Out-HTMLReport.
​

Function Out-HTMLReport {
<#
.SYNOPSIS
Send the output of a command to an HTML report.

.DESCRIPTION
Send output from a PowerShell expression to this command and create a formatted HTML report. For best results you should specify the properties to display or use Select-Object earlier in your expression. You can also group results by a property name.

.PARAMETER InputObject 
The output from some PowerShell expression.
.PARAMETER Path 
The filename and path for the HTML file.
.PARAMETER PreContent 
Content to be used at the beginning of your HTML document. This can include HTML tags.
.PARAMETER CssUri 
The path to a CSS file. See examples for how to embed a style sheet in the document.
.PARAMETER As 
Data will be formatted as fragments. It can be either a Table (default) or a List. All data will be formatted the same.
.PARAMETER Group 
The property to group data on. If you specify a grouping property, this property will not be displayed in the table or list output.
.PARAMETER Properties 
The properties to display in the report. The default will be all properties.
.PARAMETER Title 
The title for your HTML report.
.PARAMETER Head 
Content to use for the Head section of the HTML document.
.PARAMETER PostContent 
Content to be used at the end of your HTML document. This can include HTML tags.

.EXAMPLE
PS C:\> get-service | out-htmlreport c:\work\services.htm -properties Name,Displayname,Status

Create a very basic HTML report of service information with no formatting.
.EXAMPLE
PS C:\> get-process | Where Company | Out-HTMLReport -Path c:\work\procs.htm -Properties name,id,ws,vm,pm,path -title "Process Report by Company" -group Company -CssUri C:\scripts\blue.css

Get all processes where a Company property is defined and create an HTML report grouping on the company property. Selected properties for each object will be displayed as a table. This example is using an external CSS file.
.EXAMPLE
PS C:> dir -file | select Fullname,Name,@{Name="Size";Expression={$_.length}},CreationTime,LastWriteTime,@{Name="Age";Expression={(Get-Date) - $_.LastWriteTime}},Attributes,Extension | out-htmlreport c:\work\files.htm -Group extension -as List -title "File Report" -CssUri c:\scripts\blue.css

If you want to use custom properties, define them before sending to Out-HTMLReport. If you will be grouping be sure to include the grouping property.
.EXAMPLE
PS C:\> $data = Get-HotFix

Get hotfix data from the local computer.

PS C:\> $head = @"
<Title>Hot Fix Report</Title>
<style>
body { background-color:#FFFFFF;
       font-family:Tahoma;
       font-size:12pt; }
td, th { border:1px solid black; 
         border-collapse:collapse; }
th { color:white;
     background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px }
tr:nth-child(odd) {background-color: lightgray}
table { width:95%;margin-left:5px; margin-bottom:20px;}
</style>
<br>
<H1>Hot Fix Report</H1>
"@

Define a here string for the HTML head. This includes an embedded style sheet.

PS C:\> $paramHash = @{
 Head = $head
 Title = "Hot Fix Report"
 Path = "c:\work\hotfix.htm"
 Group = "Description"
 As = "List"
 PostContent = "<H6>$(Get-Date)</H6>"
 Properties = "HotFixID","InstalledOn","InstalledBy","Caption","PSComputername"
}

Define a hash table of parameter values to splat to Out-HTMLReport

PS C:\> $data | Out-HTMLReport @paramHash

Create the report.
.NOTES
Version     : 1.0
Last Update : February 27, 2015
Author      : Jeff Hicks (@jeffhicks)

Learn more about PowerShell:
Essential PowerShell Learning Resources
**************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .INPUTS [object[]] .OUTPUTS none .LINK ConvertTo-HTML #> [cmdletbinding(SupportsShouldProcess=$True)] Param ( [Parameter(Position=0,Mandatory,HelpMessage = "Enter the name of the report to create")] [ValidateNotNullorEmpty()] [string]$Path, [string]$Group, [ValidateNotNullorEmpty()] [string[]]$Properties="*", [string]$CssUri, [ValidateSet("Table","List")] [ValidateNotNullorEmpty()] [string]$As = "Table", [string]$Title, [string]$Head, [string[]]$PreContent, [string[]]$PostContent, [Parameter(Position=1,Mandatory,ValueFromPipeline, HelpMessage="Enter objects to format")] [ValidateNotNullorEmpty()] [object[]]$InputObject ) Begin { Write-Verbose "Starting $($myinvocation.mycommand)" #copy most of the bound parameters since they will #be passed to Convertto-HTML Write-verbose "PSBoundParameters" write-Verbose ($PSBoundParameters | out-string) #initialize an array to hold all the processed data [email protected]() #iniatilize a variable for the HTML body [string[]][email protected]() $body+=$PreContent } #begin Process { #add each input object to $data foreach ($item in $Inputobject) { $data+=$item } } #process End { Write-Verbose "Processing $($data.count) objects" #sort on grouping property if used if ($Group) { Write-Verbose "Grouping on $Group" $data | Group-Object -Property $Group | Sort-Object -Property Name | foreach { $body+="<H2>$($_.Name)</H2>" $body+= $_.Group | Select-Object -property $Properties -ExcludeProperty $Group | ConvertTo-HTML -As $As -Fragment } #foreach } else { Write-Verbose "No grouping" $body+= $data | Select-Object -property $Properties | ConvertTo-HTML -As $As -Fragment } #create the HTML $htmlParams = $PSBoundParameters #remove conflicting or unused parameters "InputObject","Path","Group","Properties", "PreContent","WhatIf","Confirm" | foreach { $htmlparams.Remove($_) | out-null } #add body $htmlParams.Add("Body",$body) Write-Verbose "Using these Convertto-HTML parameters" Write-Verbose ($htmlParams | out-string) #create the HTML $html = ConvertTo-HTML @htmlParams #create the file $html | Out-File -filepath $Path -encoding ASCII Write-Verbose "Report created at $path" Write-Verbose "Ending $($myinvocation.mycommand)" } #end } #end Out-Report function #optional: define an alias Set-Alias -Name ohr -Value Out-HTMLReport

The function is intended to be used at the end of a PowerShell expression is the same way would you use Out-File or Out-Printer. If you look through the function, you’ll see that is a wrapper to Convertto-HTML and uses many of the same parameters. Here’s how you can use it. First, I need some new data.

​
I have 100 event logs from the System log, and I want to create an HTML report grouped on the Source property. I'll use this hashtable of parameters to splat to my function.
​
This makes it easy to create the report.


If you don't want to reference an external CSS file you can include style in the head section. First, some new data.
​
Next, define a head section with style.
​


I'll use these parameters and create the report:


You can use my function even if you don't want to group output. I hope you've seen what you can accomplish with PowerShell and that it isn't especially complicated, especially if you read full help and examples for commands like Format-Table and ConvertTo-HTML.

Sponsored Content

Read the Best Personal and Business Tech without Ads

Staying updated on what is happening in the technology sector is important to your career and your personal life but ads can make reading news, distracting. With Thurrott Premium, you can enjoy the best coverage in tech without the annoying ads.

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 Advanced Microsoft 365 Day!

GET-IT: Advanced Microsoft 365 1-Day Virtual Conference - Live August 24th!

Join us on Tuesday, August 24th and hear from Microsoft MVPs and industry experts about how to take advantage of Microsoft 365 at a technical level and dive deep into the features and functionality that will make your environment more secure and compliant.

RSVP Now

Sponsored By