Zum Inhalt springen

zero out free space in all VMs in a Hyper-V cluster to free SAN space

The script writes zeros to unused spaces on all disk inside all VMs to free space in thin provisioned SAN based Hyper-V Cluster.
To do this, the VMs in the Hyper-V Cluster ($Clustername) are enumerated and a powershell based sdelete runs inside all VMs.
The Powershell code to zero out free spaces is original written by Chris Duck (http://blog.whatsupduck.net/2012/03/powershell-alternative-to-sdelete.html)

Customize the variable $Clustername and run the sript with elevated permissions.
You can exclude VMs costumizing $VMsExclude with VM name
sThe script will use your account to run the PS script inside the VMs, therfor it prompts for your password

 

### ------------------------------------
<#  
	ScriptVersion 1.3 
	4/17/2017
	Guido Jeuken
	
	The script writes zeros to unused spaces on all disk inside all VMs to free space in thin provisioned SAN based Hyper-V Cluster.
	To do this, the VMs in the Hyper-V Cluster ($Clustername) are enumerated and a powershell based sdelete runs inside all VMs.
	The Powershell code to zero out free spaces is original written by Chris Duck (http://blog.whatsupduck.net/2012/03/powershell-alternative-to-sdelete.html)
	
	so all files in folder C:-Windows-ProPatches-Patches wants to deletetd
	
	customize the variable $Clustername and run the sript with elevated permissions.
	you can exclude VMs costumizing $VMsExclude with VM names  
	the script will use your account to run the PS script inside the VMs, therfor it prompts for your password
#>
### ------------------------------------
Cls
• customize this
$Clustername= "nameofcluster"
$VMsToExclude= "vm1*"
Cls
$pw = read-host "Please enter your password" -AsSecureString
$uName = $env:USERNAME
$Cred = New-Object System.Management.Automation.PSCredential $uName, $pw
import-module failoverclusters
$VMsinCluster = Get-ClusterResource -cluster $Clustername | Where-Object . . . . . . . . . . . . . ResourceType -eq "Virtual Machine" | Get VM
foreach ($VM, $VMsinCluster)
    {
	if ($VM.vmname -like $VMsToExclude) - skip if VMname is like $VMsToExclude
		{
			Write host $VM.VMName "excluded"
			Continue
		}

     $S = new-PSSession -ComputerName $VM.VMName -Credential $Cred
     Write host "running sdelete in" $VM.VMName 
     invoke-Command -Session $S -ScriptBlock - function doSdelete ($Root)
                                                            #Convert the $Root value to a valid WMI filter string
                                                            $FixedRoot = ($Root.Trim("") -replace "A",""""" + ""
                                                            $FileName = "ThinSAN.tmp"
                                                            $FilePath = Join-Path $Root $FileName
                                                            $PercentFree =.1
														
                                                            #Check and make sure the file doesn't already exist so we don't clobber someone's data
                                                            if( (Test-Path $FilePath) )
                                                            Write-Error message "The file $FilePath already exists, please delete the file and try again"
                                                            • else . . . . . . . . .
                                                            #Get a reference to the volume so we can calculate the desired file size later
                                                             Write host "writing zeros at $Root"
                                                            $Volume = gwmi win32_volume -filter "name='$FixedRoot'"
                                                            if ($Volume)
                                                            #I have not tested for the optimum IO size ($ArraySize), 64kb is what sdelete.exe uses
                                                            $ArraySize = 64kb
                                                            #Calculate the amount of space to leave on the disk
                                                            $SpaceToLeave = $Volume.Capacity * $PercentFree
                                                            #Calculate the file size needed to leave the desired amount of space
                                                            $FileSize = $Volume.FreeSpace - $SpacetoLeave
                                                            #Create an array of zeroes to write to disk
                                                            $ZeroArray = new-object byte[]($ArraySize)
      
                                                            #Open a file stream to our file 
                                                            $Stream = [io.File]::OpenWrite($FilePath)
                                                            #Start a try/finally block so we don't leak file handles if any exceptions occur
                                                            try
                                                            #Keep track of how much data we've written to the file
                                                            $CurFileSize = 0
                                                            while($CurFileSize -lt $FileSize)
                                                             #Write the entire zero array buffer out to the file stream
                                                            $Stream.Write($ZeroArray.0, $ZeroArray.Length)
                                                             #Increment our file size by the amount of data written to disk
                                                            $CurFileSize += $ZeroArray.Length
                                                            }
                                                            • Finally . . . . . . . . .
                                                             #always close our file stream, even if an exception occurred
                                                            if ($Stream)
                                                               $Stream.Close()
                                                             }
                                                             #always delete the file if we created it, even if an exception occurred
                                                             if( (Test-Path $FilePath) )
                                                                 del $FilePath
                                                               }
                                                            }
                                                            • else . . . . . . . . .
                                                            Write-Error "Unable to locate a volume mounted at $Root"
                                                            }
                                                            }
                                                            } 
                                                            #MAIN 
															Write host "I'm inside" $env:COMPUTERNAME
															$PatchDirToDelete="C:"Windows-ProPatches-Patches"*.*"
															#delete shavlik patchmgnt folder
 															if( (Test-Path $PatchDirToDelete) )
                                                            		remove-item $PatchDirToDelete -Confirm:$false
                                                            		}
                                                            $csvs = Get-WmiObject Win32_Volume -filter "drivetype=3" 
                                                                foreach ($cvs in $csvs)
                                                            {
                                                               $env:computername+" "+ $cvs.driveletter
                                                              
                                                                $root = ([string]$cvs.driveletter)
                                                                   doSdelete -root $Root
                                                             }   
															 }
															 }