I was recently trying to shutdown my guests in order to perform maintenance. I wanted to do it without touching each machine. I found two scripts that did exactly what I needed. Here they are.
The first script disconnects removable drives in order to run the suspend/shutdown/resume script
"removableDrives.ps1 Version 1.0a starting"
connect-viserver ($env:computername + "." + $env:userdnsdomain) | out-null
Get-Vm | Where-Object {
(((Get-CDDrive -VM $_ | Where-Object { (($_.ConnectionState.Connected -eq $True) -or ($_.ConnectionState.StartConnected -eq $True))} ) -ne $Null) `
-or `
((Get-FloppyDrive -VM $_ | Where-Object { (($_.ConnectionState.Connected -eq $True) -or ($_.ConnectionState.StartConnected -eq $True))} ) -ne $Null))
} | %{
"VM: " + $_.Name + " (" + $_.PowerState + ")"
Get-CDDrive -VM $_ | ForEach-Object { " " + $_.Name + " Connected=" + $_.ConnectionState.Connected + " ConnectAtPowerUp=" + $_.ConnectionState.StartConnected }
Get-FloppyDrive -VM $_ | ForEach-Object { " " + $_.Name + " Connected=" + $_.ConnectionState.Connected + " ConnectAtPowerUp=" + $_.ConnectionState.StartConnected }
}
if ("Y" -eq (Read-Host `
("Do you want to disconnect all removable drives, and disable the connect `n" + `
" at startup? If you don't see any VMs everything is OK and you should `n" + `
" select N here. Type Y or N"))) {
Get-Vm | Where-Object {
(((Get-CDDrive -VM $_ | Where-Object { (($_.ConnectionState.Connected -eq $True) -or ($_.ConnectionState.StartConnected -eq $True))} ) -ne $Null) `
-or `
((Get-FloppyDrive -VM $_ | Where-Object { (($_.ConnectionState.Connected -eq $True) -or ($_.ConnectionState.StartConnected -eq $True))} ) -ne $Null))
} | %{
"VM: " + $_.Name + " (" + $_.PowerState + ")"
Get-CDDrive -VM $_ | ForEach-Object { Set-CDDRIVE -CD $_ -StartConnected $False -Connected $False -Confirm:$False | %{ " Changed " + $_.Name} }
Get-FloppyDrive -VM $_ | ForEach-Object { Set-FloppyDRIVE -Floppy $_ -StartConnected $False -Connected $False -Confirm:$False | %{ " Changed " + $_.Name} }
}
}
"removableDrives.ps1 finished."
This script actually performs the suspend/shutdown/resume operation. Simpy change the verbs where necessary.
#Connect-VIServer MyVIServer
# Get All the ESX Hosts
$ESXSRV = Get-VMHost
# For each of the VMs on the ESX hosts
Foreach ($VM in ($ESXSRV | Get-VM)){
# Shutdown the guest cleanly
$VM | Suspend-VMGuest -Confirm:$false
}
# Set the amount of time to wait before assuming the remaining powered on guests are stuck
$waittime = 200 #Seconds
$Time = (Get-Date).TimeofDay
do {
# Wait for the VMs to be Shutdown cleanly
sleep 1.0
$timeleft = $waittime - ($Newtime.seconds)
$numvms = ($ESXSRV | Get-VM | Where { $_.PowerState -eq "poweredOn" }).Count
Write "Waiting for suspension of $numvms VMs or until $timeleft seconds"
$Newtime = (Get-Date).TimeofDay - $Time
} until ((@($ESXSRV | Get-VM | Where { $_.PowerState -eq "poweredOn" }).Count) -eq 0 -or ($Newtime).Seconds -ge $waittime)
# Shutdown the ESX Hosts
#'$ESXSRV | Foreach {Get-View $_.ID} | Foreach {$_.ShutdownHost_Task($TRUE)}
Write-Host "Suspension Complete"
These were very useful to me.