VMware Cloud Community
emcclure
Enthusiast
Enthusiast
Jump to solution

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

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.

Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Ok, let's see how this version fulfills your requirements.

$vcName = Read-Host -Prompt 'Enter the vCenter name'

Connect-VIServer -Server $vcName

while($true){

    $endAnswer = 'D'

    while($endAnswer -ne 'Q'){

        if($endAnswer -eq 'D'){

            $dc = Get-Datacenter | Select -ExpandProperty Name

            if($dc.count -gt 1){

                $dc = $dc | Out-GridView -OutputMode Single -Title 'Select one datacenter'

            }

            $endAnswer = 'F'

        }

        if($endAnswer -eq 'F'){

            $folder = Get-Folder -Location $dc -Type VM | Select -ExpandProperty Name

            if($folder.count -gt 1){

                $folder = $folder | Out-GridView -OutputMode Multiple -Title 'Select one or more folders'

            }

            $endAnswer = 'V'

        }

        if($endAnswer -eq 'V'){

            $vms = Get-VM -Location $folder |

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

                Select -ExpandProperty Name

            if($vms.Count -eq 0){

                Write-Host "No VMs found that require a VMware Tools update"

                break

            }

            if($vms.count -gt 1){

                $vms = $vms | Out-GridView -OutputMode Multiple -Title 'Select one or more folders'

            }

        }

        if($vms.count -gt 0){

            foreach($vm in Get-VM -Name $vms){

                if($vm.PowerState -eq 'PoweredOn'){

                    if($vm.Guest.State -eq 'Running'){

                        Write-Host "VMware Tools on VM $($vm.Name) will be updated"

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

                    }

                    else{

                        Write-Host "VMware Tools are not running on VM $($vm.Name)"

                    }

                }

                else{

                    Write-Host "VM $($vm.Name) is not powered on"

                }

            }

        }

        else{

            Write-Host "No VMs selected"

        }

        write-host "Please select an option"

        Write-Host "D - Go back to the datacenter selection"

        Write-Host "F - Go back to the folder selection"

        Write-Host "V - Go back to the VM selection"

        Write-Host "Q - Exit the script"

        $endAnswer = ''

        while('D','F','V','Q' -notcontains $endAnswer){

            $endAnswer = (Read-Host -Prompt 'Your answer').ToUpper()

        }

    }

    Disconnect-VIServer -Server $vcName -Confirm:$false

    break

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
26 Replies
LucD
Leadership
Leadership
Jump to solution

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"

    }

}

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

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.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

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"

    }

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

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?

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

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"

    }

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

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.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

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)"

    }

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

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.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

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

    }

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

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.

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

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.

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

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

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

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

That is definitely bound to break things


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

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.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

So, what problems are still remaining?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Yes.  I was digging around one day to find out how to loop a script so someone wouldn't have to do run the script over and over and came across how I have it setup.  It's worked well for all of the scripts I've used it in so far without any issue, other than the one in this thread.  I basically wanted a way to loop back to a certain point in each script, whether it was the datacenter part or something else so people could just do their thing.  I'm always open to another way of doing things, so if there's a better way to loop each script and even get a loop to work right in this one then I'm game.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Ok, I can have another look.

But would you mind attaching the actual script as you are currently using?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
emcclure
Enthusiast
Enthusiast
Jump to solution

Sure.  Here it is.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Start with something like this

$dc = Get-Datacenter | Select -ExpandProperty Name

if($dc.count -gt 1){

    $dc = $dc | Out-GridView -OutputMode Multiple -Title 'Select one or more datacenters'

}

$folder = Get-Datacenter -Name $dc | Get-Folder -Type VM | select -ExpandProperty Name

if($folder.Count -gt 1){

    $folder = $folder | Out-GridView -OutputMode Multiple -Title 'Select one or more folders'

}

$vms = Get-Folder -Name $folder -Location $dc -Type VM | Get-VM |

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

    Select -ExpandProperty Name

if($vms.Count -eq 0){

    Write-Host "No VMs found"

    break

}

if($vms.Count -gt 1){

    $vms = $vms | Out-GridView -OutputMode Multiple -Title 'Select one or more VMs'

}

foreach($vm in Get-VM -Name $vms){

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

    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"

    }

   

    $answer = ''

    while('y','n' -notcontains $answer){

        $answer = Read-Host -Prompt "Continue with the next VM? (y/n)"

    }

    if($answer -eq 'n'){

        break   

    }

}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos