Functions/Troubleshooting/Measure-Storage.ps1

<#
    .SYNOPSIS
        Get the current storage usage on the local system.
 
    .DESCRIPTION
        Use WMI/CIM and the Windows Performance Counters to retrieve the current
        storage usage on the local system.
        - Size
          The volume size in GB.
        - Free
          The free size in GB.
        - Usage
          Current disk activity time in percent.
        - Queue
          Current disk queue length.
        - AvgRead
          Average duration in seconds for read operations.
        - AvgWrite
          Average duration in seconds for write operations.
        - IOPS
          Number of read and write operations.
 
    .EXAMPLE
        PS C:\> storage
        Use the alias of Measure-Storage to show the current memory usage.
#>

function Measure-Storage
{
    [CmdletBinding()]
    [Alias('storage')]
    param
    (
        # Flag to continue showing the memory every second.
        [Parameter(Mandatory = $false)]
        [Alias('c')]
        [Switch]
        $Continue,

        # Threshold for the disk free space in giga bytes. If the threshold is
        # reached, a warning message will be shown.
        [Parameter(Mandatory = $false)]
        [System.Int32]
        $DiskFreeGigaByteThreshold = 5,

        # Threshold for the disk free space in percent. If the threshold is
        # reached, a warning message will be shown.
        [Parameter(Mandatory = $false)]
        [System.Int32]
        $DiskFreePercentThreshold = 5,

        # Threshold for the disk time in percent. If the threshold is reached, a
        # warning message will be shown.
        [Parameter(Mandatory = $false)]
        [System.Int32]
        $DiskTimePercentThreshold = 80,

        # Threshold for the disk queue length. If the threshold is reached, a
        # warning message will be shown.
        [Parameter(Mandatory = $false)]
        [System.Int32]
        $DiskQueueLengthThreshold = 2,

        # Threshold for the average read time in seconds. If the threshold is
        # reached, a warning message will be shown.
        [Parameter(Mandatory = $false)]
        [System.Int32]
        $AvgReadTimeMillisecondThreshold = 10,

        # Threshold for the average write time in seconds. If the threshold is
        # reached, a warning message will be shown.
        [Parameter(Mandatory = $false)]
        [System.Int32]
        $AvgWriteTimeMillisecondThreshold = 10
    )

    $counterNames = '\PhysicalDisk(*)\% Disk Time',
                    '\PhysicalDisk(*)\Avg. Disk sec/Read',
                    '\PhysicalDisk(*)\Avg. Disk sec/Write',
                    '\PhysicalDisk(*)\Disk Transfers/sec',
                    '\PhysicalDisk(*)\Current Disk Queue Length'

    # Gather all volumes, partitions and disks.
    $volumes = Get-CimInstance -ClassName 'Win32_Volume' | Where-Object { $_.Name -like '?:\*' -and $_.DriveType -ne 5 } | Select-Object 'Name', 'DeviceID', 'DriveLetter', 'FreeSpace', 'Capacity'
    $partitions = Get-CimInstance -ClassName 'MSFT_Partition' -Namespace 'Root/Microsoft/Windows/Storage' | Select-Object 'DiskNumber', 'AccessPaths'

    do
    {
        # Counters new for every run.
        $perfCounterStorage = Get-Counter -Counter $counterNames -SampleInterval 1 -MaxSamples 1

        $timestamp = Get-Date

        # Iterating all volumes, get the matching partition and create a new
        # performance counter object.
        foreach ($volume in $volumes)
        {
            $partition = $partitions | Where-Object { $_.AccessPaths -contains $volume.DeviceID } | Select-Object -First 1

            $perfCounterStorageIdentifier = '{0}*{1}*' -f $partition.DiskNumber, $volume.DriveLetter

            $counterDisk = [PSCustomObject] @{
                PSTypeName = 'ProfileFever.Performance.Storage'
                Timestamp  = $timestamp
                Name       = '{0} {1}' -f $partition.DiskNumber, $volume.Name
                Size       = $volume.Capacity / 1GB
                Free       = $volume.FreeSpace / 1GB
                Used       = ($volume.Capacity - $volume.FreeSpace) / 1GB
                DiskTime   = $perfCounterStorage.CounterSamples.Where({$_.Path -like "\\*\PhysicalDisk($perfCounterStorageIdentifier)\% Disk Time"}).CookedValue
                DiskQueue  = $perfCounterStorage.CounterSamples.Where({$_.Path -like "\\*\PhysicalDisk($perfCounterStorageIdentifier)\Current Disk Queue Length"}).CookedValue
                AvgRead    = $perfCounterStorage.CounterSamples.Where({$_.Path -like "\\*\PhysicalDisk($perfCounterStorageIdentifier)\Avg. Disk sec/Read"}).CookedValue
                AvgWrite   = $perfCounterStorage.CounterSamples.Where({$_.Path -like "\\*\PhysicalDisk($perfCounterStorageIdentifier)\Avg. Disk sec/Write"}).CookedValue
                IOPS       = $perfCounterStorage.CounterSamples.Where({$_.Path -like "\\*\PhysicalDisk($perfCounterStorageIdentifier)\Disk Transfers/sec"}).CookedValue
            }
            Write-Output $counterDisk

            # Show warning messages if thresholds are reached.
            if ($counterDisk.Free -lt $DiskFreeGigaByteThreshold)
            {
                Write-Warning ('The Disk {0} Free Space is {1:0}MB falling below {2}GB' -f $counterDisk.Name, ($counterDisk.Free * 1000), $DiskFreeGigaByteThreshold)
            }
            $counterDiskFreePercent = $counterDisk.Free / $counterDisk.Size * 100
            if ($counterDiskFreePercent -lt $DiskFreePercentThreshold)
            {
                Write-Warning ('The Disk {0} Free Space is {1:0.0}% falling below {2}%' -f $counterDisk.Name, $counterDiskFreePercent, $DiskFreePercentThreshold)
            }
            if ($counterDisk.DiskTime -gt $DiskTimePercentThreshold)
            {
                Write-Warning ('The Disk {0} Disk Time is {1:0.0} exceeding {2}%' -f $counterDisk.Name, $counterDisk.DiskTime, $DiskTimePercentThreshold)
            }
            if ($counterDisk.DiskQueue -gt $DiskQueueLengthThreshold)
            {
                Write-Warning ('The Disk {0} Queue Length is {1:0} exceeding {2}' -f $counterDisk.Name, $counterDisk.DiskQueue, $DiskQueueLengthThreshold)
            }
            $counterDiskAvgReadMillisecond = $counterDisk.AvgRead * 1000
            if ($counterDiskAvgReadMillisecond -gt $AvgReadTimeMillisecondThreshold)
            {
                Write-Warning ('The Disk {0} Average Millisecond per Read is {1:0}ms exceeding {2}ms' -f $counterDisk.Name, $counterDiskAvgReadMillisecond, $AvgReadTimeMillisecondThreshold)
            }
            $counterDiskAvgWriteMillisecond = $counterDisk.AvgWrite * 1000
            if ($counterDiskAvgWriteMillisecond -gt $AvgWriteTimeMillisecondThreshold)
            {
                Write-Warning ('The Disk {0} Average Millisecond per Write is {1:0}ms exceeding {2}ms' -f $counterDisk.Name, $counterDiskAvgWriteMillisecond, $AvgWriteTimeMillisecondThreshold)
            }
        }
    }
    while ($Continue.IsPresent)
}