I wrote this script to solve a problem we have doing monthly updates.
It seems if we start too many snapshots all at once, the management agents lose contact with vCenter.
This script will take snapshots one at a time. I have a small environment, so I just run it simultaneously for each host in a cluster.
It targets only powered on Windows VMs. If it can't quiesce the vm, it will try to shut it down gently, it will then check periodically for ten minutes for the machine to stop before it gives up and Powers it off. If it isn't running tools it should shut down immediately. It will then take the snap, and turn the VM back on.
Let me know what you think, or if you have any improvements I'd love to hear.
Write-Host "Enter vCenter host name: "
$vcserver = Read-Host
connect-viserver $vcserver
Write-Host "Enter unique part of ESXi host name: "
$vihost = Read-Host
$VMsToSnap = Get-VM | Where {($_.Host -match $vihost) -and ($_.PowerState -like "PoweredOn") -and ($_.guest.OSFullname -match "Windows")}
$VMsToSnap |ForEach-Object {
if (New-Snapshot -Name "MonthlyPatches" -Quiesce -VM $_)
{Write-Host "Snap Complete for " -NoNewLine; Write-Host $_.name}
else {
if ($_ | Shutdown-VMGuest -Confirm:$false)
{ $i=0
while ((Get-VM -Name $_.name).PowerState -notlike "PoweredOff"){
if ($i -eq 60) {
$_ | Stop-VM -Confirm:$false | Out-Null
Write-Host "Forced Power Off, " -NoNewline
Start-Sleep -Seconds 10
}
$i++
Start-Sleep -Seconds 10
}
New-Snapshot -Name "MonthlyPatches" -VM $_ | Out-Null
Write-Host "Shutdown and Snap Complete for " -NoNewLine; Write-Host $_.name
$_ | Start-VM | Out-Null
} else
{ $_ | Stop-VM -Confirm:$false | Out-Null
New-Snapshot -Name "MonthlyPatches" -VM $_ | Out-Null
Write-Host "Forced Power Off, Shutdown and Snap Complete for " -NoNewLine; Write-Host $_.name
$_ | Start-VM | Out-Null
}
}
}
Hi there,
have some code improvements suggestions:
1. Use foreach ( $vm in $VMsToSnap){ instead of $VMsToSnap |ForEach-Object {
2. In the nested if you can remove code duplication like this:
$outputString = ""
if( Shutdown-VMGuest ){
while(...){
if(){
$outputString += "Forced Power Off"
break
}
}
$outputString += "Shutdown and Snap Complete for "
} else{
Stop-VM
$outputString += "Forced Power Off, Shutdown and Snap Complete for "
$outputString += $vm.name
}
[void] (New-Snapshot -Name "MonthlyPatches" -VM $vm)
Write-Host $outputString
[void] ($vm | Start-VM)
I suggest that because you have the same pattern of "Stop" > Snapshot > Start in the inner if. Why write it 2 times? and maintain it in 2 places
3. You can break out of the while loop instead of waiting another 20 seconds when $i -eq 60 returns $true
If you don't break, you risk that when leaving the if, $i becomes 61 and if the VM doesn't ge PoweredOff (for some reason),
so this would continiue looping. My suggestion is to break out of the loop.
4. Rename $i to $numberOfAttempts
5. Extract magic numbers (like 10, 60) to constant values named $totalNumberOfTries = 60; $
I hope my comment was valuable to you.
Other than these suggestions, I don't see a problem in your script.
Is something in perticular bothering you about your script or about the behaviour of the VC ?
Best regards,
Leni Kirilov