I haven’t been posting too much here recently I’m afraid. A lot of the things that I’m currently working on are pretty specific to the environment here, and are not particularly useful (or indeed, interesting) to anyone else.

One of the things I’ve been doing might be more generally useful. We needed to convert around 700 of our machines to thin-provisioned format. When migrating to the new environment we’d stuck to traditional “thick” machines, as there was a lot of upheaval, and we didn’t have the necessary monitoring in place. Now that things have settled, we were looking to take advantage of the thin format to save us some space. We had already run SDELETE on the (non-persistent) machines during the migration, so we were ready to go.

The conversion process is fairly simple: a new option during a storage vMotion. However, the idea of doing this 700 times did not appeal to me, so I wrote the script below to automate the process.

# Convert Machines To Thin Provisioned
# Ben Neise 16/03/10
# Create an empty array
$arrMachinesToBeConverted = @()
Write-Output -InputObject "Getting virtual machine objects" -ForegroundColor Blue
Write-Output -InputObject ""
# Select the broad category of VMs we're looking at, for example all the machines in a specific blue folder
$objVMs = Get-Folder "Projects" | Get-VM | Sort-Object Write-Output -InputObject "Generating list of candidates" -ForegroundColor Blue
Write-Output -InputObject ""
# Loop through all the virtual machines selected
foreach ($objVM in $objVMs){
    Write-Output -InputObject "Investigating " -NoNewline Write-Output -InputObject $objVM -ForegroundColor Blue -NoNewline
    # Skip the rest of the loop if the machine is thin provisioned
    if ($objVM | Get-HardDisk | Where-Object {$_.StorageFormat -like "Thin"}){
        Write-Output -InputObject " - disks are already thin provisioned" -ForegroundColor DarkGray continue
    } # Skip the rest of the loop is the machine is switched on and non-persistent
    if ($objVM | Where-Object {$_.PowerState -ne "PoweredOff"} | Get-HardDisk | Where-Object {$_.Persistence -notlike "IndependentPersistent"}){
        Write-Output -InputObject " - switched on and non-persistent" -ForegroundColor DarkGray continue
    # Skip the rest of the loop if the machine is powered-on, and has snapshots
    if ($objVM | Where-Object {$_.PowerState -ne "PoweredOff" -and ($objVM | Get-Snapshot)}) {
        Write-Output -InputObject " - switched on and with snapshots" -ForegroundColor DarkGray continue
    # Skips the rest of the loop if the machine has a shared drive and is not set up as fault tolerant (indicating that it's a Linked Clone) 
    # Thanks to Keshav Attrey for this method - http://www.vmdev.info/?p=546) 
    $viewVM = $objVM | Get-View -Property Name,Summary,Config.Hardware.Device
    $unshared = $viewVM.Summary.Storage.Unshared
    $committed = $viewVM.Summary.Storage.Committed
    $ftInfo = $viewVM.Summary.Config.FtInfo 
    if (($unshared -ne $committed) -and (($ftInfo -eq $null) -or ($ftInfo.InstanceUuids.Length -le 1))){
        Write-Output -InputObject "The machine is a linked clone" -ForegroundColor DarkGray continue
    Write-Output -InputObject "Added to the list of machines to be converted" $arrMachinesToBeConverted += $objVM
Write-Output -InputObject "Starting Storage vMotions" -ForegroundColor Blue
Write-Output -InputObject $arrMachinesToBeConverted.Count -ForegroundColor Blue -NoNewline
Write-Output -InputObject " machines to be converted" -ForegroundColor DarkGray
Write-Output -InputObject ""
foreach ($objVM in $arrMachinesToBeConverted | Sort-Object){
    # Get the biggest datastore
    $objBiggestDatastore = Get-Datastore | Sort-Object -Property FreeSpaceMB -Descending
    # Select the datastore from the top of the previously generated list (index 0) and remove the preceeding "Datastore-" from it's ID to give us the MOID
    $strTargetDatastore = ($objBiggestDatastore[0].Id).replace('Datastore-','')
    # Let the user know what's going on
    Write-Output -InputObject "Migrating machine " -NoNewline 
    Write-Output -InputObject $objVM -ForegroundColor Blue -NoNewline
    Write-Output -InputObject ", to Thin Provisioned format on datastore " -NoNewline
    Write-Output -InputObject $objBiggestDatastore[0] -ForegroundColor Blue -NoNewline
    Write-Output -InputObject "" 
    # Get the view of the VM we're moving
    $viewVM = Get-View -VIObject $objVM
    # Remove the preceding "HostSystem-" from the VM's Host's ID, giving us the Host's MOID
    $strTargetHost = ($objVM.host.id).replace('HostSystem-','')
    # Create a task specification for the relocation
    $specRelocate = New-Object VMware.Vim.VirtualMachineRelocateSpec
    # Add the target datastore to the specification using the MOID
    $specRelocate.datastore = New-Object VMware.Vim.ManagedObjectReference $specRelocate.datastore.type = "Datastore"
    $specRelocate.datastore.value = $strTargetDatastore
    # Add the host to the specification using the MOID
    $specRelocate.host = New-Object VMware.Vim.ManagedObjectReference
    $specRelocate.host.type = "HostSystem"
    $specRelocate.host.value = $strTargetHost 
    # This is the specification property that makes the disk Thin Provisioned
    $specRelocate.transform = "sparse" 
    # Create the task where the previously specified task is applied to the view of the target VM
    $task = $viewVM.RelocateVM_Task($specRelocate, $priority) 
    # Start the task, and wait for it to complete before continuing
    Get-VIObjectByVIView $task | Wait-Task | Out-Null

It works through the collection of virtual machine objects in $objVMs, and adds them to an array if they pass certain criteria. They must be:-

It then starts a separate loop going through each machine object in the array. I could have integrated the migration task into the first loop, but this approach means you can add a human “sanity-check” if you’ve got certain machines you don’t want to migrate.

The script finds the datastore with the largest amount of free space, and then the actual migration is done using code which I generated using Onyx. The free space on the datastores is re-evaluated before every move. This makes the script quite slow, but this isn’t something you want to rush.