Automated VMware View Agent Upgrade Using PowerShell

vmware_view_ios_icon_by_flakshack-d5opmf6Recently I needed to upgrade a number of VMware View Desktop Agents in my lab environment. My lab had been running VMware View 4.6 up until a couple of weeks ago when I finally made the jump to 5.2 — although I left the View Agents running at 4.6 because I ran out of time during the upgrade. Last week I finally decided (during my vacation of all times?) that I wanted to spend some time exploring the new Persona Management feature in View.

So why did I bother to upgrade my View Agents from 4.6?
The View desktop agent sits in the virtual machine and communicates back to the View Connection Servers it’s state, network information etc. In addition to being the eyes and ears of the View Connection Servers, the View Agent also is the piece that installs all of the major VDI drivers like PCoIP, Persona Management components etc. Since 4.6 didn’t have Persona Management I needed to upgrade the View Agents to something post Persona Management — in this case 5.2.

I assumed there would be some kind of ‘right-click and go…’ procedure for doing this from the View management interface (just like we have with VMware Tools in vCenter), but it appears that someone forgot that the entire world doesn’t run on Linked-Clones and that recomposing all your desktops every time you want to make a single software update just isn’t possible…

PowerShellI spent some time searching the internet for a quick solution to my dilemma. I wanted to get these View Agents upgraded quickly, I didn’t want to recompose the desktop pool and I certainly didn’t want to login to each desktop and do the upgrade manually.

After spending a couple of hours searching I decided there was no ‘silver bullet‘. Eventually I ran across a blog posting by resident genius Clint Kitson. He lays out pretty simply the process of  “installing” (but not necessarily upgrading) a View Agent remotely via PowerShell (PowerCLI)– wow great right? At least it was a step in the right direction.

Admittedly I am no PowerShell programmer — in fact, prior to this blog post I had never touched PowerShell.. I deal mainly with web oriented languages (and a bit of Java, C and C++ when I have to). So using Clint’s snippets as a foundation I spent a little time writing a quick script that met my purposes.

My goal was to automatically upgrade the View Agent (4.6 -> 5.2) on about 30 virtual machines running Windows XP in what is now a View 5.2 environment sitting on vSphere 4.1…

Code:

DISCLAIMER: You use the provided code at your own risk. I would not use this in a production environment in its current state. I myself ran in to issues along the way (detailed below).

I zipped up the below files ( HERE ) so you can download them quickly.

I built a quick PowerShell script (upgrade_agent.ps1):

param ( 

	[string]$vmSearchString = $(throw "VM REQUIRED"),
	[string]$viServer = "server.domain.com",
	[string]$viUsername = "some.username",
	[string]$viPassword = "password",
	[string]$desktopUsername="DOMAIN\some.username",
	[string]$desktopPassword="password",
	[string]$sourceAgentPath="\\server\share\filename.exe", #I.E. \\fileserver\public\agent-5.2-123456.exe
	[string]$destinationAgentDir="c:\",
	[string]$agentLocalOptions="ALL",
	[string]$rebootDelay="120" #should be greater than 60 and higher if you have slow VM's or are going to process many upgrades at once
)

add-pssnapin VMware.VimAutomation.Core

connect-viserver -server $viServer -user $viUsername -password $viPassword

Function upgradeAgent($vmname){

	$fileName=$global:sourceAgentPath.split('\')[-1]
	$destinationFilePath="$($global:destinationAgentDir)" #$($fileName)
	$remoteExeFullPath = "$($global:destinationAgentDir)$($fileName)"
	$sourceAgentPath = $global:sourceAgentPath
	$desktopUsername = $global:desktopUsername
	$desktopPassword = $global:desktopPassword
	$agentLocalOptions = $global:agentLocalOptions
	$rebootDelay = $global:rebootDelay

	Write-Host "Updating $vmname..."

	Write-Host "Copying Files..."
	Write-Host "VM Name: $vmname"
	Write-Host "Source Agent Path: $sourceAgentPath"
	Write-Host "Destination Path: $destinationFilePath"
	Write-Host "Guest Desktop User: $desktopUsername"
	Write-Host "Guest Desktop Password: ($($desktopPassword.length) Characters)"
	Write-Host "Trying transfer..." -NoNewLine

	try {
	    get-item $sourceAgentPath | copy-vmguestfile -destination $destinationFilePath -vm $vmname -guestuser $desktopUsername -guestpassword $desktopPassword -localtoguest
	} catch {
	    Write-Host "Failed."
	    throw $error
	    return false;
	}

	Write-Host "Done." 

	Write-Host "Making sure VMtools is running..." -NoNewLine

	Do{
	    $status = Get-vm $vmname | %{ $_.guest |%{ $_.extensiondata.toolsrunningstatus } }
	} until ($status –eq  "guestToolsRunning")

	Write-Host "Done." 

	#We need to make sure to really supress the reboot to give the USB driver time to install and initialize after the agent completes its installation
	$script='"'+$remoteExeFullPath+'" /s /v "/qn REBOOT=ReallySuppress ADDLOCAL='+$agentLocalOptions+'"'
	$script2="shutdown -r -t $rebootDelay"

	Write-Host "Executing Agent Upgrade..." -NoNewLine
	try {
		Invoke-VMScript -ScriptText $script –VM $vmname -guestuser $desktopUsername -guestpassword $desktopPassword -ScriptType Bat
	} 
	catch {
	    Write-Host "Failed." 
	    throw $error[0]
	    return false;

	}

	Write-Host "Done." 

	Write-Host "Making sure VMtools is running again..." -NoNewLine

	Do{
	    $status = Get-vm $vmname | %{ $_.guest |%{ $_.extensiondata.toolsrunningstatus } }
	} until ($status –eq  "guestToolsRunning")

	Write-Host "Done." 

	Write-Host "Executing Reboot (Delay: $rebootDelay seconds)" -NoNewLine
	try {
		Invoke-VMScript -ScriptText $script2 –VM $vmname -guestuser $desktopUsername -guestpassword $desktopPassword -ScriptType Bat
	} 
	catch {
	    Write-Host "Failed." 
	    throw $error[0]
	     return false
	}

	Write-Host "Done." 

	Write-Host "Finished upgrading Agent on $vmname."

}

$vms = get-VM -name $vmSearchString

if($vms -is [system.array] -and $vms.Count -gt 0){

	Write-Host "$($vms.Count) VMs found with the search string $vmSearchString we will try to upgrade them."
	foreach($vmObject in $vms){

		upgradeAgent $vmObject.Name

	}

}
elseif($vms.Name){

	Write-Host "1 VM found with search string $vmSearchString."
	upgradeAgent $vms.Name

}
else{

	Write-Host "NO VMs FOUND WITH SEARCH STRING $vmSearchString."

}

Wrapped the above script with a batch script to help with pushing simultaneous jobs to the background and to make sure we were using the x86 version of PowerShell:

(upgrade_agent.bat)

@echo off
set WORKING_DIR=%~dp0
cd %WORKING_DIR%
@start cmd /c ""%SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe" -file "%WORKING_DIR%upgrade_agent.ps1" %1 && pause"

Running The Script:

The PowerShell requires the VMware PowerCLI snap-ins — I installed PowerCLI on my vCenter server so I run the scripts there, but anywhere you have registered the snap-ins or have installed VMware PowerCLI should work.

Keep in mind: This script actually transfers the EXE you specify to the target VM so make sure the machine you are running the script from is close to your View desktops.

Once you have the files extracted, you can modify the PARAM area (top of file) in upgrade_agent.ps1 with your  parameters.

There are really two ways to run the script. You can either target a single machine or target a group of machines using a VM search string.

Keep in mind: When you use a search string it processes the upgrades for the found VMs one at a time (can be very slow — but there is a solution — keep reading).

Single machine (example):

Z:\agent_upgrade.bat WINXP-A-1

A group of machines (example):

Z:\agent_upgrade.bat WINXP-A-*

In my case:

Processing 30 vm’s one at a time was WAY too slow for my liking. The reason I put the batch file together in the first place was so that I could launch a number of groups all at once (since the batch file forks the PowerShell call in to a seperate window).  I ran three commands one after the other:

Z:\agent_upgrade.bat WINXP-A-0*
Z:\agent_upgrade.bat WINXP-A-1*
Z:\agent_upgrade.bat WINXP-A-2*

The above allowed me to have three desktops updated at a time and processed all 30 of my desktops.

Notes / Issues:

Now don’t get me wrong, this is probably the ugliest bit of code I have ever written, but hey — it did get the job done. That being said, I did run in to a couple of issues I’ll share to hopefully save you some cycles:

  • Sometimes the actual upgrade of the View Agent on the VM failed (returned error code). There are lots of reasons why MSI based install packages can fail. At the end of the day every issue I ran in to was resolved by restarting the VM manually (from vCenter) and retrying the script again (usually limiting the retry to the single VM that had the problem).
  • The script does not clean up after itself. I did this on purposes for my own environment. Adding a file delete command on success of install should be pretty simple if you just copy the logic provided.
  • You can specify different components to install using the “ADDLOCAL=” property. You just need to set the $agentLocalOptions variable with the options you want. Reference to the options can be found (here).
  • I didn’t specify a lot of other install options like VDM_VC_MANAGED_AGENT or VDM_SERVER_NAME. You can look at the $script variable in the PowerShell script and modify it to your hearts content. For me, I didn’t need the extra options since I was processing an upgrade of an View Agent vs. a fresh install.
  • The View Management Interface can report an unassigned user is logged in to the Desktop (and will even show your username/password as the newly assigned user to the desktop). — DON’T PANIC — wait for the desktop to restart and when it comes back online the View management interface should show everything correctly.

 

Tagged with: , , , , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

*