PropertyCollector.RetrieveContents Failed

If you’re used to work with vSphere template, and if like us are still in vSphere 4.1 (still not upgraded in vSphere 5 = added in todolist :p), you may have encounter the following issue after trying to edit settings of a template (after VM > Template convertion) :

We got an ugly-mean-not-happy-error :

Call “PropertyCollector.RetrieveContents” for object “propertyCollector” on vCenter Server “vcenter” failed.

VMware published a KBabout that on 2 august 2011 : KB1025367

Here is the workaround :

  1. Power off the virtual machine.
  2. Note the ESX/ESXi host on which the virtual machine resides.
  3. Right-click the virtual machine and click Remove from Inventory.
  4. Browse the datastore and locate the virtual machine.
  5. Right-click the <vm name>.vmx file and click Add to Inventory.
  6. If you are unable to add the virtual machine, close the vSphere Client from vCenter Server.
  7. Log in directly to the ESX/ESXi host on which the virtual machine resides using the vSphere Client. To do this, you must know the root password.
  8. Browse the datastore and locate the virtual machine.
  9. Right-click the <vm name>.vmx file and click Add to Inventory.
  10. Log in to vCenter Server using the vSphere Client.

You can do it manually once, but it’ll soon be boring if you have to do it a lot, and the important thing to know is the unregister/register VM task will erase all Custom Field (normal behavior) ^^

So we made a small PowerCLI script (of course) in order to do the job for us !

Basing on the amazing scripts from @LucD22 http://www.lucd.info/2009/12/02/raiders-of-the-lost-vmx/ and @hypervisor_fr http://www.hypervisor.fr/?p=2927, our script will backup all informations which could be lost during the Unregister (Custom Field, VM Folder, etc…), and then will perform an Unregister/Register task, and finally restore the previously backuped informations.

Function Fix-VMPropCol {
	PARAM (
		[switch]$IsVM = $false,
		[switch]$IsTemplate = $false,
		[Parameter(ValueFromPipeline=$true)]
		[string]$VM = $null
	)

	Function Get-Usage {
		Write-Host "Incorrect Parameters" -ForegroundColor red
		Write-Host "Fix-VMPropCol [-IsVM / -IsTemplate]"
		Write-Host "IsVM		: input object is virtual machine"
		Write-Host "IsTemplate	: input object is template"
	}

	Function Log-Console([string]$log) {
		[string]$logText = (Get-Date -Format "yyyy.MM.dd HH:mm:ss") + " - " + $log
		Write-Host -ForegroundColor green $logText
	}

	# Hiding Write-Progress bar of Set-VM cmdlet
	$ProgressPreference = "SilentlyContinue"

	if (($IsVM -and $IsTemplate) -or ((-not $IsVM) -and (-not $IsTemplate))) {
		Get-Usage
		Break
	}

	Log-Console("Gathering informations")
	$VM_View = Get-View -ViewType VirtualMachine -Filter @{"Name"="^$VM$"}
	$exp_VMName = $VM_View.Name
	$exp_VMHost = Get-View $VM_View.Runtime.Host
	$exp_VMXPath = $VM_View.Config.Files.vmPathName
	$exp_Folder = Get-View $VM_View.parent
	$tab_VM = "" | Select VMName
	$tab_VM.VMName = $exp_VMName

	# Backup informations
	# Backup custom fields
	Log-Console("Gathering Custom Fields")
	if ($VM_View.Summary.CustomValue) {
		foreach ($vmField in $VM_View.Summary.CustomValue) {
			$tab_VM | Add-Member -MemberType NoteProperty -Name ($VM_View.AvailableField | ?{$vmField.Key -eq $_.Key}).name -Value $vmField.value
		}
	}

	# Backup Notes
	Log-Console("Gathering Notes")
	if ($VM_View.Config.Annotation.length -gt 0) {
		$tab_VM | Add-Member -MemberType NoteProperty -Name "Annotation" -Value $VM_View.Config.Annotation
	}

	# Unregister VirtualMachine
	Log-Console("Unregister VirtualMachine")
	if (($tab_VM | Get-Member -MemberType NoteProperty | Measure-Object).count -gt "1") {
		if ($IsTemplate) {
			Get-Template -Name $VM | Remove-Template -DeletePermanently:$false -Confirm:$false
		} elseif ($IsVM) {
			Get-VM -Name $VM | Remove-VM -DeletePermanently:$false -Confirm:$false
		}
	}

	# Register VirtualMachine
	Log-Console("Register VirtualMachine")
	#$params = @($exp_VMXPath,$null,$true,$null,$exp_VMHost.MoRef)
	$params = @($exp_VMXPath,$null,(!$IsTemplate -eq $null),(get-view $exp_VMHost.Parent).ResourcePool,$exp_VMHost.MoRef)
	$taskMoRef = $exp_Folder.GetType().GetMethod("RegisterVM_Task").Invoke($exp_Folder, $params)

	# Waiting just a little bit to be sure object is here ...
	Log-Console("Waiting for task to finalize")
	While ( (Get-Task | ?{$_.id -match $taskMoRef.value}).state -match "Running") { Sleep 4 }

	# Convert into VirtualMachine
	if ($IsTemplate) {
		Log-Console("Convert Template to Virtual Machine in order to set up back Custom Fields and Notes")
		Set-Template -Template $VM -ToVM | Out-Null
	}

	# Reconfigure Custom fields
	Log-Console("Set up back Custom Fields and Notes")
	Foreach ($VMField in ($tab_VM | Get-Member -MemberType NoteProperty)) {
		if ($VMField.name -match "^Annotation$") {
			Set-VM -VM $VM -Description $tab_VM.Annotation -Confirm:$false | Out-Null
		} elseif ($VMField.name -notmatch "^VMName$") {
			Set-Annotation -Entity $VM -CustomAttribute $VMField.name -Value $tab_VM."$($VMField.name)" -Confirm:$false | Out-Null
		}
	}

	# Convert back into template
	if ($IsTemplate) {
		Log-Console("Convert back to Template")
		Set-VM -VM $VM -ToTemplate -Confirm:$false | Out-Null
	}
}

This function have to be used straight after a Get-VM or a Get-Template pipe and take as only one argument a switch in order to match the input object type (-IsVM for a machine virtuelle and -IsTemplate for a template) :

Get-VM -Name VM | Fix-VMPropCol -IsVM
Get-Template -Name TEMPLATE | Fix-VMPropCol -IsTemplate

In the future update, we plan to add the automatic detection of the InputObject in order to avoid the need of the -IsVM or -IsTemplate switch and also allowing the launch of the cmdlet alone without piping.

Leave a Reply

Required fields are marked *.