VMware Cloud Community
lmhealthcare
Contributor
Contributor

Script to Snapshot VMs for updates

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    

            }

      }

}

0 Kudos
1 Reply
admin
Immortal
Immortal

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

0 Kudos