Functions/Performance/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
    )

    $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 1)
            {
                Write-Warning ('The Disk {0} Free Space is {1:0}MB falling below 1GB' -f $counterDisk.Name, ($counterDisk.Free * 1000))
            }
            $counterDiskFreePercent = $counterDisk.Free / $counterDisk.Size * 100
            if ($counterDiskFreePercent -lt 5)
            {
                Write-Warning ('The Disk {0} Free Space is {1:0.0}% falling below 5%' -f $counterDisk.Name, $counterDiskFreePercent)
            }
            if ($counterDisk.DiskTime -gt 80)
            {
                Write-Warning ('The Disk {0} Disk Time is {1:0.0} exceeding 80%' -f $counterDisk.Name, $counterDisk.DiskTime)
            }
            if ($counterDisk.DiskQueue -gt 2)
            {
                Write-Warning ('The Disk {0} Queue Length is {1:0} exceeding 2' -f $counterDisk.Name, $counterDisk.DiskQueue)
            }
            if ($counterDisk.AvgRead -gt 0.010)
            {
                Write-Warning ('The Disk {0} Average Millisecond per Read is {1:0}ms exceeding 10ms' -f $counterDisk.Name, ($counterDisk.AvgRead * 1000))
            }
            if ($counterDisk.AvgWrite -gt 0.010)
            {
                Write-Warning ('The Disk {0} Average Millisecond per Write is {1:0}ms exceeding 10ms' -f $counterDisk.Name, ($counterDisk.AvgWrite * 1000))
            }
        }
    }
    while ($Continue.IsPresent)
}