PowerCLI Update VMware Tools without a reboot.

UPDATE:  This Functionality has been completely replaced with the Update-Tools cmdlet!


 UPDATE: 2009-7-31Recently several individuals have contacted me to report complications using my update-tools script. These issues center around my use, and dependency on WMI, and what appears to be vSphere 4 growing pains. Hopefully, the PowerCLI team can get this functionality built into the VUM cmdlets! Until then some assembly required!


There appears to be a bug in the VMwareToolsUpgrader.exe that will cause the system to reboot no matter what. This behavior is especially bothersome because the VI API simply calls that executable to perform any non-interactive upgrade.

This morning I was updating a smaller farm to 3.5u4. 15 minuets into the tools upgrade process I had enough… Enter PowerShell, I whipped up a quick script that will update All Windows VM’s to the latest version of VMware Tools.

$starttime = get-date
#requires -version 2
$GuestCred = Get-Credential
$OldTools = Get-View -ViewType "VirtualMachine" `
    -Property Guest,name `
    -filter @{
        "Guest.GuestFamily"="windowsGuest";
        "Guest.ToolsStatus"="ToolsOld";
        "Guest.GuestState"="running"
    }
 
Foreach ($VM in $OldTools) {
    # Mount the tools CD
    $VM.MountToolsInstaller()
 
    # Get the drive letter for the CD Drive tools is mounted in
    $DrvLetter = Get-WmiObject Win32_CDROMDrive -ComputerName $VM.name -Credential $GuestCred |
        Where-Object {$_.VolumeName -match "VMware Tools"} |
        Select-Object -ExpandProperty Drive
 
    # our update cmd
    $cmd = "$($DrvLetter)setup.exe /S /v`"/qn REBOOT=ReallySuppress REINSTALLMODE=vomus REINSTALL=ALL`""
 
    # Create a new process on the VM, and execute setup
    $go = Invoke-WMIMethod -path win32_process `
        -Name Create `
        -Credential $GuestCred `
        -ComputerName $VM.Name `
        -ArgumentList $cmd
 
    # If we sucessfully spawned an upgrade wait for it to finish
    IF ($go.ReturnValue -eq 0)
    {
        $i=0
        While ($VM.Guest.ToolsStatus -ne 'toolsOk')
        {
            Start-Sleep -Seconds 1
            $VM.UpdateViewData("Guest")
            if ($I -gt 120)
            {
                Write-Warning "$($VM.name) appears to have hung, please investigate!"
                break
            }
            $i++
        }
        $vm.UnmountToolsInstaller()
        Write-verbose "$($VM.Name) Successfully updated"
    }
    else
    {
        Write-Warning "error $(go.ReturnValue) triggering tools install on $($VM.name). "
        $vm.UnmountToolsInstaller()
    }
}
$processed = $oldTools.count - (Get-View -ViewType "VirtualMachine" `
    -Property Guest,name `
    -filter @{
        "Guest.GuestFamily"="windowsGuest";
        "Guest.ToolsStatus"="ToolsOld";
        "Guest.GuestState"="running"
    }).count
$ts = New-TimeSpan -Start $starttime
write-host ("{0} out of {1} vm's were updated in {2}.{3} Min" -f $processed, $oldTools.count, $ts.Minutes,$ts.Seconds)

Using this script I updated 50 Vm’s in 40min. There is alot of room for improvement here. I opted for the slow and steady pace, but larger environments could easily tune this to 100 at a time.

~Glenn

P.S. I started with Invoke-VMScript but only half of the vm’s had PowerShell installed, and I needed something that would hit them all.