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.
3 comments
Hide comments