🗂️ Overview
When VMware ESXi hosts cannot find VMware Tools ISO files locally, they look at a configurable
path called ProductLocker — controlled by the advanced setting
UserVars.ProductLockerLocation. By pointing all hosts in a cluster to a
shared datastore path, you only ever need to drop updated Tools files in
one place, and every VM that restarts picks them up automatically.
This post walks through a PowerCLI script that:
- Accepts user input for vCenter address, datastore path, and cluster name — no hardcoded values
- Applies
UserVars.ProductLockerLocationto every ESXi host in the target cluster(s) via the vSphere AdvancedOption API - Optionally puts hosts in Maintenance Mode and restarts them so the
/productLockersymlink is refreshed - Prints a clean summary of successes and failures
📋 Script Execution Flow
| Property | Detail |
|---|---|
| Module | VMware PowerCLI |
| API used | AdvancedOption.UpdateOptions() (vSphere Managed Object) |
| Setting changed | UserVars.ProductLockerLocation |
| Scope | One or all clusters in a vCenter |
| Restart required? | Yes — to refresh the /productLocker symlink (prompted) |
| Broadcom reference | KB 313876 |
✅ Prerequisites
- VMware PowerCLI installed (
Install-Module VMware.PowerCLI -Scope CurrentUser) - vCenter account with Host → Configuration → Advanced Settings privilege
- A shared datastore directory already created and permission set to
700 - VMware Tools depot files downloaded from Broadcom (see download section below)
⚙️ How the Script Works
-
1
User Prompts The script uses
Read-Hostto collect vCenter FQDN/IP, the datastore VMFS path, and an optional cluster name — no hardcoded values, safe to commit to any repo. -
2
Secure Connection Credentials are collected with
Get-CredentialandSet-PowerCLIConfigurationis set to prompt on certificate issues (useful in lab/self-signed cert environments). -
3
Cluster Resolution Targets a named cluster, or falls back to all clusters in the vCenter if none is specified.
-
4
AdvancedOption API Update For each ESXi host, the script creates a
VMware.Vim.OptionValueobject and pushes it viaAdvancedOption.UpdateOptions()— the same vSphere API the UI uses. -
5
Optional Maintenance Mode + Restart Prompts whether to put hosts into Maintenance Mode (with vMotion evacuation) and restart them. The reboot ensures
/productLockersymlink is refreshed at boot time. -
6
Summary Report Prints how many hosts were updated vs failed, making it easy to spot hosts that need attention without parsing verbose logs.
💻 The Script
# ── User Input ──────────────────────────────────────────── $vcenter = Read-Host -Prompt "Enter vCenter FQDN or IP" $productLockerPath = Read-Host -Prompt "Enter ProductLocker datastore path" $clusterInput = Read-Host -Prompt "Enter Cluster name (blank = ALL clusters)" # ── Connect to vCenter ──────────────────────────────────── $cred = Get-Credential -Message "Enter vCenter credentials" Set-PowerCLIConfiguration -InvalidCertificateAction Prompt -Confirm:$false Connect-VIServer -Server $vcenter -Credential $cred -ErrorAction Stop # ── Resolve cluster(s) ──────────────────────────────────── if ([string]::IsNullOrWhiteSpace($clusterInput)) { $clusters = Get-Cluster } else { $clusters = Get-Cluster -Name $clusterInput } # ── Update ProductLockerLocation on every host ──────────── foreach ($cluster in $clusters) { foreach ($esxiHost in (Get-Cluster -Name $cluster.Name | Get-VMHost)) { $hostView = Get-VMHost -Name $esxiHost | Get-View $option = New-Object VMware.Vim.OptionValue $option.Key = "UserVars.ProductLockerLocation" $option.Value = $productLockerPath (Get-View $hostView.ConfigManager.AdvancedOption).UpdateOptions(@($option)) Write-Host "[OK] $esxiHost" -ForegroundColor Green } } # ── Optional restart (Maintenance Mode + Reboot) ────────── if ((Read-Host "Restart all hosts now? (Y/N)") -match '^[Yy]') { foreach ($esxiHost in $updatedHosts) { Set-VMHost -VMHost $esxiHost -State Maintenance -Evacuate:$true -Confirm:$false Restart-VMHost -VMHost $esxiHost -Confirm:$false } } Disconnect-VIServer -Confirm:$false
📥 Downloading VMware Tools from Broadcom
After the hosts reboot, you need to copy the VMware Tools depot files into the datastore path. The latest version as of May 2025 is VMware Tools 13.0.5, which resolves CVE-2025-41244 and CVE-2025-41246.
knowledge.broadcom.com/external/article/313876
VMware Tools free downloads portal:
support.broadcom.com → VMware Tools Free Downloads
Depot directory structure after extraction
/vmfs/volumes/<datastore-id>/.vmwaretools/ ├── metadata.zip ├── sig.tgz ├── linux.iso ├── linuxPreGlibc25.iso ├── netware.iso ├── solaris.iso ├── windows.iso └── winPreVista.iso
🔄 Post-Reboot Workflow
- Wait for hosts to come back online and exit Maintenance Mode
- Verify the setting: vCenter UI → Host → Configure → Advanced System Settings, search for
UserVars.ProductLockerLocation - From ESXi shell, validate the symlink:
readlink /productLocker - Power cycle a test VM — VMware Tools should auto-upgrade from the new depot path
ls -l /productLocker
readlink /productLocker
# Expected output: /vmfs/volumes/<datastore-id>/.vmwaretools
💡 Tips & Gotchas
- Boot timing: If the datastore mounts after the ESXi security policy loads at boot, the symlink may point to the wrong path. Verify with
secpolicytools -d | grep productLocker. - Running VMs: Any VM already powered on needs a vMotion off and back to the host (or a full shutdown + power-on) to see the new Tools path — a reset alone is not sufficient.
- Blank cluster field: Leaving the cluster prompt blank targets every cluster in vCenter — handy for multi-cluster environments but use with caution in production.
- Self-signed certs: The script sets
-InvalidCertificateAction Prompt. Change toIgnorefor fully automated pipelines.
📌 Summary
By centralising the VMware Tools depot on a shared datastore and automating the
UserVars.ProductLockerLocation update across your entire cluster, you eliminate
per-host manual configuration and ensure all VMs always pull the latest approved Tools version.
The PowerCLI script above handles the full lifecycle — discovery, update, reboot, and reporting —
with zero hardcoded values.