I came across some VMs that although being Thin Provisioned took up all the diskspace that they could. vMotion (explicitly choosing target type Thin didn’t help either. The Guest OS reported 60 GB of an 80 GB volume being free, but the VMDK was 80GB is size. This is something that happens, if the disk has been written to, the VMDK inflates, when the files are deleted the VMDK may not shrink. There is a Knowledge Base article about this, and it explains exactly what to do. But I needed to do it for many VMs, so I wrote a script. It will vMotion the VM to the datastore with the largest free space available, push sDelete.exe to the machine, runs sDelete, vMotions the machine back to it’s original datastore. All without powering down the machine or the need for vmkfstools. The script now only deals with the C:\ disk (since that was the disk on my machines, but can be easily be adapted to work multiple disks. And it assumes that the machine has a D:\ disk where it can put the sDelete.exe file. Don’t worry when the output halts at “SDelete is set for 1 pass.” it will take some time. Just let it sit, and it will continue. In my experience it takes 15-30 min for a 80GB volume.

It needs a CSV inputfile with some information, which can all be exported from vCenter:
VirtualMachine;Hostname;DataStore MyVirtualMachine;MyVirtualMachine.MyDomain.MyTLD;MyDatastore
$ManualCopy=@()
$ReclaimedSpace=0
$suspectVMs = Import-CSV C:\MyFiles\BloatedVMs.csv -Delimiter ";"
$suspectVMs | Sort-Object {Get-Random} | Foreach-Object {
$currentVM=$_
Write-Host Now Proccesing $($currentVM.VirtualMachine) on $($currentVM.Datastore)
$ConsumedBefore=(Get-VM $currentVM.VirtualMachine).get_UsedSpaceGB()
$SourcePath = "C:\Users\vmNinja\Downloads\sDelete\sDelete.exe"
$TargetPath = "\\" + $currentVM.HostName + "\D$\"
try {
Copy-Item -Path $SourcePath -Destination $TargetPath -ErrorAction Stop
}
Catch {
$ManualCopy += $currentVM
Write-Warning -message "Cannot Move VM $($currentVM.VirtualMachine) Unable to copy needed files. Error: $($Error[0].exception.message)"
}
$SourceDataStore = (Get-DataStore -Name $currentVM.DataStore)[0]
$SpaceNeeded = (Get-VM $currentVM.VirtualMachine).Guest.Disks | Measure-Object CapacityGB -Sum | Select-Object -ExpandProperty Sum
$TargetDataStore = Get-VMHost (Get-VM $currentVM.VirtualMachine).VMHost | Get-Cluster | Get-Datastore | Where-Object {$_.Type -eq "NFS"} | Sort-Object -Property FreeSpaceGB -Descending | Select-Object -First 1
If ($TargetDataStore.FreeSpaceGB -gt ($SpaceNeeded*2)) {
Try {
Get-VM $currentVM.VirtualMachine | Move-VM -Destination (Get-VMHost (Get-VM $currentVM.VirtualMachine).VMHost) -DiskStorageFormat Thin -DataStore (Get-Datastore $TargetDataStore | Select-Object -ExpandProperty Name -First 1) -ErrorAction Stop
}
Catch {
$ManualCopy += $currentVM
Write-Warning -Message "Could not move $($currentVM.VirtualMachine) from $($currentVM.Datastore) to $($TargetDataStore[0]) Error: $($Error[0].Exception.Message)"
}
$CommandString = "D:\sDelete.exe /acceptEULA -nobanner -z C:"
$ScriptBlock = [ScriptBlock]::Create($CommandString)
Try {
Invoke-Command -ScriptBlock $ScriptBlock -ComputerName $currentVM.HostName -ErrorAction Stop
}
Catch {
$ManualCopy += $currentVM
Write-Warning -Message "Could run sDelete on $($currentVM.VirtualMachine) Error: $($Error[0].Exception.Message)"
}
Try {
Get-VM $currentVM.VirtualMachine | Move-VM -Destination (Get-VMHost (Get-VM $currentVM.VirtualMachine).VMHost) -DiskStorageFormat Thin -DataStore (Get-Datastore $SourceDataStore | Select-Object -ExpandProperty Name -First 1) -ErrorAction Stop
$ConsumedAfter=(Get-VM $currentVM.VirtualMachine).get_UsedSpaceGB()
$ReclaimedSpace=$ReclaimedSpace+([MATH]::Round(($ConsumedBefore - $ConsumedAfter),2))
Write-Host "$($CurrentVM.VirtualMachine) Space Reclaimed: $([MATH]::Round(($ConsumedBefore - $ConsumedAfter),2)) GB" -ForegroundColor Green
}
Catch {
$ManualCopy += $currentVM
Write-Warning -Message "Could not move $($currentVM.VirtualMachine) from $($TargetDataStore[0]) back to to $($currentVM.Datastore) Error: $($Error[0].Exception.Message)"
}
}
Else {
$ManualCopy += $currentVM
$TargetSpace=$([MATH]::Round(($SpaceNeeded*2),2))
Write-Warning -message "Cannot Move VM $($currentVM.VirtualMachine) There is not enough storage free in the Clusters' Datastores, need at least: $($TargetSpace) GB"
}
}
if ($ManualCopy.Count -ge 1) {
Write-Warning -Message "These VirtualMachines could not be debloated automatically, please check them manually: "
$ManualCopy | Sort-Object -Property VirtualMachine -Unique
}
Write-Host "Total Space Reclaimed: $ReclaimedSpace" -ForegroundColor Green