Use PowerShell to Determine Fragmentation of Your Drive

Summary: Learn how to use Windows PowerShell to determine the amount of fragmentation of your hard disk drives.

Hey, Scripting Guy! Question

  Hey, Scripting Guy! One of the things that annoys me is that the “new” defragmenter program in Windows 7 does not return any information. I guess from a user standpoint, it is OK—and it runs as a service, and that is OK. But I enjoyed seeing the reports. Perhaps it is the geek in me. Is there something Windows PowerShell can do about this?


Hey, Scripting Guy! Answer Hello NB,

Microsoft Scripting Guy, Ed Wilson, here. Believe it or not, it has turned cold down here in Charlotte, North Carolina. At this very minute, it is a beautiful sunny day, but the temperature has not risen above 48 degrees Fahrenheit (8 degrees Celsius, according to my Windows PowerShell conversion module).

Beginning with Windows Vista, the defrag program runs as a scheduled task. This task appears in the Task Scheduler utility, as shown in the following image.

Image of Task Scheduler

I manually edited the command line of the scheduled task because I have a solid-state drive (SSD)—drive C, and I did not want the defrag program running against that drive. To ensure this, I modified the switches to cause the program to only defragment my drive E.

When defrag runs (either an analysis or an actual defragmentation session), an event is written to the Application log. As seen in the following image, the information is less than illuminating.

Image of event message

This is where our understanding of WMI methods comes into play. It also proves the value of the report I produced last week of implemented WMI methods.

“What has all this got to do with WMI and with methods?” you may ask.

I will tell you…the Win32_Volume WMI class (first available in Windows Server 2003, but not available in Windows XP) contains a method called DefragAnalysis. The cool thing about the DefragAnalysis method is that it returns an instance of the Win32_DefragAnalysis WMI class. This sounds a bit complicated, and in VBScript, it is actually rather complicated. In Windows PowerShell, however, it is a cinch!

The first thing we need to do is to start Windows PowerShell as an Administrator. Right-click the Windows PowerShell icon and choose Run as an Administrator from the Tasks menu, as shown in the following image.

Image of Tasks menu

Now that we have a Windows PowerShell console running with Administrator rights, I use the Get-WmiObject cmdlet to retrieve an instance of the Win32_Volume WMI class that represents my drive E. I store the returned object in the $drive variable. Next, I simply call the DefragAnalysis method. Remember, when calling methods, the use of parentheses is required. The two commands and the associated output are shown here.

PS C:\> $drive = Get-WmiObject -Class Win32_Volume -Filter “DriveLetter = ‘e:'”

PS C:\> $drive.DefragAnalysis()


__GENUS           : 2

__CLASS           : __PARAMETERS



__RELPATH         :


__DERIVATION      : {}

__SERVER          :

__NAMESPACE       :

__PATH            :

DefragAnalysis    : System.Management.ManagementBaseObject

DefragRecommended : False

ReturnValue       : 0

A return value of 0 means that there were no errors. Groovy, everything is copacetic. Except for one glaring problem—this command returns no more information than what is contained in the event log.

Remember when I said that this method returns an instance of the Win32_DefragAnalysis WMI class? Yes? Great. Now in the output, it shows us the following line.

DefragAnalysis : System.Management.ManagementBaseObject

This tells me that the DefragAnalysis property contains an object. The best way to retrieve this object, is to store the returned objects in a variable. This technique and the associated output from the command is shown here.

PS C:\> $drive = Get-WmiObject -Class Win32_Volume -Filter “DriveLetter = ‘e:'”

PS C:\> $report = $drive.DefragAnalysis()

PS C:\> $report.DefragAnalysis


__GENUS                       : 1

__CLASS                       : Win32_DefragAnalysis

__SUPERCLASS                  :

__DYNASTY                     : Win32_DefragAnalysis

__RELPATH                     : Win32_DefragAnalysis

__PROPERTY_COUNT              : 27

__DERIVATION                  : {}

__SERVER                      : MRED1

__NAMESPACE                   : ROOT\cimv2

__PATH                        : \\MRED1\ROOT\cimv2:Win32_DefragAnalysis

AverageFileSize               : 976

AverageFragmentsPerFile       : 1

AverageFreeSpacePerExtent     : 1822187520

ClusterSize                   : 4096

ExcessFolderFragments         : 0

FilePercentFragmentation      : 0

FragmentedFolders             : 0

FreeSpace                     : 350030282752

FreeSpacePercent              : 69

FreeSpacePercentFragmentation : 0

LargestFreeSpaceExtent        : 349548830720

MFTPercentInUse               : 100

MFTRecordCount                : 39423

PageFileSize                  : 0

TotalExcessFragments          : 2

TotalFiles                    : 37513

TotalFolders                  : 874

TotalFragmentedFiles          : 2

TotalFreeSpaceExtents         : 192

TotalMFTFragments             : 2

TotalMFTSize                  : 40370176

TotalPageFileFragments        : 0

TotalPercentFragmentation     : 0

TotalUnmovableFiles           : 10

UsedSpace                     : 150074404864

VolumeName                    :

VolumeSize                    : 500104687616

As you can see from this output, the DefragAnalysis method returns some very useful information.

There is another way to call WMI methods, and I will show this to you now.

The Windows PowerShell cmdlet Invoke-WmiMethod is used to invoke WMI methods. It accepts the name of the method to run, an input object (or WMI class name), and any arguments that might be needed. Unfortunately, unlike the Set-WmiProperty cmdlet that I discussed yesterday, the Invoke-WMiMethod cmdlet uses an array for input (as opposed to a hash table like the Set-WmiProperty).

To use the Invoke-WmiMethod cmdlet, I first use the Get-WmiObject cmdlet to retrieve an instance of the Win32_Volume WMI class that is associated with my drive E. Next, I call the Invoke-WmiMethod to pass the input object that is stored in the $drive variable, and I use the name parameter to call the DefragAnalysis method. This method requires no arguments. The command and its associated output are shown here.

PS C:\> $drive = Get-WmiObject -Class Win32_Volume -Filter “DriveLetter = ‘e:'”

PS C:\> Invoke-WmiMethod -InputObject $drive -Name DefragAnalysis


__GENUS           : 2

__CLASS           : __PARAMETERS



__RELPATH         :


__DERIVATION      : {}

__SERVER          :

__NAMESPACE       :

__PATH            :

DefragAnalysis    : System.Management.ManagementBaseObject

DefragRecommended : False

ReturnValue       : 0

NB, you can easily put the previous commands into a .PS1 file and make a script out of it. If you like the analysis report that is generated by the DefragAnalysis method, you might even redirect the output to a text file, and use the Task Scheduler to Run the Script on a Regular Basis.

NB, that is all there is to using the DefragAnalysis method and Windows PowerShell to determine the level of fragmentation of your hard disk drives. Join me tomorrow when I will have a guest blog about Microsoft Exchange Server from guest blogger, Nicolas Blank. Nicolas is really cool, and the article is excellent. You will not want to miss it.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Comments (3)

  1. Anonymous says:

    Worth noting that the DefragAnalysis() seems to need an elevated command prompt (at least on 2008R2).

    (Without this I got return code 11: unknown error.)

  2. mredwilson says:

    @Stephen you are welcome. @Rjcox you are right, DefragAnalysis() method does require an elevated prompt (at least on Vista, Windows Server 2008, Windows 7, and Windows Server 2008 R2. If you are writing a script to do this, you may want to use my test-isadministrator function (search for it on this blog) to ensure is running with proper privileges.

  3. Stephen Rains says:

    Thanks for the information