shutdown-button-mdOver that past few months I've been getting quite a few emails and comments as it pertains to the scripts I have shared to shutdown and power on VMs in the event of power loss.  I assume since the storm weather is kicking in people are realizing that they need to have some kind of protection in regards to gracefully getting the VMs down.  Now I've had the (privilege?) of watching my script run multiple times in a production environment and it has gotten to the point where it is working flawlessly.  One caveat to the script though – whichever Windows box is executing the PowerShell must stay powered on longer than your vCenter Server.

That being said the script would be pretty useless if you had a virtualized vCenter Server running on the same cluster as your production VMs that your are shutting down – this is what all of the emails and comments that I've been getting have been based around.  So, without further ado I've added a few updates to the script to better support those with a virtualized vCenter.  A few explanations below…

So first off (Line 50) I've added a variable called $vCenterVMName and assigned this a default value of NA.  This variable should hold the VM Name of your vCenter Server.  I've tried to keep just one version of the script supporting both virtualized and non-virtualized vCenter's thus is why the default to "NA".  So if you don't have a virtualized vCenter just leave the variable set to NA and the script will run as it always had, otherwise if you do, put its' VM name in here.

Secondly down on Lines 107 to 1118 I do a few things.  First, check to see if you have changed that default value and if you have we need to figure out which ESXi host vCenter is running on in order to shut it down last.  This is what's done here.

Then, in each of the shutdown loops (Line 125 and Line 162) I've added an if statement.  Basically it just checks to see if the VM it is processing is the vCenter Server.  If so, it doesn't process the shutdown.

Lastly I've done a couple more things.  On Line 186 I've added a check while shutting down the hosts.  If the host that is being processed is the host that vCenter is running on I want to leave it till the end, so it is skipped during this loop.  Then, the finally the block of code starting on Line 196, if needed, will send the shutdown command to the host that is running vCenter Server.

As you may of noticed I've added a some logging as well, nothing bit, just a record of what happened in a txt file rather than relying on Write-Host.  The script in its' entirety is below or you can download it here.  Watch for updates to the power on script to support the vritualized vCenter as well.  Just not sure how I'm going to tackle that one yet 🙂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#################################################################################
# Power off VMs (poweroffvms.ps1)
#
# This is a complete update to my previous shutdown script.  No longer do we
# loop through the esx hosts and shut down VMs per host.  We now just shut them
# all down up front.  Also, no more $vmstoleaveon variable.  I'm assuming here 
# that your vCenter is not virtual.  If it is you can most likely grab version
# 1 of this script and take the code to leave the vCenter host till last.  Maybe
# someday I'll get around to updating it to merge the two...but for now, this is
# it!
#
# This script does have a 'partner' script that powers the VMs back on, you can
# grab that script at https://blog.mwpreston.net/shares/
#
# Created By: Mike Preston, 2012 - With a whole lot of help from Eric Wright 
#                                  (@discoposse)
#
# Variables:  $mysecret - a secret word to actually make the script run, stops 
#                         the script from running when double click DISASTER
#             $vcenter - The IP/DNS of your vCenter Server
#             $username/password - credentials for your vCenter Server
#             $filename - path to csv file to store powered on vms
used for the poweronvms.ps1 script.
#             $cluster - Name of specific cluster to target within vCenter
#             $datacenter - Name of specific datacenter to target within vCenter
#
#
# Usage: ./poweroffvms.ps1 "keyword"
#        Intended to be ran in the command section of the APC Powerchute Network
#        Shutdown program before the shutdown sequence has started.
#
#################################################################################
 
param($keyword)
 
Add-PSSnapin VMware.VimAutomation.Core
#some variables
$vcenter = "vcenterServer"
$username = "user"
$password = "password"
$cluster = "clusterName"
$datacenter = "datacenterName"
$filename = "c:\path\to\poweredonvms.csv"
$mysecret = "kerfuffle"
$date = ( get-date ).ToString('yyyyMMdd')
$logfile = New-Item -type file "C:\ShutdownLog-$date.txt"
 
#for use with a virtualized vCenter - bring it down last :) - if you don't need this functionality
#simply leave the variable set to NA and the script will work as it always had
$vCenterVMName = "NA"
 
Write-Host "A Powerchute network shutdown command has been sent to the vCenter Server." -Foregroundcolor yellow
Write-Host "This script will shutdown all of the VMs and hosts located in $datacenter" -Foregroundcolor yellow
Write-Host "Upon completion, this server ($vcenter) will also be shutdown gracefully" -Foregroundcolor yellow
Write-Host ""
Write-Host "This script has also has a counterpart (powervmsbackon.ps1) which should" -Foregroundcolor yellow
Write-Host "be setup to run during the startup of this machine ($vcenter). " -Foregroundcolor yellow
Write-Host "This will ensure that your VMs are powered back on once power is restored!" -Foregroundcolor yellow
Sleep 5
 
Add-Content $logfile "mwpreston.net PowerOff Script Engaged"
Add-Content $logfile ""
 
if ($keyword -ne $mysecret) 
{ 
Write-Host "You haven't passed the proper detonation sequence...ABORTING THE SCRIPT" -ForegroundColor red
Add-Content $logfile "You haven't passed the proper detonation sequence...ABORTING THE SCRIPT"
exit
}
 
#connect to vcenter
Write-Host "Connecting to vCenter - $vcenter ...." -nonewline
Add-Content $logfile "Connecting to vCenter - $vcenter"
$success = Connect-VIServer $vcenter -username $username -Password $password
if ($success) { Write-Host "Connected!" -Foregroundcolor Green }
else
{
Write-Host "Something is wrong, Aborting script" -Foregroundcolor Red
Add-Content $logfile "Something is wrong, Aborting script"
exit
}
Write-Host ""
Add-Content $logfile  ""
 
#Get a list of all powered on VMs - used for powering back on....
Get-VM -Location $cluster | where-object {$_.PowerState -eq "PoweredOn" } | Select Name | Export-CSV $filename
 
#change DRS Automation level to partially automated...
Write-Host "Changing cluster DRS Automation Level to Partially Automated" -Foregroundcolor green
Get-Cluster $cluster | Set-Cluster -DrsAutomation PartiallyAutomated -confirm:$false 
#change the HA Level
Write-Host ""
Write-Host "Disabling HA on the cluster..." -Foregroundcolor green
Write-Host ""
Add-Content $logfile "Disabling HA on the cluster..."
Add-Content $logfile ""
Get-Cluster $cluster | Set-Cluster -HAEnabled:$false -confirm:$false 
 
#get VMs again (we will do this again instead of parsing the file in case a VM was powered in the nanosecond that it took to get here.... :)
Write-Host ""
Write-Host "Retrieving a list of powered on guests...." -Foregroundcolor Green
Write-Host ""
Add-Content $logfile "Retrieving a list of powered on guests...."
Add-Content $logfile ""
$poweredonguests = Get-VM -Location $cluster | where-object {$_.PowerState -eq "PoweredOn" }
 
Add-Content $logfile "Checking to see if vCenter is virtualized"
#retrieve host info for vCenter
if ($vcenterVMName -ne "NA") 
{ 
Add-Content $logfile "vCenter is indeed virtualized, getting ESXi host hosting vCenter Server"
$vCenterHost = (Get-VM $vCenterVMName).Host.Name 
Add-Content $logfile "$vCenterVMName currently running on $vCenterHost - will process this last"
}
else
{
Add-Content $logfile "vCenter is not virtualized"
}
 
Add-Content $logfile "Proceeding with VM PowerOff"
Add-Content $logfile ""
#and now, let's start powering off some guests....
ForEach ( $guest in $poweredonguests ) 
{
if ($guest.Name -ne $vCenterVMName)
{ 
Write-Host "Processing $guest ...." -ForegroundColor Green
Write-Host "Checking for VMware tools install" -Foregroundcolor Green
$guestinfo = get-view -Id $guest.ID
if ($guestinfo.config.Tools.ToolsVersion -eq 0)
{
Write-Host "No VMware tools detected in $guest , hard power this one" -ForegroundColor Yellow
Add-Content $logfile "$guest.Name - no VMware tools, hard power off"
Stop-VM $guest -confirm:$false
}
else
{
write-host "VMware tools detected.  I will attempt to gracefully shutdown $guest"
Add-Content $logfile "$guest.Name - VMware tools installed, gracefull shutdown"
$vmshutdown = $guest | shutdown-VMGuest -Confirm:$false
}
}   
}
 
#Lets wait a minute or so for shutdowns to complete
Write-Host ""
Write-Host "Giving VMs 2 minutes before resulting in hard poweroff"
Write-Host ""
Add-Content $logfile ""
Add-Content $logfile "Waiting a couple minutes then hard powering off all remaining VMs"
Add-Content $logfile ""
Sleep 120
 
 
#Now, let's go back through again to see if anything is still powered on and shut it down if it is
Write-Host "Beginning Phase 2 - anything left on....night night..." -ForegroundColor red
Write-Host ""
#get our list of guests still powered on...
$poweredonguests = Get-VM -Location $cluster | where-object {$_.PowerState -eq "PoweredOn" }
ForEach ( $guest in $poweredonguests ) 
{
if ($guest.Name -ne $vCenterVMName)
{ 
Write-Host "Processing $guest ...." -ForegroundColor Green
#no checking for toosl, we just need to blast it down...
write-host "Shutting down $guest - I don't care, it just needs to be off..." -ForegroundColor Yellow
Add-Content $logfile "$guest.Name - Hard Power Off"
Stop-VM $guest -confirm:$false
}
}
 
#wait 30 seconds
Write-Host "Waiting 30 seconds and then proceding with host power off"
Write-Host ""
Add-Content $logfile ""
Add-Content $logfile "Processing power off of all hosts now"
Sleep 30
 
#and now its time to slam down the hosts - I've chosen to go by datacenter here but you could put the cluster
#  There are some standalone hosts in the datacenter that I would also like to shutdown, those vms are set to
# start and stop with the host, so i can just shut those hosts down and they will take care of the vm shutdown ;)
 
$esxhosts = Get-VMHost -Location $cluster
foreach ($esxhost in $esxhosts)
{
if ($esxhost.Name -ne $vCenterHost)
{
#Shutem all down
Write-Host "Shutting down $esxhost" -ForegroundColor Green
Add-Content $logfile "Shutting down $esxhost"
$esxhost | Foreach {Get-View $_.ID} | Foreach {$_.ShutdownHost_Task($TRUE)}
}
}
 
#finally shut down the host which vCenter resides on if using that functionality
if ($vcenterVMName -ne "NA") 
{
Add-Content $logfile ""
Add-Content $logfile "Processing $vcenterHost - shutting down " 
Get-VMhost $vCenterHost | Foreach {Get-View $_.ID} | Foreach {$_.ShutdownHost_Task($TRUE)} 
}
 
Add-Content $logfile ""
Add-Content $logfile "All done!"
#that's a wrap

So there you have it!  Hopefully this will help some of you out with your shutdown endeavors.  Feel free to make any changes you need in order to make it work for your environment and let me know how it goes – keep those comments, concerns, thoughts and emails coming in!