Gathering the Information

I’m a big fan of modular programming. Big, big fan. With that in mind, I tend to write functions that gather the information I want to be in my report - and I’ll usually do one function per major section of my report. You’ll see in a bit how that’s beneficial. By writing each function individually, I make it easier to use that same information in other tasks, and I make it easier to debug each one. The trick is to have each function output a single type of object that combines all of the information for that report section. I’ve created five functions, which I’ve pasted into a single script file. I’ll give you each of those functions one at a time, with a brief commentary for each. Here’s the first:

  1. function Get-InfoOS {
  2. [CmdletBinding()]
  3. param(
  4. [Parameter(Mandatory=$True)][string]$ComputerName
  5. )
  6. $os = Get-WmiObject -class Win32_OperatingSystem -ComputerName $ComputerName
  7. $props = @{'OSVersion'=$os.version;
  8. 'SPVersion'=$os.servicepackmajorversion;
  9. 'OSBuild'=$os.buildnumber}
  10. New-Object -TypeName PSObject -Property $props
  11. }

This is a straightforward function, and the main reason I bothered to even make it a function - as opposed to just using Get-WmiObject directly - is that I want different property names, like “OSVersion” instead of just “Version.” That said, I tend to follow this exact same programming pattern for all info-retrieval functions, just to keep them consistent.

  1. function Get-InfoCompSystem {
  2. [CmdletBinding()]
  3. param(
  4. [Parameter(Mandatory=$True)][string]$ComputerName
  5. )
  6. $cs = Get-WmiObject -class Win32_ComputerSystem -ComputerName $ComputerName
  7. $props = @{'Model'=$cs.model;
  8. 'Manufacturer'=$cs.manufacturer;
  9. 'RAM (GB)'="{0:N2}" -f ($cs.totalphysicalmemory / 1GB);
  10. 'Sockets'=$cs.numberofprocessors;
  11. 'Cores'=$cs.numberoflogicalprocessors}
  12. New-Object -TypeName PSObject -Property $props
  13. }

Very similar to the last one. You’ll notice here that I’m using the -f formatting operator with the RAM property, so that I get a value in gigabytes with 2 decimal places. The native value is in bytes, which isn’t useful for me.

  1. function Get-InfoBadService {
  2. [CmdletBinding()]
  3. param(
  4. [Parameter(Mandatory=$True)][string]$ComputerName
  5. )
  6. $svcs = Get-WmiObject -class Win32_Service -ComputerName $ComputerName `
  7. -Filter "StartMode='Auto' AND State<>'Running'"
  8. foreach ($svc in $svcs) {
  9. $props = @{'ServiceName'=$svc.name;
  10. 'LogonAccount'=$svc.startname;
  11. 'DisplayName'=$svc.displayname}
  12. New-Object -TypeName PSObject -Property $props
  13. }
  14. }

Here, I’ve had to recognize that I’ll be getting back more than one object from WMI, so I have to enumerate through them using a ForEach construct. Again, I’m primarily just renaming properties. I absolutely could have done that with a Select-Object command, but I like to keep the overall function structure similar to my other functions. Just a personal preference that helps me include fewer bugs, since I’m used to doing things this way.

  1. function Get-InfoProc {
  2. [CmdletBinding()]
  3. param(
  4. [Parameter(Mandatory=$True)][string]$ComputerName
  5. )
  6. $procs = Get-WmiObject -class Win32_Process -ComputerName $ComputerName
  7. foreach ($proc in $procs) {
  8. $props = @{'ProcName'=$proc.name;
  9. 'Executable'=$proc.ExecutablePath}
  10. New-Object -TypeName PSObject -Property $props
  11. }
  12. }

Very similar to the function for services. You can probably start to see how using this same structure makes a certain amount of copy-and-paste pretty effective when I create a new function.

  1. function Get-InfoNIC {
  2. [CmdletBinding()]
  3. param(
  4. [Parameter(Mandatory=$True)][string]$ComputerName
  5. )
  6. $nics = Get-WmiObject -class Win32_NetworkAdapter -ComputerName $ComputerName `
  7. -Filter "PhysicalAdapter=True"
  8. foreach ($nic in $nics) {
  9. $props = @{'NICName'=$nic.servicename;
  10. 'Speed'=$nic.speed / 1MB -as [int];
  11. 'Manufacturer'=$nic.manufacturer;
  12. 'MACAddress'=$nic.macaddress}
  13. New-Object -TypeName PSObject -Property $props
  14. }
  15. }

The main thing of note here is how I’ve converted the speed property, which is natively in bytes, to megabytes. Because I don’t care about decimal places here (I want a whole number), casting the value as an integer, by using the -as operator, is easier for me than the -f formatting operator. Also, it gives me a chance to show you this technique!

Note that, for the purposes of this book, I’m going to be putting these functions into the same script file as the rest of my code, which actually generates the HTML. I don’t normally do that. Normally, info-retrieval functions go into a script module, and I then write my HTML-generation script to load that module. Having the functions in a module makes them easier to use elsewhere, if I want to. I’m skipping the module this time just to keep things simpler for this demonstration. If you want to learn more about script modules, pick up Learn PowerShell Toolmaking in a Month of Lunches or PowerShell in Depth, both of which are available from Manning.com.