Skip navigation

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.

#requires -Version 2
#$DebugPreference = 'Continue'
 
function Get-OSInfo {
.SYNOPSIS
Gets information about the operating system
.DESCRIPTION
Get-OSInfo retrieves operating system information including
BIOS serial number, service pack version, and so on.
.PARAMETER computername
Specifies one or more computer names to query. Accepts
pipeline input.
.PARAMETER logfile
The path and filename of a file to log failed computers into.
.EXAMPLE
Get-OSInfo -computername localhost
.EXAMPLE
Get-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 {
.SYNOPSIS
Restarts, logs off, shutsdown, or powers off one or
more Windows servers.
.DESCRIPTION
Uses the WMI class Win32_OperatingSystem, which has a
Win32Shutdown() method.
.PARAMETER computername
The computer name(s) to target. Accepts pipeline input.
.PARAMETER method
LogOff, Shutdown, PowerOff, Reboot. The operation is not
forced, which means an application can cancel the operation.
.EXAMPLE
Reboot-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-Windows
Export-ModuleMember -function Get-OSInfo
Export-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.
Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish