Disk defragmentation in PowerShell
Introduction
I recently posted a function on the blog that allowed you to get disk defragmentation status within PowerShell and promised at the end of that post that I would follow up with a second function that would allow you to perform a disk defragmentation of a volume in PowerShell, this post details that function.
Perform a disk defragmentation in PowerShell
The ability to perform a disk defragmentation from the command line is useful as it allows you to perform scheduled disk defragmentation against your servers disk volumes on a scheduled basis, ensuring that optimum server performance can be maintained.
The Start-DiskDefrag PowerShell function detailed below allows you to perform a disk defragmentation against a specific volume on a Windows computer, however the real power of this function is realised when it is used along with the Get-DefragStatus function from my previous post as this allows you to get the defragmentation status of drives from multiple computers and pipe only those requiring a defrag through to the Start-DiskDefrag function, an example of how this can be achieved is shown further down this article.
Let’s start by showing the code for the function, to define this function within your PowerShell session simply copy and paste the code below into your PowerShell window to execute it or copy it and save it into a .ps1 file. For those of you who prefer to download a PowerShell script version of this function, I have made one available here.
<#
.SYNOPSIS
Perform a disk defragmentation against a disk volume.
.DESCRIPTION
Start-DiskDefrag can be used to perform a disk defragmentation for a disk volume on a specified machine.
The cmdlet supports both direct input in the form of arguments to parameters and also pipline input
from the Get-DefragStatus cmdlet.
.PARAMETER ComputerName
Specifies the names or IP addresses of the computer which the cmdlet should run against.
.PARAMETER DriveLetter
Specifies the drive letter of the volume which should be defragmented
.PARAMETER Force
Specifies that the defragmentation should take place regardless of the available free space on a disk volume
.LINK
http://www.mywinkb.com/Disk-defragmentation-in-PowerShell
.LINK
http://www.mywinkb.com/Get-disk-defrag-status-PowerShell
.EXAMPLE
C:\PS>Start-DiskDefrag -ComputerName localhost -DriveLetter C:
This command performs a disk defragmentation on the C: drive on the machine localhost.
.EXAMPLE
C:\PS>Start-DiskDefrag -ComputerName Server01 -DriveLetter D: -Force
This command performs a disk defragmentation on the D: drive on the machine Server01, the
force parameter is specified to perform the disk defragmentation regardless of the free space
available on the D: drive.
.EXAMPLE
C:\PS>Get-DefragStatus -ComputerName Server01,Server02 | Where-Object { $_.DefragRequired -eq $true } | Start-DiskDefrag
This command returns all volumes on the computers Server01 & Server02 which require a disk defrag and then
begins a disk defragmentation on them if they have sufficient free space.
#>Function Start-DiskDefrag {
[CmdletBinding()]
[OutputType([Object])]
Param (
[Parameter(Mandatory = $true,
ValueFromPipeLineByPropertyName=$true)]
[string] $ComputerName,
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[string] $DriveLetter,
[Parameter()]
[switch] $Force
)
Process {
#Check if the computer specified is available, if not generate an error
Write-Verbose "Attempting to connect to $ComputerName"
if ((Test-Connection -ComputerName $computername -Count 1 -Quiet) -eq $false) {
Write-Error -Message "Unable to connect to computer: $ComputerName"
}
#The connection to the computer was successful, continue
Else {
Write-Verbose "Connection successful…"
Write-Verbose "Attempting to get volume information for $driveletter on $computername via WMI"
Try {
#Use WMI to get the disk volume via the Win32_Volume class
$Volume = Get-WmiObject -ComputerName $ComputerName -Class win32_volume -Filter "DriveLetter=’$DriveLetter’"
}
Catch { }
Write-Verbose "Volume retrieved successfully.."
#Check if the force switch was specified, if it was begin the disk defragmentation
If ($force) {
Write-Verbose "force parameter detected, disk defragmentation will be performed regardless of the free space on the volume"
Write-Verbose "Defragmenting volume $driveletter on $computername"
#Call the defrag method on the wmi object
$Defrag = $Volume.Defrag($true)
}
#If force was not specified check the available disk space the volume specified
Else {
Write-Verbose "Checking free space for volume $driveletter on $computername"
#Check the free space on the volume is greater than 15% of the total volume size, if it isn’t write an error
if (($Volume.FreeSpace /1GB) -lt ($Volume.Capacity / 1GB) * 0.15) {
Write-Error "Volume $Driveletter on $computername does not have sufficient free space to allow a disk defragmentation, to perform a disk `
defragmentation either free up some space on the volume or use Start-DiskDefrag with the -force switch"
}
Else {
#Sufficient free space is available, perform the disk defragmentation
Write-Verbose "Volume has sufficient free space for a defragmentation to be performed"
Write-Verbose "Beginning disk defragmentation"
$Defrag = $Volume.Defrag($false)
}
} #Close -force switch conditional
#Check the defragmentation results and inform the user of any errors
Switch ($Defrag.ReturnValue) {
0 { Write-Verbose "Defragmentation completed successfully…" }
1 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: Access Denied" }
2 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: Defragmentation is not supported for this volume" }
3 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: Volume dirty bit is set" }
4 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: Insufficient disk space" }
5 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: Corrupt master file table detected" }
6 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: The operation was cancelled" }
7 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: The operation was cancelled" }
8 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: A disk defragmentation is already in process" }
9 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: Unable to connect to the defragmentation engine" }
10 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: A defragmentation engine error occurred" }
11 { Write-Error -Message "Defragmentation of volume $DriveLetter on $ComputerName failed: Unknown error" }
}
} #Close Test-Computer conditional
} #End Process Block} #End Function
The beginning of the code snippet above contains the inline help for the function, which allows you to use the command ‘Get-Help Start-DiskDefrag’ within your PowerShell console to retrieve help on using the function. Inside the body of the function the Win32_Volume WMI class is used to check the free space available on the volume specified and also to perform the disk defragmentation. The Start-DiskDefrag function does not produce any output when it completes successfully, but will produce detailed error information if an error were to occur.
Examples of performing a disk defragmentation in PowerShell
As mentioned at the beginning of this post the Start-DiskDefrag function can be used to perform a disk defragmentation for a specific volume on a computer, however the real power of this function is realised when used with the Get-DefragStatus function from my previous post, we will look at examples of both of these usage types below.
Performing a disk defragmentation against a specific volume
To perform a disk defragmentation of a specific volume on a computer you need to specify the name of the computer you would like to connect to and the drive letter of the volume you would like to defragment. An example of the command to defrag the D: drive on a computer named Server01 is shown below.
Start-DiskDefrag –ComputerName –DriveLetter D:
Performing a disk defragmentation against multiple computers and volumes
To perform a disk defragmentation against multiple volumes on multiple computers the Start-DiskDefrag function can be used with the Get-DefragStatus function, to do this the Get-DefragStatus function should be used with the Where-Object cmdlet to filter for only the volumes on the computers specified which require a disk defragmentation, these are then piped into the Start-DiskDefrag function. An example of the command to perform a disk defragmentation against all volumes which require a defrag on the computers Server01,Server02 and Server03 is shown below.
Get-DefragStatus -ComputerName Server01,Server02,Server03 | Where-Object { $_.DefragRequired -eq $true } | Start-DiskDefrag
Another option for providing the names of the computers you would like to perform a defragmentation against is to store them in a .txt file with one computer name on each line, for example if you had a file named servers.txt at the root of your C: drive with the following contents
Server01
Server02
Server03
Server04
The following command could be used to perform a disk defragmentation of the volumes on each of the computers above which require a defrag.
Get-Content C:\Servers.txt | Get-DefragStatus | Where-Object { $_.DefragRequired -eq $true } | Start-DiskDefrag
Using the –Force parameter
There is one final parameter to the Start-DiskDefrag function to be aware of, the –Force parameter. By default the Start-DiskDefrag function will only perform a disk defragmentation on a volume if that volume has sufficient free space, a volume must have at least 15% free space to allow an optimum disk defragmentation to take place. If you need to perform a disk defragmentation on a volume which has insufficient free space you can use the –force parameter of the Start-DiskDefrag function, this will force the disk defragmentation to take place regardless of the free space on the volume.
Close
Well, that should give you all the information you need on using the Start-DiskDefrag function above to perform disk defragmentation within PowerShell, remember that for those of you who prefer to download a script than use the function listed above a script version is available for download here.
- That’s all for now, to stay in touch with new posts and PowerShell programs posted on the blog please follow me on twitter. I will be looking to bundle both the Get-DefragStatus and the Start-DiskDefrag functions into a module shortly to allow for easier use, stay tuned for the release of this and further posts.
