A Peek into a PowerShell Class (and a Script Module / Advanced Function example)

Wrapping up PowerShell class, I always like to conclude with a big finish. This week, it's a script module that contains two "advanced functions" (or, if you prefer, "script cmdlets"). You'll see examples of debug trace code, error handling, support for the -confirm and -whatif switches, comment-based help, "private" functions within a script module, and so on. Both Get-OSInfo and Reboot-Windows are functional (and the use of "Reboot" as a verb was deliberate, to demonstrate PowerShell's warning message about nonstandard verbs when you import the module). Drop this into \Documents\WindowsPowerShell\Modules\Tools\Tools.psm1, and then run Import-Module tools to give them a try.

Don Jones

October 28, 2010

9 Min Read
ITPro Today logo in a gray background | ITPro Today

Wrapping up PowerShell class, I always like to conclude with a big finish. This week, it's a script module that contains two "advanced functions" (or, if you prefer, "script cmdlets"). You'll see examples of debug trace code, error handling, support for the -confirm and -whatif switches, comment-based help, "private" functions within a script module, and so on. Both Get-OSInfo and Reboot-Windows are functional (and the use of "Reboot" as a verb was deliberate, to demonstrate PowerShell's warning message about nonstandard verbs when you import the module). Drop this into DocumentsWindowsPowerShellModulesToolsTools.psm1, and then run Import-Module tools to give them a try.

#requires -Version 2#$DebugPreference = 'Continue' function Get-OSInfo {.SYNOPSISGets information about the operating system.DESCRIPTIONGet-OSInfo retrieves operating system information includingBIOS serial number, service pack version, and so on..PARAMETER computernameSpecifies one or more computer names to query. Acceptspipeline input..PARAMETER logfileThe path and filename of a file to log failed computers into..EXAMPLEGet-OSInfo -computername localhost.EXAMPLEGet-Content names.txt | Get-OSInfo#>    [CmdletBinding()]    param (        [Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,        ValueFromPipelineByPropertyName=$True)]        [Alias('host')]        [string[]]$computername,                 [Parameter(Mandatory=$True)]        [string]$logfile    )    BEGIN {        Write-Verbose "Inside Get-OSWorker BEGIN block"        $inputFromPipeline = $true        if ($psBoundParameters.containskey('computername')) {            $inputFromPipeline = $false        }        del $logfile -ea silentlycontinue    }    PROCESS {        if ($inputFromPipeline) {            OSWorker -computername $computername -logfile $logfile        } else {            foreach ($name in $computername) {                OSWorker -comp $name -log $logfile            }        }    }    END {}} function OSWorker {    param (        [string]$computername,        [string]$logfile    )    Write-Debug "Inside OSWorker"    Write-Debug "`$computername is $computername"    Write-Debug "`$logfile is $logfile"    $continue = $True    try {        Write-Debug "Attempting WMI call"        $os = Get-WmiObject -class Win32_OperatingSystem -ea Stop -comp $computername    } catch {        $continue = $false        Write-Debug "WMI call failed"        $computer | out-file $logfile -append     }    if ($continue) {        Write-Debug "Attempting 2nd WMI call"        $bios = Get-WmiObject -class Win32_BIOS -comp $computername        $obj = New-Object -TypeName PSObject        $obj | Add-Member -MemberType NoteProperty -Name ComputerName -value ($computername)        $obj | Add-Member -MemberType NoteProperty -Name OSBuild -value ($os.buildnumber)        $obj | Add-Member -MemberType NoteProperty -Name OSDescription -value ($os.caption)        $obj | Add-Member -MemberType NoteProperty -Name SPVersion -value ($os.servicepackmajorversion)        $obj | Add-Member -MemberType NoteProperty -Name BIOSSerial -value ($bios.serialnumber)        Write-Output $obj    } else {        Write-Debug "Not attempting 2nd WMI call"    }} function Reboot-Windows {.SYNOPSISRestarts, logs off, shutsdown, or powers off one ormore Windows servers..DESCRIPTIONUses the WMI class Win32_OperatingSystem, which has aWin32Shutdown() method..PARAMETER computernameThe computer name(s) to target. Accepts pipeline input..PARAMETER methodLogOff, Shutdown, PowerOff, Reboot. The operation is notforced, which means an application can cancel the operation..EXAMPLEReboot-Windows -computername SERVERDC1 -method PowerOff#>    [CmdletBinding(SupportsShouldProcess=$True,ConfirmImpact='High')]    param (        [Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,        ValueFromPipelineByPropertyName=$True)]        [Alias('host')]        [string[]]$computername,                 [Parameter(Mandatory=$True)]        [ValidateSet('LogOff','Shutdown','Reboot','PowerOff')]        [string]$method    )    BEGIN {        $inputFromPipeline = $true        if ($psBoundParameters.containskey('computername')) {            $inputFromPipeline = $false        }        Switch ($method) {            'LogOff' { $param = 0 }            'Shutdown' { $param = 1 }            'Reboot' { $param = 2 }            'PowerOff' { $param = 8 }        }     }    PROCESS {        if ($inputFromPipeline) {            if ($pscmdlet.ShouldProcess($computername)) {                RebootWorker -computername $computername $param            }        } else {            foreach ($name in $computername) {                if ($pscmdlet.ShouldProcess($computername)) {                    RebootWorker $name $param                }            }        }    }    END {}    } function RebootWorker {    param($computername,$param)    Get-WmiObject -class Win32_OperatingSystem -comp $computername |    Invoke-WmiMethod -name Win32Shutdown -arg $param} New-Alias goi Get-OSInfo Export-ModuleMember -function Reboot-WindowsExport-ModuleMember -function Get-OSInfoExport-ModuleMember -alias goi
You're welcome to extend and add to these functions, or even change them completely. As in-class examples, they're obviously exhibiting a lot of different things. For example, we discussed the fact that well-written (and consistently used) Write-Debug statements could sometimes serve in lieu of inline comments, as well as serving as trace code, which is why you don't see any inline comments in these examples. However, in the course of class, we didn't get around to adding Write-Debug everywhere.

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like