Looks like we just passed mid-point in Scripting Games 2013, at least for fun part: writing scripts. I must say that I really couldn’t find anything that I’ve seen as “learning experience” in this one – we already did more complex WMI reporting in previous event. We were requested to create very basic HTML report, and we have cmdlet for that. So… I guess I just went lazy with this one. No real error checking, because design claims all have been taken care of. Sweet.
Beginner
I decided to run single command, with some code nested. It looks much better in newest ISE than it looks on my blog, so first “external” piece: part that will generate elements that do not depend on the data from targeted server:
ConvertTo-Html -Title 'Drive Free Space Report' -Body @" <h2>Local Fixed Disk Report</h2> $( 'Code to generate report table' ) <hr> $(Get-Date) "@ | Set-Content Report.html
The actual code that will run within sub-expression (and will convert data from server to HTML table) is using the same cmdlet, with –Fragment switch this time around:
Get-CimInstance -ComputerName $env:COMPUTERNAME -ClassName Win32_LogicalDisk -Filter 'DriveType = 3' | Select @{ Name = 'Drive' Expression = { $_.DeviceId } }, @{ Name = 'Size (GB)' Expression = { "{0:N2}" -f ($_.Size / 1GB) } }, @{ Name = 'FreeSpace (MB)' Expression = { "{0:N2}" -f ($_.FreeSpace / 1MB) } } | ConvertTo-Html -Fragment
And that’s it. I’ve added Invoke-Item to look at my results, but that’s not really part of the solution. So you won’t find it here…
Advanced
For advanced category I decided to do two things: make reporting part a separate function (in case I would one day decide that csv is better option than ugly html ), and output reference to files created (e.g. if I want to move them, or copy them, or attach them to mail, or invoke to see them in browser). And because I’m using CIM – I written it in v3 style. Obviously, I’ve included #requires directive there…
To make code easier to understand I’ve used trick that I learned from mjolinor: changing order of begin, process and end block…:
function New-DiskReport { [OutputType('System.IO.FileInfo')] param ( ) process { foreach ($Computer in $ComputerName) { $ReportPath = "$Path\$Computer.html" $Table = Get-DiskInventory -ComputerName $Computer | ConvertTo-Html -Fragment | Out-String $Report -f $Computer, $Table, (Get-Date) | Out-File $ReportPath Get-Item $ReportPath } } begin { <# Define: -- Get-DiskInventory function -- $Report 'template' #> } }
I don’t use end block at all, and begin is used for initialization: define internal function, and herestring that will work as HTML template. This way function logic goes first, and configuration pieces are down-there, almost like it would be some compiled language.
As you can see I’m vocal about type that will go out, and I do what I promised: I output FileInfo objects for each report generated. I won’t go into details how Get-DiskInventory function looks like, it’s basically code for beginners with pipeline support and parameters defined. Template is also similar, but with “bonus” name of computer included:
$Report = ConvertTo-Html -Title $Title -Body @' <h2>Local Fixed Disk Report: {0}</h2> {1} <hr> {2} '@
With that template created in begin block all I have to do for each computer inventoried is to pass correct set of parameters to –f operator:
$Report -f $Computer, $Table, (Get-Date)
Finally, once I complete reporting, I can do something with results. E.g. if I want to open all reports generated in the browser:
echo DC, CoreDC, CoreFile | New-DiskReport | Invoke-Item
And as it was the case previously, OutputType will help me work with objects I got. I won’t have to remember names of properties – they will be handed to me by PowerShell based on type I declared. As always: feel free to tell me what I did wrong. And now – I can start looking at your entries, to find out what went wrong this time on my own…
Pingback: Event 3: My way… | PowerShell.org
Nice notes…
I love that I keep getting the same ideas that you use {“This should pass the files out to the pipeline”}. I’ve gotten one comment so far on my event 3, which applauded that idea.
But it also kind of sucks… since you just do it so much more proffessionally. 😉
So all comments from now on are just going to be “Bartek did it better.”
Thanks for the pingback!.
I did the html rather differently, using an expandable here-string with the all the html code in it and variables embedded. It seems more intiutive to read in the code having all the html in one place, rather than having to jump back and forth between different parts of the code mentally replacing format numbers with variables and then variables with strings to know eactly what’s being written to those files.