The Surly Admin

Father, husband, IT Pro, cancer survivor

Set-ShutdownComputers – Without Confirmation

A while back I wrote a script that would restart a computer, but require a very firm confirmation from the administrator.  Two levels of confirmation, in fact and it’s been a fairly popular post on the blog too.  Recently someone asked for a similar script to work off of a set of computers, and considering the work I’ve been doing recently with the pipeline I thought this was an ideal time to revisit that script and add some functionality.  And remove the confirmations because seriously, what could go wrong?

Background

It all started with this forum thread over at Spiceworks, and there are a ton of great answers already in there but I wanted to do it the Powershell way and I wanted to really utilize the power of the pipeline and Powershell’s ability to accept input from multiple sources.  But before I do, I want to make sure I give credit where it’s due!  This script is heavily modified from an original by Fredrik Wall and you can find the original here:  Shutdown-Computer.

For my script, all the magic begins with the PARAM section:

Param (
[CmdletBinding()]
   [Parameter (Mandatory=$true,
      ValueFromPipeline=$true,
      ValueFromPipelineByPropertyName=$true)]
   [Alias("Computers")]
   [String[]]$Name,

   [Parameter (Mandatory=$true)]
   [Alias("ShutdownType")]
   [ValidateSet ("LogOff","Shutdown","Reboot","ForcedLogOff","ForcedShutdown","ForcedReboot","PowerOff","ForcedPowerOff")]
   [string]$Type
)

Only two parameters here, but a ton of parameter settings.  The first parameter has several settings:  Mandatory and two pipeline values which I’ve talked about here.  I also added an alias of -Computers in case you forget to use -Name–which is an odd parameter name if you want to enter a bunch of computer names.  Then when designating $Name itself, I’ve also set it as an array, which will allow the script to accept array input such as:

-ComputerNames "computer1","computer2","computer3"

One other nice thing about using [string[]] is that if you don’t enter any value for $Name at all, Powershell will prompt you for a name and will actually accept multiple lines.  So you enter a computer name, it will prompt you for another one and so on until you enter a blank value.  Nice!

Next comes $Type.  This parameter does not accept input from the pipeline but I do use ValidateSet to enforce all of the possible values that $Type can have.  Powershell will not let you run the script until you correctly enter the right value for $Type.  Notice when shutting down a computer we have a lot of options.

Begin {
   Switch ($Type)
   {  "LogOff" { $ShutdownType = "0" }
      "Shutdown" { $ShutdownType = "1" }
      "Reboot" { $ShutdownType = "2" }
      "ForcedLogOff" { $ShutdownType = "4" }
      "ForcedShutdown" { $ShutdownType = "5" }
      "ForcedReboot" { $ShutdownType = "6" }
      "PowerOff" { $ShutdownType = "8" }
      "ForcedPowerOff" { $ShutdownType = "12" }
   }
   $Results = $()
}

As with any Pipeline script we need to use the Begin/Process/End blocks.  Since $Type is made to be human friendly, we’re going to need to convert those values to numbers that our WMI class (see below) will accept.  Pretty easily done with a Switch { } block.  We also initialize our $Results array where we’ll return the feedback from our attempted shutdowns.

Process {
   Write-Verbose "Attempting to shutdown $Name..."
   Try {
      (Get-WmiObject Win32_OperatingSystem -ComputerName $Name -ErrorAction Stop).Win32Shutdown($ShutdownType)
      Write-Verbose "Shutdown of $Name successful"
      $Result = "Successful"
   }
   Catch {
      Write-Verbose "Shutdown of $Name Failed!`n"
      $Result = "Failed"
   }
   $Results += New-Object PSObject -Property @{
      'Computer Name' = $Name
      'Shutdown Type' = $Type
      Result = $Result
   }
}

Now we move on to the Process block where we attach to the computer using WMI and attempt the shutdown.  I use a Try/Catch block here to detect if the WMI call fails.  After we’re done I load the feedback we got into a PSObject for later reporting.  We use dot sourcing on the Get-WMIObject cmdlet by grouping it together with parenthesis and then accessing the methods available with that object, in this case Win32Shutdown and then pass on our $ShutdownType variable that we determined back in the Begin block.  You can also access properties with this technique if you only need to get one property but don’t do this if you need multiple properties.  Load the results into a variable, otherwise you’re making a remote WMI call for every property which is pretty inefficient.

End {
   #Return the results object
   $Results
}

And here’s the wrapup.  We’ve created our $Results object with all the information about what happened in it and now we simply display it.  This is a standard Powershell way of returning data and this object is fully compatible with every object processing cmdlet out there.  You can use Select, Where (although why you would use Where I can’t imagine!), Out-File, ConvertTo-HTML, Export-CSV or your cmdlet/function of choice.

Examples

Here are a few examples of what you can do:

.\Set-ShutdownComputers.ps1

This will simply cause the script to prompt you for the computers you want to work on, and the action you want to take on them.

.\Set-ShutdownComputers.ps1 -Name "computer1","computer2" -Type Shutdown

Will shutdown computer1 and computer2.

.\Set-ShutdownComputers.ps1 -Name (Get-Content servers.txt) -Type Reboot

Will reboot every computer in servers.txt.

Get-Content servers.txt | .\Set-ShutdownComputers.ps1 -Type Reboot

Will do the same thing.

Get-ADComputer -Filter { Name -like "WS*" } | .\Set-ShutdownComputers.ps1 -Type Logoff | Export-CSV logoffresultslog.csv

Will query Active Directory for every computer name that starts with WS, log them off and save the results to a CSV.

As you can see, there are just a ton of different options you can do with pipeline and your scripts, and by returning objects in your scripts–instead of just text–you then open the script up to all the other things Powershell can do (reporting, selection, etc).

 

Source code can be found here.

February 7, 2013 - Posted by | PowerShell | , ,

No comments yet.

Leave a comment