Script: PowerCLI: Upgrade VMs to New Tools and Hardware Level Version

Summary: Upgrade the VMWare Tools level and the hardware level from VMs
Date: Around 2013
Refactor: 22 March 2025: Checked links and formatting.

Note: If you need a simple version to just upgrade the hardware level to 9 (vSphere 5.1) look here.

This is a powershell/powercli script which upgrades VMs to the latest level of VMware Tools and to hardware level 7.
The script works in phases:

# This scripy will upgrade the VMWare Tools level and the hardware level from VMs
#
# Original Author:
# AFokkema: http://ict-freak.nl/2009/07/15/powercli-upgrading-vhardware-to-vsphere-part-2-vms/
#
# Modifications done by:
# Sjoerd Hooft: http://www.warmetal.nl/powerclivmupgrade
# Modifications:
# Improved readability: Removed blank lines when not required; Used standard white spaces for readablilty; Added variables section
# Changed the before and after hardware list: Use variables for the listname depending on timestamp; Added information for VMs with multiple nics; Removed Excel functionality so using csv allways
# Added the possibility for input from more than just a folder: 1 VM (for testing purposes); Folder; ResourcePool; File with VMs; All VMs
# Changed the power-off and power-on VM functions to check both tools and running status
# Changed the check tools state function to include all possible statuses
# Added the possibillity to exit the script when things go wrong
# Made separate functions for upgrading the tools and hardware level, and skipping linux guests
 
# Variables
#$vCenter = Read-Host "Enter your vCenter servername"
#$Folder = Read-Host "Enter the name of the folder where the VMs are stored"
$timestamp = Get-Date -format "yyyyMMdd-HH.mm"
# Note: enter the csv file without extension:
$csvfile = "C:\Users\sjoerd\Desktop\powercli scripts\$timestamp-vminfo.csv"
 
Function VM-Selection{
   $sourcetype = Read-Host "Do you want to upgrade AllVMs, a VM, Folder, ResourcePool or from a VMfile?"
   if($sourcetype -eq "AllVMs"){
      $abort = Read-Host "You've chosen $sourcetype, this is your last chance to abort by pressing <CTRL>+C. Press <ENTER> to continue selecting old hardware VMs"
      #$vms = Get-VM | Get-View | Where-Object {-not $_.config.template -and $_.Config.Version -eq "vmx-04" } | Select Name
     $vms = Get-VM | Get-View | Where-Object {-not $_.config.template} | Select Name
   }
   else{
      $sourcename = Read-Host "Give the name of the object or inputfile (full path) you want to upgrade"
      if($sourcetype -eq "VM"){
         $abort = Read-Host "You've chosen $sourcetype, this is your last chance to abort by pressing <CTRL>+C. Press <ENTER> to continue selecting old hardware VMs"
       #$vms = Get-VM $sourcename | Get-View | Where-Object {-not $_.config.template -and $_.Config.Version -eq "vmx-04" } | Select Name
       $vms = Get-VM $sourcename | Get-View | Where-Object {-not $_.config.template} | Select Name
     }
     elseif($sourcetype -eq "Folder"){
         $abort = Read-Host "You've chosen $sourcetype, this is your last chance to abort by pressing <CTRL>+C. Press <ENTER> to continue selecting old hardware VMs"
         #$vms = Get-Folder $sourcename | Get-VM  | Get-View | Where-Object {-not $_.config.template -and $_.Config.Version -eq "vmx-04" } | Select Name
       $vms = Get-Folder $sourcename | Get-VM  | Get-View | Where-Object {-not $_.config.template} | Select Name
      }
      elseif($sourcetype -eq "ResourcePool"){
         $abort = Read-Host "You've chosen $sourcetype, this is your last chance to abort by pressing <CTRL>+C. Press <ENTER> to continue selecting old hardware VMs"
         #$vms = Get-ResourcePool $sourcename | Get-VM  | Get-View | Where-Object {-not $_.config.template -and $_.Config.Version -eq "vmx-04" } | Select Name
       $vms = Get-ResourcePool $sourcename | Get-VM  | Get-View | Where-Object {-not $_.config.template} | Select Name
      }
      elseif(($sourcetype -eq "VMfile") -and ((Test-Path -path $sourcename) -eq $True)){
         $abort = Read-Host "You've chosen $sourcetype with this file: $sourcename, this is your last chance to abort by pressing <CTRL>+C. Press <ENTER> to continue selecting old hardware VMs"
         #$list = Get-Content $sourcename | Foreach-Object {Get-VM $_ | Get-View | Where-Object {-not $_.config.template -and $_.Config.Version -eq "vmx-04" } | Select Name }
         $list = Get-Content $sourcename | Foreach-Object {Get-VM $_ | Get-View | Where-Object {-not $_.config.template} | Select Name }
       $vms = $list
      }
      else{
         Write-Host "$sourcetype is not an exact match of AllVMs, VM, Folder, ResourcePool or VMfile, or the VMfile does not exist. Exit the script by pressing <CTRL>+C and try again."
      }
   }
   return $vms
}
 
Function PowerOn-VM($vm){
   Start-VM -VM $vm -Confirm:$false -RunAsync | Out-Null
   Write-Host "$vm is starting!" -ForegroundColor Yellow
   sleep 10
 
   do {
      $vmview = get-VM $vm | Get-View
     $getvm = Get-VM $vm
     $powerstate = $getvm.PowerState
      $toolsstatus = $vmview.Guest.ToolsStatus
 
      Write-Host "$vm is starting, powerstate is $powerstate and toolsstatus is $toolsstatus!" -ForegroundColor Yellow
      sleep 5
     #NOTE that if the tools in the VM get the state toolsNotRunning this loop will never end. There needs to be a timekeeper variable to make sure the loop ends
 
   }until(($powerstate -match "PoweredOn") -and (($toolsstatus -match "toolsOld") -or ($toolsstatus -match "toolsOk") -or ($toolsstatus -match "toolsNotInstalled")))
 
   if (($toolsstatus -match "toolsOk") -or ($toolsstatus -match "toolsOld")){
       $Startup = "OK"
      Write-Host "$vm is started and has ToolsStatus $toolsstatus"
   }
   else{
       $Startup = "ERROR"
      [console]::ForegroundColor = "Red"
      Read-Host "The ToolsStatus of $vm is $toolsstatus. This is unusual. Press <CTRL>+C to quit the script or press <ENTER> to continue"
      [console]::ResetColor()
   }
    return $Startup
}
 
Function PowerOff-VM($vm){
   Shutdown-VMGuest -VM $vm -Confirm:$false | Out-Null
   Write-Host "$vm is stopping!" -ForegroundColor Yellow
   sleep 10
 
   do {
      $vmview = Get-VM $vm | Get-View
      $getvm = Get-VM $vm
     $powerstate = $getvm.PowerState
     $toolsstatus = $vmview.Guest.ToolsStatus
 
      Write-Host "$vm is stopping with powerstate $powerstate and toolsStatus $toolsstatus!" -ForegroundColor Yellow
      sleep 5
 
   }until($powerstate -match "PoweredOff")
 
   if (($powerstate -match "PoweredOff") -and (($toolsstatus -match "toolsNotRunning") -or ($toolsstatus -match "toolsNotInstalled"))){
      $Shutdown = "OK"
     Write-Host "$vm is powered-off"
   }
   else{
      $Shutdown = "ERROR"
     [console]::ForegroundColor = "Red"
     Read-Host "The ToolsStatus of $vm is $toolsstatus. This is unusual. Press <CTRL>+C to quit the script or press <ENTER> to continue"
     [console]::ResetColor()
   }
   return $Shutdown
}
 
Function Check-ToolsStatus($vm){
   $vmview = get-VM $vm | Get-View
   $status = $vmview.Guest.ToolsStatus
 
   if ($status -match "toolsOld"){
      $vmTools = "Old"}
   elseif($status -match "toolsNotRunning"){
      $vmTools = "NotRunning"}
   elseif($status -match "toolsNotInstalled"){
      $vmTools = "NotInstalled"}
   elseif($status -match "toolsOK"){
      $vmTools = "OK"}
   else{
      $vmTools = "ERROR"
     Read-Host "The ToolsStatus of $vm is $vmTools. Press <CTRL>+C to quit the script or press <ENTER> to continue"
     }
   return $vmTools
}
 
Function Check-VMHardwareVersion($vm){
   $vmView = get-VM $vm | Get-View
   $vmVersion = $vmView.Config.Version
   $v4 = "vmx-04"
   $v7 = "vmx-07"
 
   if ($vmVersion -eq $v4){
      $vmHardware = "Old"}
   elseif($vmVersion -eq $v7){
      $vmHardware = "OK"}
   else{
      $vmHardware = "ERROR"
     [console]::ForegroundColor = "Red"
     Read-Host "The Hardware version of $vm is not set to $v4 or $v7. This is unusual. Press <CTRL>+C to quit the script or press <ENTER> to continue"
     [console]::ResetColor()
   }
   return $vmHardware
}
 
Function Upgrade-VMHardware($vm){
   $vmview = Get-VM $vm | Get-View
   $vmVersion = $vmView.Config.Version
   $v4 = "vmx-04"
   $v7 = "vmx-07"
 
   if ($vmVersion -eq $v4){
      Write-Host "Version 4 detected" -ForegroundColor Red
 
# Update Hardware
      Write-Host "Upgrading Hardware on" $vm -ForegroundColor Yellow
      Get-View ($vmView.UpgradeVM_Task($v7)) | Out-Null
   }
}
 
Function CreateHWList($vms, $csvfile){
# The setup for this hwlist comes from http://www.warmetal.nl/powerclicsvvminfo
   Write-Host "Creating a CSV File with VM info" -ForegroundColor Yellow
 
   $MyCol = @()
      ForEach ($item in $vms){
         $vm = $item.Name
       # Variable getvm is required, for some reason the $vm cannot be used to query the host and the IP-address
       $getvm = Get-VM $VM
       $vmview = Get-VM $VM | Get-View
 
       # VM has to be turned on to make sure all information can be recorded
       $powerstate = $getvm.PowerState
       if ($powerstate -ne "PoweredOn"){
          PowerOn-VM $vm}
 
       $vmnic = Get-NetworkAdapter -VM $VM
         $nicmac = Get-NetworkAdapter -VM $VM | ForEach-Object {$_.MacAddress}
         $nictype = Get-NetworkAdapter -VM $VM | ForEach-Object {$_.Type}
         $nicname = Get-NetworkAdapter -VM $VM | ForEach-Object {$_.NetworkName}
 
         $VMInfo = "" | Select VMName,NICCount,IPAddress,MacAddress,NICType,NetworkName,GuestRunningOS,PowerState,ToolsVersion,ToolsStatus,ToolsRunningStatus,HWLevel,VMHost
         $VMInfo.VMName = $vmview.Name
         $VMInfo.NICCount = $vmview.Guest.Net.Count
         $VMInfo.IPAddress = [String]$getvm.Guest.IPAddress
         $VMInfo.MacAddress = [String]$nicmac
         $VMInfo.NICType = [String]$nictype
         $VMInfo.NetworkName = [String]$nicname
         $VMInfo.GuestRunningOS = $vmview.Guest.GuestFullname
         $VMInfo.PowerState = $getvm.PowerState
         $VMInfo.ToolsVersion = $vmview.Guest.ToolsVersion
         $VMInfo.ToolsStatus = $vmview.Guest.ToolsStatus
         $VMInfo.ToolsRunningStatus = $vmview.Guest.ToolsRunningStatus
         $VMInfo.HWLevel = $vmview.Config.Version
         $VMInfo.VMHost = $getvm.VMHost
 
         $myCol += $VMInfo
     }
 
   if ((Test-Path -path $csvfile) -ne $True){
      $myCol |Export-csv -NoTypeInformation $csvfile
   }
   else{
      $myCol |Export-csv -NoTypeInformation $csvfile-after.csv
   }
}
 
Function CheckAndUpgradeTools($vm){
 
   $vmview = Get-VM $VM | Get-View
   $family = $vmview.Guest.GuestFamily
   $vmToolsStatus = Check-ToolsStatus $vm
 
   if($vmToolsStatus -eq "OK"){
      Write-Host "The VM tools are $vmToolsStatus on $vm"}
   elseif(($family -eq "windowsGuest") -and ($vmToolsStatus -ne "NotInstalled")){
      Write-Host "The VM tools are $vmToolsStatus on $vm. Starting update/install now! This will take at few minutes." -ForegroundColor Red
     Get-Date
     Get-VMGuest $vm | Update-Tools -NoReboot
     do{
     sleep 10
     Write-Host "Checking ToolsStatus $vm now"
     $vmToolsStatus = Check-ToolsStatus $vm
     }until($vmToolsStatus -eq "OK")
      PowerOff-VM $vm
     PowerOn-VM $vm
   }
   else{
      # ToDo: If the guest is running windows but tools notrunning/notinstalled it might be an option to invoke the installation through powershell.
     # Options are then Invoke-VMScript cmdlet or through windows installer: msiexec-i "D: \ VMware Tools64.msi" ADDLOCAL = ALL REMOVE = Audio, Hgfs, VMXNet, WYSE, GuestSDK, VICFSDK, VAssertSDK / qn
     # We're skipping all non-windows guest since automated installs are not supported
     Write-Host "$vm is a $family with tools status $vmToolsStatus. Therefore we're skipping this VM" -ForegroundColor Red
   }
}
 
Function CheckAndUpgrade($vm){
 
   $vmHardware = Check-VMHardwareVersion $vm
   $vmToolsStatus = Check-ToolsStatus $vm
 
   if($vmHardware -eq "OK"){
      Write-Host "The hardware level is $vmHardware on $vm"}
   elseif($vmToolsStatus -eq "OK"){
      Write-Host "The hardware level is $vmHardware on $vm." -ForegroundColor Red
      $PowerOffVM = PowerOff-VM $vm
     if($PowerOffVM -eq "OK"){
        Write-Host "Starting upgrade hardware level on $vm."
       Upgrade-VMHardware $vm
        sleep 5
       PowerOn-VM $vm
        Write-Host $vm "is up to date" -ForegroundColor Green
     }
     else{
     Write-Host "There is something wrong with the hardware level or the tools of $vm. Skipping $vm."
     }
   }
}
 
# Connect-VIServer $vCenter
 
$vms = VM-Selection
CreateHWList $vms $csvfile
foreach($item in $vms){
   $vm = $item.Name
   Write-Host "Test $vm"
   CheckAndUpgradeTools $vm
   CheckAndUpgrade $vm
}
CreateHWList $vms $csvfile
 
#Disconnect-VIServer -Confirm:$false