Zero-Drive.psm1

<#
 .Synopsis
  Zero free space on a disk.
 
 .Description
  Powershell script version of Sdelete -Z to zero free space on a disk.
 
 .Parameter Drive
  Disk drive to Zero, supports with and without colon (:)
 
 .Parameter FileName
  FileName of Zero Temporary file to create.
 
 .Parameter BlockSize
  Disk write blocksize in Byte.
 
 .Parameter SpaceToLeave
  Disk space to leave in Gigabyte.
 
 .Example
   # Zero free space on c:
   Invoke-ZeroDrive -Drive C
 
 .Example
   # Zero free space on d: using specified filename and specified free space to not zero, using blocksize 4KB
   Invoke-ZeroDrive -Drive D: -FileName "zero.tmp" -SpaceToLeave 25 -BlockSize 4kb
 
#>


Function Invoke-ZeroDrive {
    param(
        [parameter(Mandatory=$true)]
        [ValidateScript({
            if( -Not ($_ | Test-Path) ) {
                throw "Drive $_ does not exist."
            }
            return $true
        })]
        [System.IO.DriveInfo]$Drive,
    
        [parameter(Mandatory=$false)]
        [string]$FileName = "zero_drive.tmp",

        [parameter(Mandatory=$false)]
        [ValidateSet(4kb,8kb,16kb,32kb,64kb,1mb)]
        [int]$BlockSize = 64kb,
    
        [parameter(Mandatory=$false)]
        [int]$SpaceToLeave = 2
    ) 
    
    $ZeroFileSizeTarget   = $Drive.AvailableFreeSpace - ($SpaceToLeave * 1024 * 1024 * 1024)
    $ZeroFileSizeTargetGB = [math]::Round(($ZeroFileSizeTarget / 1024 / 1024 / 1024),2)
    $ZeroFilePath         = $Drive.Name + "$FileName"

    # Abort if a File with the same path and name already exist (Could not get this to work within parameter validate scope)
    if( Test-Path -Path $ZeroFilePath ) {
        Throw "$ZeroFilePath Already exist, aborting."
    }

    Try {
        
        $CurrentZeroFileSize = 0
        $ProgressPercent = 0
        $StopWatch = [system.diagnostics.stopwatch]::StartNew()
        Write-Progress -Activity "Zeroing $ZeroFileSizeTargetGB GB free space with $ZeroFilePath" -PercentComplete $ProgressPercent
     
        $ZeroArray = new-object byte[]($BlockSize)
        $Stream = [io.File]::OpenWrite($ZeroFilePath)
    
        while($CurrentZeroFileSize -lt $ZeroFileSizeTarget) {
        
            # Only update progress when progress is +1 integer, Write-Progress slows down the script by updating the progress bar for each disk write.
            $PreviousProgressPercent = $ProgressPercent
            $ProgressPercent = [int]($CurrentZeroFileSize / $ZeroFileSizeTarget * 100)
            if ($ProgressPercent -gt $PreviousProgressPercent) {
                $ElapsedTime = [string]$StopWatch.Elapsed.Hours+" Hours "+[string]$StopWatch.Elapsed.Minutes+" Minutes "+[string]$StopWatch.Elapsed.Seconds+" Seconds"
                if ($StopWatch.Elapsed.Seconds -ge 1) {
                    $SpeedByte  = ($CurrentZeroFileSize / 1024 / 1024) / $StopWatch.Elapsed.TotalSeconds
                    $SpeedMByte = [math]::Round($SpeedByte,0)
                }
                Write-Progress -Activity "Zeroing $ZeroFileSizeTargetGB GB free space with $ZeroFilePath" -Status "$ProgressPercent% in $ElapsedTime" -PercentComplete $ProgressPercent -CurrentOperation "Average Speed: $SpeedMByte MB/s" -SecondsRemaining (($StopWatch.Elapsed.TotalSeconds / $ProgressPercent) * (100 - $ProgressPercent))
            }
        
            $Stream.Write($ZeroArray,0, $ZeroArray.Length)
            $CurrentZeroFileSize +=$ZeroArray.Length
        }
    }

    Catch {
        $Exception = $true
        Throw
    }

    Finally {

        # Cleanup (Also works with CTRL-C, though write-output gets lost with CTRL-C, but write-host works with CTRL-C)
        $StopWatch.Stop()

        If ($Stream) {
            $Stream.Close()
        }

        If (Test-Path -Path $ZeroFilePath) {
            Remove-Item $ZeroFilePath
        }

        Write-Progress -Activity "Finished Zeroing $ZeroFileSizeTargetGB GB free space with $ZeroFilePath" -Completed
        
        If (-not $Exception) {
            $ZeroWrittenGB = [string]([math]::Round(($CurrentZeroFileSize / 1024 / 1024 / 1024),2))+" GB"
            $ElapsedTime = $StopWatch.Elapsed
            Write-Host "Finished Zeroing $ZeroWrittenGB in $ElapsedTime on $ZeroFilePath (Average Speed: $SpeedMByte MB/s)"
        }

    }
}