Only a couple of days ago, a customer asked for a script to deploy hundreds of VMs with a couple of tasks to be done after the VM was available on the network. Like move into a different OU, or register with XenDesktop, etc. The challenge about this was to have the PowerCLI script wait for the VM to be actually done with the Sysprep process and be available on the network before the remaining tasks are executed.

I found an article from Alan Renouf on blogs.vmware.com about this topic but was unable to do it the way he suggested. I didn’t really figure out why it seemed to work for him but not in my case, but for example the way he tested for the Event type and a couple of other things did not work out and created syntax errors etc. While his code showed me how to do it in general, I decided to write my own code. Own code is always better for personal understanding, too icon wink PowerCLI – Waiting for Guest Customization to Finish AND my code is way simpler as it does not simultaneously monitor multiple VMs but only one at a time.

function StartVMAndWaitForSysprep
(
	[Parameter(Mandatory=$True)]
	[Vmware.VIMAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$vmRef
)
{
	$vm = Start-VM $vmRef -Confirm:$False -ErrorAction:Stop

	# wait until VM has started
	Write-Host "Waiting for VM to start ..."
	while ($True)
	{
		$vmEvents = Get-VIEvent -Entity $vm

		$startedEvent = $vmEvents | Where { $_.GetType().Name -eq "VMStartingEvent" }

		if ($startedEvent)
		{
			break
		}
		else
		{
			Start-Sleep -Seconds 2
		}
	}

	# wait until customization process has started
	Write-Host "Waiting for Customization to start ..."
	while($True)
	{
		$vmEvents = Get-VIEvent -Entity $vm
		$startedEvent = $vmEvents | Where { $_.GetType().Name -eq "CustomizationStartedEvent" }

		if ($startedEvent)
		{
			break
		}
		else
		{
			Start-Sleep -Seconds 2
		}
	}

	# wait until customization process has completed or failed
	Write-Host "Waiting for customization ..."
	while ($True)
	{
		$vmEvents = Get-VIEvent -Entity $vm
		$succeedEvent = $vmEvents | Where { $_.GetType().Name -eq "CustomizationSucceeded" }
		$failEvent = $vmEvents | Where { $_.GetType().Name -eq "CustomizationFailed" }

		if ($failEvent)
		{
			Write-Host "Customization failed!"
			return $False
		}

		if($succeedEvent)
		{
			Write-Host "Customization succeeded!"
			return $True
		}

		Start-Sleep -Seconds 2
	}
}

Finally, but not not worth its own posting, we have a function to wait for the guest to have an IP address:

function WaitForToolsToReportIP
(
	[Parameter(Mandatory=$True)]
	[Vmware.VIMAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$vmRef
)
{
	$ping = New-Object System.Net.NetworkInformation.Ping

	while ($True)
	{
		$vm = Get-VM $vmRef

		$guestInfo = $vm.guest

		if($guestInfo.IPAddress -and $guestInfo.IPAddress[0])
		{
			return $True
		}
		else
		{
			Start-Sleep -Seconds 2
		}
	}
}

As you can see, I check whether VMware Tools report an IP address. The advantage of this is that I don’t have to rely on the OS to respond to pings.

Hope that helps!