Automate common VM changes after converting a VM to vSphere

I recently had the need to ‘prep’ a VM after converting it to vSphere. By ‘prep’ I mean (after you've installed VMware tools) do the usual grind of updating the virtual hardware to the latest supported by ESXi, update the vNIC to VMXNET3, and change the SCSI controllers to ParaVirtual. I thought about the times when I was in customer land and we would have to convert VMs from some other platform or in some cases, correct a VM that had been built incorrectly. This train of thought lead me to write the script below and share it out. It really isn't much but you can totally wrap this in another script to run through a bunch of VMs.

The full script is at the end of this post, but I'd like to break it down for those who want to know how it works. At a high level, here is what the script does:

  • Upgrade virtual hardware
  • Change the primary SCSI Controller (Controller 0) to ParaVirtual
  • Change the primary vNIC to VMXNET3

There's a few things happening in there so let's go through the script.

param (
    [Parameter(Mandatory=$true)][string]$vmname
)
$vm = Get-VM -Name $vmname
$vmView = Get-View -VIObject $vm
The first part is a param block asking for a VM name as a string. From here, we can do a look up in any connected vCenter sessions to get the VM object. We'll also get the VM view, and you'll see why a bit later.

Now to make sure the VM is powered off. If it's running, stop the script, but if it's off go ahead and upgrade the virtual hardware to the latest.

if ($vm.PowerState -eq "PoweredOff"){
    $nullParam = @($null)
    $vmView.GetType().GetMethod(UpgradeVM_Task).Invoke($vmView,$nullParam)
} else {
    break
}
Assuming that's all OK, the script moves on to the next part, the SCSI controller. In my experience, I've not been able to simply change a SCSI controller type to Paravirtual and have Windows boot successfully. Even with VMware Tools installed with all appropriate drivers, if you switch it over sometimes the boot drive isn't detected and Windows fails to boot. You'll need to: Shutdown the VM, add a second controller with a disk, boot Windows so the new hardware is detected and the driver loaded, shutdown Windows, switch over the controller type, then boot. That's far too much clicking for me, so here's how you do it with PowerCLI:
$hdd = New-HardDisk -VM $vm -DiskType Flat -CapacityKB 100 -StorageFormat Thin
$scsi = New-ScsiController -Type ParaVirtual -BusSharingMode NoSharing -HardDisk $hdd
$newHdd = Get-HardDisk -vm $vm | where {$_.ExtensionData.ControllerKey -eq $scsi.Key}
sleep 3
Start-VM -VM $vm | Wait-Tools
Stop-VMGuest -VM $vm -Confirm:$false
sleep 15
Excuse the sleep timers, it's simply to give vCenter time to execute the task without having to write out a task-waiter line. Line 1 we're defining a very small new hard disk for the target VM ($hdd). vCenter will add this disk to the first SCSI controller by default. Line 2 we're creating a new SCSI controller ($scsi) of type ParaVirtual, and attaching our new disk ($hdd) to it.

Line 3 is retrieving the new disk by first enumerating all disks attached to $vm, then selecting only the disk that is attached to our new SCSI controller where {$_.ExtensionData.ControllerKey -eq $scsi.Key}. We'll use this later to remove the disk.

Lines 4-8 are pretty obvious, we're starting $vm, waiting for VMware Tools to respond to know the OS has booted successfully, then stopping the Guest OS in preparation for the next step: removing the hard drive and controller.

Remove-HardDisk -HardDisk $newHdd -confirm:$false
sleep 2
Line 1 here is simply removing the newly created disk $newHdd from $vm. Nothing too fancy here. The last of the disk changes is to change the primary SCSI controller to ParaVirtual:
Set-ScsiController -ScsiController $(Get-VM $vm | Get-ScsiController | Select -First 1) -Type ParaVirtual -Confirm:$false
sleep 2
Line 1 you can see we're setting the first SCSI controller (Get-VM $vm | Get-ScsiController | Select -First 1) to type “Paravirtual”. Finally, we can now change the network adapter type to VMXNET3:
$nic = Get-NetworkAdapter -vm $vm | Select -First 1
Set-NetworkAdapter -NetworkAdapter $nic -confirm:$false -Type VMXNET3
After these changes, go ahead and start the VM and wait for VMware Tools to respond.
Start-VM -VM $vm | Wait-Tools
That's it! Hopefully it worked for you. If not, let me know in the comments.

Script

Here's the whole script for you.