1 2 Previous Next 26 Replies Latest reply on Dec 12, 2018 1:40 PM by emcclure

    Trying to get a script to update VM Tools on selected VMs

    emcclure Enthusiast

      I'm trying to get write a script that will allow someone to select a VM folder and then it will scan the VM's in that folder to see if they need an upgrade or not.  Then a window would come up with a list of the VM's that need to be upgraded.  The user could then select the ones to upgrade and tools would be upgraded (w/o reboot).  I've tried a few different things, even with a csv file, but it seems whenever I kind of get it to work it only takes one VM and upgrades it.  I'm looking at the csv file and I see multiple machines in there.  Oh I'm also trying to ignore powered off machines.  Here's what I have for my code:

       

      $vms = Get-Folder $myfolder | Get-VM

      foreach ($vm in $vms) {

       

      if ($vm.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld") {

       

      $selectedvms = Get-VM -Name $vm.Name

       

          $selectedvms | select Name | Export-CSV -Path .\toolsneedupgrade.csv -NoTypeInformation -UseCulture -Append

        Write-Host "VM-->",$vm.Name, "needs to have tools upgraded"

        }

      }

       

      Import-Csv -Path .\toolsneedupgrade.csv -UseCulture | Foreach-Object -Process { Get-VM -Name $_.Name

          #Get-VM -Name $vm | #Out-GridView -Title 'Select the VMs you wish to upgrade tools on' -OutPutMode Multiple

      }

       

      foreach ($vm in $selectedvms) {

      if ($vm.Guest.State -eq "Running")

      {

      Write-Host "VM -->",$vm.Name, "is powered on so tools will be updated"

      Get-VM -Name $vm.Name | Update-Tools -NoReboot

      }

      elseif ($vm.Powerstate -eq "PoweredOff")

      {

      Write-Host "VM-->",$vm.Name, "is powered off, so ignoring"

      }

       

      And when I run it everything seems ok until I select the folder.  It then outputs this:

       

      VM--> VM1 needs to have tools upgraded
      VM--> VM2 needs to have tools upgraded

      Name                 PowerState Num CPUs MemoryGB
      ----                 ---------- -------- --------
      VM1             PoweredOff 4        24.000
      VM2           PoweredOn  2        8.000
      VM3             PoweredOn  2        8.000
      VM1             PoweredOff 4        24.000
      VM2             PoweredOn  2        8.000
      VM --> VM2 is powered on so tools will be updated
      Update-Tools : 12/6/2018 3:40:27 PM     Update-Tools            Operation is not valid due to the current state of the object.
      At C:\Users\emcclure\Desktop\GenScripts\GenUpgradeVMTools.ps1:67 char:25
      + Get-VM -Name $vm.Name | Update-Tools -NoReboot
      +                         ~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo          : NotSpecified: (:) [Update-Tools], VimException
          + FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.UpdateVmTools

       

      It does wind up updating the VM tools on the machine, but doesn't get to the part where it sees the VM is powered off and moves on in the script.  I'm sure it's something simple I'm missing, but I'm not sure what.

        • 1. Re: Trying to get a script to update VM Tools on selected VMs
          LucD Guru
          vExpertUser ModeratorsCommunity Warriors

          In the first ForEach loop you are overwriting the content of the $selectedVM variable for each VM.

          After the ForEach loop yoru CSV will contain all the candidate VMs (due to the Append switch), but the $selectedVM variable will only contain the last selected VM.

           

          You could do something like this

           

          $vms = Get-Folder 'vm' | Get-VM

           

          $selectedVM = Get-Folder -Name $myFolder | Get-VM |

              where{$_.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld"}

           

          $selectedVM | Select -Property Name |

              Export-CSV -Path .\toolsneedupgrade.csv -NoTypeInformation -UseCulture -Append

           

          Import-Csv -Path .\toolsneedupgrade.csv -UseCulture | Foreach-Object -Process {

              Get-VM -Name $_.Name

              #Get-VM -Name $vm |

              #Out-GridView -Title 'Select the VMs you wish to upgrade tools on' -OutPutMode Multiple

          }

           

          foreach ($vm in $selectedvms) {

              if ($vm.Guest.State -eq "Running")

              {

                  Write-Host "VM -->",$vm.Name, "is powered on so tools will be updated"

                  Get-VM -Name $vm.Name | Update-Tools -NoReboot

              }

              elseif ($vm.Powerstate -eq "PoweredOff")

              {

                  Write-Host "VM-->",$vm.Name, "is powered off, so ignoring"

              }

          }

           

          • 2. Re: Trying to get a script to update VM Tools on selected VMs
            emcclure Enthusiast

            Hi LucD,

             

            So I made those changes, but no improvement.  In fact it's not even trying to update the machines.  I removed the $vms = Get-Folder line as it seems like it's redundant for the line below it.  I also changed the foreach ($vm in $selectedvms) since there's no $selectedvms anywhere and it still doesn't work.  What I'd really like is to create a pop up using the Out-GridView to see the list of machines so the user can update whichever ones they want.  Below is what I currently have for the code:

             

            $selectedVM = Get-Folder -Name $myfolder | Get-VM | where {$_.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld"}

             

                $selectedVM | select -Property Name | Export-CSV -Path .\toolsneedupgrade.csv -NoTypeInformation -UseCulture -Append

             

            Import-Csv -Path .\toolsneedupgrade.csv -UseCulture | Foreach-Object -Process { Get-VM -Name $_.Name

              }

             

            foreach ($vm in $selectedvms) {

            if ($vm.Guest.State -eq "Running")

            {

            Write-Host "VM -->",$vm.Name, "is powered on so tools will be updated"

            Get-VM -Name $vm.Name | Update-Tools -NoReboot

            }

            elseif ($vm.Powerstate -eq "PoweredOff")

            {

            Write-Host "VM-->",$vm.Name, "is powered off, so ignoring"

            }

             

            The way it is now I can select a folder and it will list me the VM's, what their name, powerstate, number of cpu's and memory are and that's it.  Doesn't go past that to try and do anything and I can verify in vCenter that there are a couple VM's that need to be upgraded with tools.

            • 3. Re: Trying to get a script to update VM Tools on selected VMs
              LucD Guru
              vExpertUser ModeratorsCommunity Warriors

              Ok, try this version.
              The $selectedVM now contains the VMs that the user selected via Out-GridView

               

              I left out the Export-Csv and Import-Csv since it really does not play any role in the script's logic.

              If you want an external file to record which VMs are handled, you can add the Export-Csv again.

               

              $myfolder = 'MyFolder'

               

              $selectedVM = Get-Folder -Name $myfolder | Get-VM |

                  where {$_.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld"} |

                  Out-GridView -OutputMode Multiple

               

              foreach ($vm in $selectedVM) {

                  if ($vm.Guest.State -eq "Running")

                  {

                      Write-Host "VM --> $($vm.Name) is powered on so tools will be updated"

                      Update-Tools -VM $vm -NoReboot

                  }

                  elseif ($vm.Powerstate -eq "PoweredOff")

                  {

                      Write-Host "VM--> $($vm.Name) is powered off, so ignoring"

                  }

              }

               

              • 4. Re: Trying to get a script to update VM Tools on selected VMs
                emcclure Enthusiast

                Hi LucD,

                 

                So now with those changes it will only update one VM.  It will show that a VM is powered on and needs to be updated, but that's it.  It doesn't go thru the whole list of VM's I select and it doesn't show anything in regards to the VM's that are powered off.  When I had the csv file it would show a list of the VM's that were on or off, so I'm not sure what's going on here.  Do I need something else in the if statement part?

                • 5. Re: Trying to get a script to update VM Tools on selected VMs
                  LucD Guru
                  vExpertCommunity WarriorsUser Moderators

                  I added some debugging statements in the script.

                  Can you try with this version?

                   

                  $myfolder = 'MyFolder'

                   

                  $selectedVM = Get-Folder -Name $myfolder | Get-VM |

                      where {$_.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld"} |

                      Out-GridView -OutputMode Multiple

                   

                  Write-Host "Selected VM: $($selectedVM.Name -join '|')"

                   

                  foreach ($vm in $selectedVM) {

                      Write-Host "Looking at $($vm.Name)"

                      Write-Host "`tGuest state $($vm.Guest.State)"

                      Write-Host "`tPower state $($vm.PowerState)"

                   

                      if ($vm.Guest.State -eq "Running")

                      {

                          Write-Host "VM --> $($vm.Name) is powered on so tools will be updated"

                          Update-Tools -VM $vm -NoReboot

                      }

                      elseif ($vm.Powerstate -eq "PoweredOff")

                      {

                          Write-Host "VM--> $($vm.Name) is powered off, so ignoring"

                      }

                  }

                   

                  • 6. Re: Trying to get a script to update VM Tools on selected VMs
                    emcclure Enthusiast

                    Hi LucD,

                     

                    With the code below I get this:

                     

                    Selected VM: VMToolUpg1|VMToolUpg2|VMToolUpg3|VMToolUpg4|VMToolUpg5

                    Looking at VMToolUpg1

                            Guest state Running

                            Power state PoweredOn

                    VM --> VMToolUpg1 is powered on so tools will be updated

                     

                    Then it takes me to the loop at the end of the script.  All 5 of those selected need to be upgraded.  2 of them are not powered on and I would expect the script to tell me that so it ignores them, but it never gets there and never starts on a 2nd VM.

                    • 7. Re: Trying to get a script to update VM Tools on selected VMs
                      LucD Guru
                      User ModeratorsCommunity WarriorsvExpert

                      Are you running this from a .ps1 file?

                      If yes, can you attach the script.

                      If not, how are you running the script?
                      Can you share a screenshot?

                       

                       

                       

                      Some extra debug lines

                       

                      $myfolder = 'MyFolder'

                       

                      $selectedVM = Get-Folder -Name $myfolder | Get-VM |

                          where {$_.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld"} |

                          Out-GridView -OutputMode Multiple

                       

                      Write-Host "Selected VM count : $($selectedVM.Count)"

                      Write-Host "Selected VM: $($selectedVM.Name -join '|')"

                       

                      Write-Host "Before loop - selected VM count : $($selectedVM.Count)"

                      foreach ($vm in $selectedVM) {

                          Write-Host "Inside loop - selected VM count : $($selectedVM.Count)"

                          Write-Host "Looking at $($vm.Name)"

                          Write-Host "`tGuest state $($vm.Guest.State)"

                          Write-Host "`tPower state $($vm.PowerState)"

                       

                          if ($vm.Guest.State -eq "Running")

                          {

                              Write-Host "VM --> $($vm.Name) is powered on so tools will be updated"

                              Update-Tools -VM $vm -NoReboot

                              Write-Host "After Update-Tools"

                              Write-Host "`tVM $($vm.Name)"

                          }

                          elseif ($vm.Powerstate -eq "PoweredOff")

                          {

                              Write-Host "VM--> $($vm.Name) is powered off, so ignoring"

                              Write-Host "VM powered off"

                              Write-Host "`tVM $($vm.Name)"

                          }

                      }

                       

                      • 8. Re: Trying to get a script to update VM Tools on selected VMs
                        emcclure Enthusiast

                        Yes running it as a .ps1 file.  I just ran it with the changes you had and it didn't even attempt to update the VM's.  Just gave me this:

                         

                        Selected VM count : 5

                        Selected VM: VMToolUpg4|VMToolUpg1|VMToolUpg5|VMToolUpg2|VMToolUpg3

                        Before loop - selected VM count : 5

                        Inside loop - selected VM count : 5

                        Looking at VMToolUpg4

                                Guest state NotRunning

                                Power state PoweredOff

                        VM--> VMToolUpg4 is powered off, so ignoring

                        VM powered off

                                VM VMToolUpg4

                         

                        File is attached with the most recent changes.

                        • 9. Re: Trying to get a script to update VM Tools on selected VMs
                          LucD Guru
                          vExpertUser ModeratorsCommunity Warriors

                          The problem is in your handling of the Continue or Exit block.

                          • you are executing $commands again in line 62 when $result equals 0. Since you are already in a ForEach loop, you in fact don't have to do anything on Continue
                          • instead of exit it is better to use break in a ForEach loop

                           

                          This simple example should demonstrate (just runs 4 times through the loop and asks the Continue/Exit question)

                           

                          1..4 | %{

                              Write-Host "Loop $_"

                              $continue = New-Object System.Management.Automation.Host.ChoiceDescription '&continue', 'Continue getting tools info?'

                              $exit = New-Object System.Management.Automation.Host.ChoiceDescription '&exit', 'Exit the script'

                             

                              $options = [System.Management.Automation.Host.ChoiceDescription[]]($continue, $exit)

                             

                              $title = 'Continue or exit script'

                              $message = 'Do you want to continue or exit the script? Exiting script will disconnect you from all hosts/vCenters'

                              $result = $host.ui.PromptForChoice($title, $message, $options, 0)

                              if ($result -ne "0"){

                                  break

                              }

                          }

                          • 10. Re: Trying to get a script to update VM Tools on selected VMs
                            emcclure Enthusiast

                            Hi LucD,

                             

                            So I've had the loop like that for a while in my scripts and afaik it's never been a problem for me with the main part of any script.  I made the changes as shown below and when it gets to the part of choosing continue or exit it ends the script once I choose continue.

                            • 11. Re: Trying to get a script to update VM Tools on selected VMs
                              emcclure Enthusiast

                              Ok so for the moment I have removed the loop just to figure things out.  I made a couple changes to the code which are below:

                               

                              $dc = Get-Datacenter

                              if ($dc.Count -gt 1) {

                                Write-Host "I found $($dc.Count) datacenters"
                                Write-Host "Choose a datacenter"
                                  1..($dc.Count) | %{
                                 Write-Host "$_ - $($dc[$_ - 1].Name)"
                                 }
                                 $answer = 0
                                 while (1..($dc.Count) -notcontains $answer){
                                 $answer = Read-Host -Prompt "Select a datacenter (1-$($dc.Count))"
                                 }

                              }

                              Get-Datacenter -Name $dc[$answer - 1].Name | Get-Folder -Type "VM" | sort Name | Format-Table
                              $myfolder = Read-Host "Select a folder"

                              $selectedVM = Get-Folder -Name $myfolder | Get-VM | where {$_.ExtensionData.Guest.ToolsVersionStatus2 -eq "guestToolsSupportedOld"} | Out-GridView -Title 'Select VMs you wish to scan for tools updates' -OutputMode Multiple

                              $myvms = Get-VM -Name $selectedVM | Select -Property Name | sort Name

                              $selectedVMs = Get-VM -Name $myvms.Name | sort Name

                              foreach ($vm in $selectedVMs) {

                              if ($vm.Name -and ($vm.Guest.State -eq "Running"))
                              {
                              Write-Host "VM -->",$vm.Name, "is powered on so tools will be updated"
                              Get-VM -Name $vm.Name | Update-Tools -NoReboot

                              }
                              elseif ($vm.Powerstate -eq "PoweredOff")
                              {
                              Write-Host "VM-->",$vm.Name, "is powered off, so ignoring"

                              }

                               

                              So now everything is working ok to a certain degree.  It will update, or at least attempt to update whatever VM's I click.  For example I have this output:

                               

                              VM --> VMToolUpg4 is powered on so tools will be updated

                              VM --> VMToolUpg5 is powered on so tools will be updated

                              VM --> VMToolUpg6 is powered on so tools will be updated

                              VM --> VMToolUpg7 is powered on so tools will be updated

                              VM--> VMToolUpg8 is powered off, so ignoring

                              VM--> VMToolUpg9 is powered off, so ignoring

                               

                              Which looks great.  However it only really updates VMToolUpg4.  It takes the longest, but when I look in vSphere I see that the tools show as upgraded, even though I didn't reboot the machine.  When I look at all the others in vSphere it does the install/mount/upgrade of the tools rather quickly.  Even if I reboot them they still show as not being upgraded.

                               

                              I added the $vm.Name in the if section as I noticed it didn't want to get multiple VM's for whatever reason.  No matter how many I selected it would only try to update one, but once I added that in it started to at least attempt to update them all.

                               

                              So now I'm wondering why it's only updating the first one and not the rest.  I'm also wondering why the loop is breaking things.  I have this in a lot of other scripts and it hasn't caused a problem anywhere else.

                              • 12. Re: Trying to get a script to update VM Tools on selected VMs
                                emcclure Enthusiast

                                I may have found what was blocking the ones from updating, but need to do a little more research.  Will update this thread later.

                                • 13. Re: Trying to get a script to update VM Tools on selected VMs
                                  LucD Guru
                                  vExpertCommunity WarriorsUser Moderators

                                  Do you really call $commands inside $commands in the other scripts?

                                  That is definitely bound to break things

                                  • 14. Re: Trying to get a script to update VM Tools on selected VMs
                                    emcclure Enthusiast

                                    So I happened to just run the Update-Tools command against a VM and got the vix error code 21009.  I ran another script that I have that does this:

                                     

                                    Get-VM -Name $VMname.Name | New-AdvancedSetting -Name "isolation.tools.guestInitiatedUpgrade.disable" -value "FALSE" -Force -Confirm:$false #Adds an entry to the vmx file to allow VM tools to upgrade

                                     

                                    And I was able to update VM tools on those machines.  So that fixes that problem.

                                    1 2 Previous Next