Skip to content

Instantly share code, notes, and snippets.

@PatrickTerlisten
Last active February 21, 2018 15:30
Show Gist options
  • Save PatrickTerlisten/538753c24695156543ce to your computer and use it in GitHub Desktop.
Save PatrickTerlisten/538753c24695156543ce to your computer and use it in GitHub Desktop.
This script uses sdelete to zero-out all disks of a Windows VM. Afterwards, the VM is moved between datastores to reclaim zeroed space.
<#
.SYNOPSIS
No parameters needed. Just execute the script.
.DESCRIPTION
This script uses sdelete to zero-out all disks of a Windows VM. Afterwards, the VM
is moved between datastores to reclaim zeroed space.
History
v1.3: Redesign.
v1.2: Added function to zero-out all disks in a guest VM. WMI is used to fetch a
list with all local disk (drivetype = 3).
v1.1: The script now skips VMs with Snapshots and ZeroedThick or EagerZeroedThick
disks
v1.0: Initial version
.EXAMPLE
Reclaim-ThinVMDK
.NOTES
Author: Patrick Terlisten, [email protected], Twitter @PTerlisten
This script is provided “AS IS” with no warranty expressed or implied. Run at your own risk.
This work is licensed under a Creative Commons Attribution NonCommercial ShareAlike 4.0
International License (https://creativecommons.org/licenses/by-nc-sa/4.0/).
.LINK
https://www.vcloudnine.de
#>
### Please change the content of the variables for $PathToSDelete, $VIServer
### $CredFile, $Username, $DstDS, $DstDSHost and $ClusterName according to your environment.
# Path to SDelete and command to exectue. Please make sure that you use the latest version 1.61!
$PathToSDelete ='C:\Windows\sdelete.exe'
$SDeleteCommandLine = 'sdelete -q -z'
# vCenter Server
# I recommend to create a new VICredentialStoreItem and use this to connect to the vCenter Server
$VIServer = 'vcenter.domain.tld'
# To create an encrypted password file, execute the following command
# Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File securestring.txt
# Fill $CredFile with path to securesting.txt. Please make sure that you create the
# $CredFile with the user that is running this script.
$CredFile = 'C:\PATH\TO\securestring.txt'
$Username = 'DOMAIN\USERNAME'
$Password = (Get-Content $CredFile | ConvertTo-SecureString)
$Cred = New-Object System.Management.Automation.PSCredential ($Username, $Password)
# Set destination datastore. Can be a local or shared VMFS datastore.
# If a local datastore is used, $DstDSHost must be set. In case of a shared datastore
# set $DstDSHost to $null.
$DstDS = 'DATASTORE-NAME'
$DstDSHost = 'esx-host.domain.tld'
#$DstDSHost = $null
# Name of vSphere Cluster
$ClusterName = 'VSPHERE-CLUSTER'
# Connect to vCenter Server. VICredentialStoreItem is used for connection. Otherwise change
# parameters and add -User -Password
Write-Host "`n"
Write-Host "Connecting to vCenter Server $VIServer" -ForegroundColor Green
Connect-VIServer $VIServer | Out-Null
# Build an array with all Windows VMs
$SrcVM = (Get-Cluster $ClusterName | Get-VM | Where-Object { $_.Guest.OSFullName -like '*Windows*' })
# Debug: Run only for specific VM
#$SrcVM = (Get-VM | Where { $_.Name -like "vmname" })
# For every object in the array...
$SrcVM | ForEach-Object {
# Tell me what you're doing..
Write-Host "`n"
Write-Host "### Processing VM $_ ###" -ForegroundColor Green
# Check for ZeroedThick and EagerZeroedThick disks
$DiskFormat = ((Get-HardDisk $_).StorageFormat | Select-Object -uniq)
If ( $DiskFormat -like '*Thick*' ) {
Write-Host "`n"
Write-Host "ERROR: At least one disk on VM $_ is of type ZeroedThick or EagerZeroedThick. Skipping this VM" -ForegroundColor Red
}
else {
# Check for Snapshots
$NumberOfSnaps = (Get-VM $_ | Get-Snapshot)
If ($NumberOfSnaps.Count -gt 0) {
Write-Host "`n"
Write-Host "ERROR: VM $_ has at least one active snaphost. Skipping this VM" -ForegroundColor Red
}
elseif ($NumberOfSnaps.Count -eq 0) {
# Set source datastore
$SrcDS = ((Get-HardDisk $_).Filename).Split('[')[1].Split(']')[0]
# Set source host
$SrcHost = (Get-VM $_ | Get-VMHost).name
# Initiate new PSSession
$RemotePSSession = New-PSSession -ComputerName $_.Guest.HostName -Credential $Cred
# Tell me what you're doing...
Write-Host "Starting to zero-out volumes on VM $_. Calling Invoke-Command to run SDelete. To be honest: This can take some time... Check the Task Manager inside the VM for a running sdelete.exe process." -ForegroundColor Green
# Test if SDelete exist. If yes, zero-out all disks
$ExitCodeInvokeCommand = Invoke-Command -Session $RemotePSSession -ArgumentList $PathToSDelete, $SDeleteCommandLine -ScriptBlock {
# This is the sdelete commandline which is used in the GetWmiObject invocation
$CommandlineInGuest = $args[1]
# Test for SDelete and run SDelete
If (Test-Path $args[0]) {
Get-WmiObject win32_logicaldisk| Where-Object { $_.drivetype -eq 3 } | ForEach-Object { cmd.exe /c $CommandlineInGuest $_.name } | Out-Null
$ErrorCode = 0
} Else {
$ErrorCode = 1
}; $ErrorCode
}
# Remove PSSession
Remove-PSSession $_.Guest.HostName
# If SDelete was found and zero-out has completed, move VM to $DstDS and then back to $SrcDS and $SrcHost
If ($ExitCodeInvokeCommand -eq 0) {
# Move-VM is used to SvMotion the VM to the destination datastore using the original DiskStorageFormat (should be always thin)
# If a local datastore is used as destination, the VM is moved to the host with the local datastore an then later back to its source host
If ($DstDSHost -eq $Null) {
Write-Host "Exitcode of Invoke-Command was $ExitCodeInvokeCommand. Everything seems to be fine. Moving VM $_ to $DstDS." -ForegroundColor Green
Move-VM -VM $_ -Datastore $DstDS -DiskStorageFormat $DiskFormat | Out-Null
} Else {
Write-Host "Exitcode of Invoke-Command was $ExitCodeInvokeCommand. Everything seems to be fine. Moving VM $_ to $DstDS on $DstDSHost." -ForegroundColor Green
Move-VM -VM $_ -Destination $DstDSHost -Datastore $DstDS -DiskStorageFormat $DiskFormat | Out-Null
}
# Move-VM is used to SvMotion the VM back to its original datastore using the original DiskStorageFormat
Write-Host "Moving VM back to $SrcDS on $SrcHost and ." -ForegroundColor Green
Move-VM -VM $_ -Destination $SrcHost -Datastore $SrcDS -DiskStorageFormat $DiskFormat | Out-Null
}
# If $ExitCodeInvokeCommand -eq 1, sdelete.exe wasn't found. Skip this VM.
elseif ( $ExitCodeInvokeCommand -eq 1 ) {
Write-Host "`n"
Write-Host 'SDelete was not found. Skipping this VM' -ForegroundColor Red
}
}
}
}
# Disconnect from vCenter Server
Write-Host "`n"
Write-Host "Disconnecting from vCenter Server $VIServer" -ForegroundColor Green
Disconnect-VIServer $VIServer -Force -Confirm:$false | Out-Null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment