function Get-Disk {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[String]$ComputerName
)
Begin {
"Get-Disk( '$ComputerName' )" | Write-Verbose
$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
}
Process {
$CimSessionOption = New-CimSessionOption -Protocol Dcom
$CimSession = New-CimSession -SessionOption $CimSessionOption -Verbose:$false -ComputerName $ComputerName
$SqlDisks = @()
'Get Local Disks...' | Write-Verbose
$Disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' -CimSession $CimSession -Verbose:$false
foreach ($Disk in $Disks) {
"Disk.DeviceID = '$($Disk.DeviceID)'." | Write-Verbose
$SqlDisk = New-Object -TypeName PSObject
$SqlDisk.PSObject.TypeNames.Insert(0, 'SqlAdmin.Disk')
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DriveLetter -Value $Disk.DeviceID
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DiskFreeSpaceBytes -Value $Disk.FreeSpace
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DiskSizeBytes -Value $Disk.Size
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name FileSystem -Value $Disk.FileSystem
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name VolumeDirty -Value $Disk.VolumeDirty
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name VolumeName -Value $Disk.VolumeName
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name VolumeSerialNumber -Value $Disk.VolumeSerialNumber
$Partition = Get-CimAssociatedInstance -CimInstance $Disk -ResultClassName Win32_DiskPartition -Verbose:$false
"Partition.DeviceID = '$($Partition.DeviceID)'." | Write-Verbose
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name PartitionDeviceID -Value $Partition.DeviceID
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name BlockSizeBytes -Value $Partition.BlockSize
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name BlockCount -Value $Partition.NumberOfBlocks
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name PartitionDiskIndex -Value $Partition.DiskIndex
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name PartitionSizeBytes -Value $Partition.Size
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name StartingOffset -Value $Partition.StartingOffset
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name PartitionType -Value $Partition.Type
$Drive = Get-CimAssociatedInstance -CimInstance $Partition -ResultClassName Win32_DiskDrive -Verbose:$false
"Drive.DeviceID = '$($Drive.DeviceID)'." | Write-Verbose
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DrivePartitionCount -Value $Drive.Partitions
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DriveBytesPerSector -Value $Drive.BytesPerSector
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DriveDeviceID -Value $Drive.DeviceID
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name InterfaceType -Value $Drive.InterfaceType
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DriveFirmwareRevision -Value $Drive.FirmwareRevision
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DriveModel -Value $Drive.Model
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DriveSizeBytes -Value $Drive.Size
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DriveStatus -Value $Drive.Status
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name DriveSerialNumber -Value $Drive.SerialNumber
$Volume = $Volumes = Get-CimInstance -ClassName Win32_Volume -Filter "DriveLetter='$($Disk.Name)'" -CimSession $CimSession -Verbose:$false
"Volume.Label = '$($Volume.Label)'." | Write-Verbose
'Running Defrag Analysis...' | Write-Verbose
$DefragStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$Report = Invoke-CimMethod -CimSession $CimSession -InputObject $Volume -MethodName DefragAnalysis -Verbose:$false
$DefragStopwatch.Stop()
"Defrag Analysis completed. Duration = $($DefragStopwatch.Elapsed.ToString()) [hh:mm:ss.ddd]." | Write-Verbose
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name AverageFileSize -Value $Report.DefragAnalysis.AverageFileSize
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name AverageFragmentsPerFile -Value $Report.DefragAnalysis.AverageFragmentsPerFile
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name AverageFreeSpacePerExtent -Value $Report.DefragAnalysis.AverageFreeSpacePerExtent
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name ClusterSize -Value $Report.DefragAnalysis.ClusterSize
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name VolumeFreeSpace -Value $Report.DefragAnalysis.FreeSpace
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name FreeSpacePercentFragmentation -Value $Report.DefragAnalysis.FreeSpacePercentFragmentation
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name LargestFreeSpaceExtent -Value $Report.DefragAnalysis.LargestFreeSpaceExtent
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name MFTPercentInUse -Value $Report.DefragAnalysis.MFTPercentInUse
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name MFTRecordCount -Value $Report.DefragAnalysis.MFTRecordCount
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name TotalExcessFragments -Value $Report.DefragAnalysis.TotalExcessFragments
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name TotalFiles -Value $Report.DefragAnalysis.TotalFiles
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name TotalFolders -Value $Report.DefragAnalysis.TotalFolders
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name TotalFragmentedFiles -Value $Report.DefragAnalysis.TotalFragmentedFiles
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name TotalFreeSpaceExtents -Value $Report.DefragAnalysis.TotalFreeSpaceExtents
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name TotalMFTFragments -Value $Report.DefragAnalysis.TotalMFTFragments
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name TotalMFTSize -Value $Report.DefragAnalysis.TotalMFTSize
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name TotalUnmovableFiles -Value $Report.DefragAnalysis.TotalUnmovableFiles
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name UsedSpace -Value $Report.DefragAnalysis.UsedSpace
Add-Member -InputObject $SqlDisk -MemberType NoteProperty -Name VolumeSize -Value $Report.DefragAnalysis.VolumeSize
$SqlDisks += $SqlDisk
}
}
End {
$Stopwatch.Stop()
"$($SqlDisks.Count) disks found on the computer. Duration = $($Stopwatch.Elapsed.ToString()) [hh:mm:ss.ddd]." | Write-Verbose
$SqlDisks
}
} # Get-Disk()
### INVOKE ###
$MyDisks = Get-Disk -ComputerName '.' -Verbose
$MyDisks | Out-GridView
I use the DCOM protocol due to some restrictions in some network segments.
The usage of the CmdLet Get-CimAssociatedInstance makes associating the classes mush easier compared to associating the classes in WMI.
The execution of the method DefragAnalysis is different used from CIM than WMI. The use of the CmdLet Invoke-CimMethod took some time to get, but I find it much cleaner to use.
When the script is executed, the output could look like this:
DriveLetter : C:
DiskFreeSpaceBytes : 488987631616
DiskSizeBytes : 750153363456
FileSystem : NTFS
VolumeDirty : False
VolumeName : ********
VolumeSerialNumber : ********
PartitionDeviceID : Disk #0, Partition #0
BlockSizeBytes : 512
BlockCount : 1465143296
PartitionDiskIndex : 0
PartitionSizeBytes : 750153367552
StartingOffset : 1048576
PartitionType : Installable File System
DrivePartitionCount : 1
DriveBytesPerSector : 512
DriveDeviceID : \\.\PHYSICALDRIVE0
InterfaceType : IDE
DriveFirmwareRevision : EXT0
DriveModel : Samsung SSD 840 EVO 750G
DriveSizeBytes : 750153761280
DriveStatus : OK
DriveSerialNumber : ********
AverageFileSize : 142
AverageFragmentsPerFile : 1,15
AverageFreeSpacePerExtent : 6483968
ClusterSize : 4096
VolumeFreeSpace : 489014767616
FreeSpacePercentFragmentation : 31
LargestFreeSpaceExtent : 334636171264
MFTPercentInUse : 100
MFTRecordCount : 473855
TotalExcessFragments : 54011
TotalFiles : 339026
TotalFolders : 76795
TotalFragmentedFiles : 12689
TotalFreeSpaceExtents : 75369
TotalMFTFragments : 2
TotalMFTSize : 485228544
TotalUnmovableFiles : 147
UsedSpace : 261138595840
VolumeSize : 750153363456
The verbose output shows some durations on the execution:VERBOSE: Get-Disk( '.' )
VERBOSE: Get Local Disks...
...
VERBOSE: Disk.DeviceID = 'C:'.
VERBOSE: Partition.DeviceID = 'Disk #0, Partition #0'.
VERBOSE: Drive.DeviceID = '\\.\PHYSICALDRIVE0'.
VERBOSE: Volume.Label = '********'.
VERBOSE: Running Defrag Analysis...
VERBOSE: Defrag Analysis completed. Duration = 00:00:56.9287395 [hh:mm:ss.ddd].
VERBOSE: 2 disks found on the computer. Duration = 00:00:59.5802508 [hh:mm:ss.ddd].
The script is not optimized for performance and do have a response time on some seconds. The response time increased especially when I added the defragmentation data.
Please notice that the classes Win32_Volume and Win32_DefragAnalysis are not available on Windows XP or earlier.
Reference
MSDN Library: Win32_DiskDrive, Win32_DiskPartition, Win32_LogicalDisk, Win32_Volume and „WMI Tasks: Disks and File Systems“.Richard Saddaway: "Defrag Analysis Part 2".
History
2010-12-22 First release of the script.2015-06-25 Second release of the script, now using CIM CmsLets.