Summary: In today’s blog, we continue our excerpt from the upcoming book by Don Jones, Richard Siddaway, and Jeffrey Hicks.
Microsoft Scripting Guy, Ed Wilson, is here. Yesterday, we posted PowerShell in Depth: Part 1, an excerpt provided by Candace Gillhoolley from Manning Publications. The book, PowerShell in Depth, is written by Don Jones, Richard Siddaway, and Jeffery Hicks. Today we have the conclusion to that blog.
There’s definitely a trick to creating reports with Windows PowerShell. Windows PowerShell isn’t at its best when it’s forced to work with text; objects are where it excels. This blog, based on Chapter 33 from PowerShell in Depth, focuses on a technique that can produce a nicely formatted HTML report, suitable for emailing to a boss or colleague.
Assembling the final HTML page
Assembling the final page simply involves adding our two existing fragments, although we are also going to embed a style sheet. Using cascading style sheet (CSS) language is a bit beyond the scope of this blog, but this example will give you a basic idea of what it can do. This embedded style sheet lets us control the formatting of the HTML page, so that it looks a little nicer. If you would like a good tutorial and reference to CSS, check out this CSS Tutorial on the w3schools.com site.
$head = @'
<style>
body { background-color:#dddddd;
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 }
table { margin-left:50px; }
</style>
'@
ConvertTo-HTML -head $head -PostContent $frag1,$frag2 `
-PreContent "<h1>Hardware Inventory for SERVER2</h1>"
We have put that style sheet into the $head variable by using a here string to type out the entire CSS syntax that we wanted. That gets passed to the Head parameter, our HTML fragments to the PostContent parameter, and we couldn’t resist adding a header for the whole page, where we’ve again hardcoded a computer name (SERVER2).
We saved the entire script as C:\Good.ps1, and ran it like this:
./good > Report.htm
That directs the output HTML to Report.htm. This HTML report consists of multiple HTML fragments, which is incredibly beautiful as shown here:
Okay, maybe it is no work of art, but it is highly functional, and frankly, it looks better than the on-screen-only report we started with in yesterday’s blog. List 2 shows the completed script, where we’ve swapped out the hard-coded computer name for a script-wide parameter that defaults to the local host. Notice too that we’ve included the [CmdletBinding()] declaration at the top of the script, which enables the Verbose parameter. We have used Write-Verbose to document what each step of the script is doing.
List 2: An HTML inventory report script
<#
.DESCRIPTION
Retrieves inventory information and produces HTML
.EXAMPLE
./Good > Report.htm
.PARAMETER
The name of a computer to query. The default is the local computer.
#>
[CmdletBinding()]
param([string]$computername=$env:computername)
# function to get computer system info
function Get-CSInfo {
param($computername)
$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername
$cs = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername
$bios = Get-WmiObject -Class Win32_BIOS -ComputerName $computername
$props = @{'ComputerName'=$computername
'OS Version'=$os.version
'OS Build'=$os.buildnumber
'Service Pack'=$os.sevicepackmajorversion
'RAM'=$cs.totalphysicalmemory
'Processors'=$cs.numberofprocessors
'BIOS Serial'=$bios.serialnumber}
$obj = New-Object -TypeName PSObject -Property $props
Write-Output $obj
}
Write-Verbose 'Producing computer system info fragment'
$frag1 = Get-CSInfo -computername $computername |
ConvertTo-Html -As LIST -Fragment -PreContent '<h2>Computer Info</h2>' |
Out-String
Write-Verbose 'Producing disk info fragment'
$frag2 = Get-WmiObject -Class Win32_LogicalDisk -Filter 'DriveType=3' `
-ComputerName $computername |
Select-Object @{name='Drive';expression={$_.DeviceID}},
@{name='Size(GB)';expression={$_.Size / 1GB -as [int]}},
@{name='FreeSpace(GB)';expression={$_.freespace / 1GB -as [int]}} |
ConvertTo-Html -Fragment -PreContent '<h2>Disk Info</h2>' |
Out-String
Write-Verbose 'Defining CSS'
$head = @'
<style>
body { background-color:#dddddd;
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 }
table { margin-left:50px; }
</style>
'@
Write-Verbose 'Producing final HTML'
Write-Verbose 'Pipe this output to a file to save it'
ConvertTo-HTML -head $head -PostContent $frag1,$frag2 `
-PreContent "<h1>Hardware Inventory for $ComputerName</h1>"
Now that’s a script you can build upon! Using the script is very easy.
PS C:\> $computer = SERVER01
PS C:\> C:\Scripts\good.ps1 -computername $computer |
>> Out-File "$computer.html"
>>
PS C:\> Invoke-Item "$computer.html"
The script runs, produces an output file for future reference, and displays the report. Keep in mind that our work to build the Get-CSInfo function is reusable. Because that function outputs an object, not only pure text, you could repurpose it in a variety of places where you might need the same information.
To add to this report, you would:
- Write a command or function that generates a single kind of object, which contains all the information you need for a new report section.
- Use that object to produce an HTML fragment, and store it in a variable.
- Add that new variable to the list of variables in the script’s last command, thus adding the new HTML fragment to the final report.
- Sit back and relax.
Yes, this report is text. Ultimately, every report will be because text is what humans read. The point of this one is that everything stays as Windows PowerShell-friendly objects until the last possible instance. We let Windows PowerShell, rather than our own fingers, format everything for us. The actual working bits of this script, which retrieve the information we need, could easily be copied and pasted and used elsewhere for other purposes. That was not as easy to do with our original pure-text report because the actual working code was embedded with all of that formatted text.
Building reports is certainly a common need for administrators, and Windows PowerShell is well suited to the task. The trick, we feel, is to produce reports in a way that makes the reports’ functional code (the bits that retrieve information and so forth) somewhat distinct from the formatting- and the output-creation code. In fact, Windows PowerShell is generally capable of delivering great formatting with very little work on your part, as long as you work it the way it needs you to.
~Don, Richard, and Jeffery
Thank you Candace, Don, Richard, and Jeffery.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy