Public/Diagnostics/Get-DiskPerformance.ps1

<#
Copyright © 2024 Integris. For internal company use only. All rights reserved.
#>


FUNCTION Get-DiskPerformance {
    <#
    .SYNOPSIS
    Performs disk performance benchmarking on specified drives.
 
    .DESCRIPTION
    This function benchmarks the performance of specified disk drives using the Microsoft DiskSpd tool. It measures random and sequential read/write speeds and provides a rating based on the results.
 
    .PARAMETER DriveLetter
    Specifies the drive letters to benchmark. Default is "C".
 
    .PARAMETER All
    If specified, benchmarks all available drives.
 
    .EXAMPLE
    Get-DiskPerformance -DriveLetter @("C", "D")
 
    This command benchmarks the performance of the C and D drives.
 
    .EXAMPLE
    Get-DiskPerformance -All
 
    This command benchmarks the performance of all available drives.
 
    .NOTES
    This function requires the DiskSpd tool to be installed in the specified directory and may need to be run with elevated privileges.
    #>


    [CmdletBinding(DefaultParameterSetName='Specific')]
    PARAM (
        [Parameter(ParameterSetName = 'Specific')]
        [String[]]$DriveLetter = @("C"),

        [Parameter(ParameterSetName = 'All')]
        [Switch]$All = $False
    ) 
            
    IF (!(Test-AdministratorElevation)) { RETURN }

    IF (!(Test-IntegrisFileDependency -RootPath "$ModuleUtilityDir\DiskSpd" -ChildPath "\x86\diskspd.exe" -DownloadURL "https://aka.ms/getdiskspd" -Unzip -Force)) { RETURN }
    
    IF ($All -eq $True) { $DriveLetter = (Get-Volume | Where-Object { $_.DriveLetter.Length -eq 1 }).DriveLetter }

    $Results = @()

    $ProgressCountCurrent = 0
    $ProgressCountTotal = $DriveLetter.Count * 4
    $ProgressPercent = "0"
    $TimeLeft = New-Timespan -Seconds ($ProgressCountTotal * 15)

    FOREACH ($DLetter in $DriveLetter) {
        $ProgressPercent = [MATH]::Round(($ProgressCountCurrent / $ProgressCountTotal * 100),0)
        Write-Progress -ID 15 -Activity "Total Progress" -Status "$($ProgressPercent)% - Time Left: ~$($TimeLeft.Hours):$($TimeLeft.Minutes.ToString().Padleft(2,"0")):$($TimeLeft.Seconds.ToString().Padleft(2,"0"))" -PercentComplete $ProgressPercent
        $ProgressCountCurrent++
        $TimeLeft = $TimeLeft - (New-TimeSpan -Seconds 15)

        IF ($null -eq (Get-Volume -DriveLetter $DLetter -ErrorAction SilentlyContinue)) { Write-Warning "Drive letter $($DLetter) not found."; RETURN }

        $ExeFolder = "$ModuleUtilityDir\DiskSpd\x86"
        $IOType = ""
        $AccessType = ""
        $ResultRating = 0
        $ResultRatingString = ""

        Write-Progress -ID 3 -Activity "Running Disk Benchmark Tests" -Status "0% - Random Read - About 15 Seconds" -PercentComplete 0

        $RandomTest = & "$ExeFolder\diskspd.exe" -b4k -d10 -t1 -O32 -r -W2 -C2 -w0 -Z -c1G -Su "$($DLetter):\Integris\PowerShell Module\Get-DiskPerformance\Temp\test.dat"
        $RandomTestResults = $RandomTest[-15] | convertfrom-csv -Delimiter "|" -Header Bytes,IO,Mib,IOPS,File | Select-Object IO,MIB,IOPs
        [int]$RandomMBs = $RandomTestResults.Mib
  
        IF ($RandomMBs -gt 200) { $ResultRating = 9; $ResultRatingString = "Crazy Fast" }
        ELSEIF ($RandomMBs -gt 100) { $ResultRating = 8; $ResultRatingString = "Extremely Fast" }
        ELSEIF ($RandomMBs -gt 60) { $ResultRating = 7; $ResultRatingString = "Very Fast" }
        ELSEIF ($RandomMBs -gt 30) { $ResultRating = 6; $ResultRatingString = "Fast" }
        ELSEIF ($RandomMBs -gt 15) { $ResultRating = 5; $ResultRatingString = "Good" }
        ELSEIF ($RandomMBs -gt 8) { $ResultRating = 4; $ResultRatingString = "Acceptable" }
        ELSEIF ($RandomMBs -gt 3) { $ResultRating = 3; $ResultRatingString = "Slow" }
        ELSEIF ($RandomMBs -gt 1) { $ResultRating = 2; $ResultRatingString = "VerySlow" }
        ELSE { $ResultRating = 1; $ResultRatingString = "Extremely Slow" }
    
        $IOType = "Random"
        $AccessType = "Read"
        $Results += New-Object PSObject -WarningAction SilentlyContinue -Property @{
            DriveLetter = $DLetter
            IOType = $IOType
            AccessType = $AccessType
            ResultMBs = [int]$RandomTestResults.Mib
            ResultIOPS = [int]$RandomTestResults.IOPS
            ResultRating = $ResultRating
            ResultRatingString = $ResultRatingString
        }   

        $ProgressPercent = [MATH]::Round(($ProgressCountCurrent / $ProgressCountTotal * 100),0)
        Write-Progress -ID 15 -Activity "Total Progress" -Status "$($ProgressPercent)% - Time Left: ~$($TimeLeft.Hours):$($TimeLeft.Minutes.ToString().Padleft(2,"0")):$($TimeLeft.Seconds.ToString().Padleft(2,"0"))" -PercentComplete $ProgressPercent
        $ProgressCountCurrent++
        $TimeLeft = $TimeLeft - (New-TimeSpan -Seconds 15)
        Write-Progress -ID 3 -Activity "Running Disk Benchmark Tests" -Status "25% - Random Write - About 15 Seconds" -PercentComplete 25

        $Random4kWrite = & "$ExeFolder\diskspd.exe" -b4k -d10 -t1 -O32 -r -W2 -C2 -w100 -Z -c1G -Su "$($DLetter):\Integris\PowerShell Module\Get-DiskPerformance\Temp\test.dat" 
        $Random4kWriteResults = $Random4kWrite[-15] | convertfrom-csv -Delimiter "|" -Header Bytes,IO,Mib,IOPS,File | Select-Object IO,MIB,IOPs

        [int]$RandomWriteMBs = $Random4kWriteResults.Mib
        
        IF ($RandomWriteMBs -gt 200) { $ResultRating = 9; $ResultRatingString = "Crazy Fast" }
        ELSEIF ($RandomWriteMBs -gt 100) { $ResultRating = 8; $ResultRatingString = "Extremely Fast" }
        ELSEIF ($RandomWriteMBs -gt 60) { $ResultRating = 7; $ResultRatingString = "Very Fast" }
        ELSEIF ($RandomWriteMBs -gt 30) { $ResultRating = 6; $ResultRatingString = "Fast" }
        ELSEIF ($RandomWriteMBs -gt 15) { $ResultRating = 5; $ResultRatingString = "Good" }
        ELSEIF ($RandomWriteMBs -gt 8) { $ResultRating = 4; $ResultRatingString = "Acceptable" }
        ELSEIF ($RandomWriteMBs -gt 3) { $ResultRating = 3; $ResultRatingString = "Slow" }
        ELSEIF ($RandomWriteMBs -gt 1) { $ResultRating = 2; $ResultRatingString = "VerySlow" }
        ELSE { $ResultRating = 1; $ResultRatingString = "Extremely Slow" }
    
        $IOType = "Random"
        $AccessType = "Write"
        $Results += New-Object PSObject -WarningAction SilentlyContinue -Property @{
            DriveLetter = $DLetter
            IOType = $IOType
            AccessType = $AccessType
            ResultMBs = [int]$Random4kWriteResults.Mib
            ResultIOPS = [int]$Random4kWriteResults.IOPS
            ResultRating = $ResultRating
            ResultRatingString = $ResultRatingString
        }
        
        $ProgressPercent = [MATH]::Round(($ProgressCountCurrent / $ProgressCountTotal * 100),0)
        Write-Progress -ID 15 -Activity "Total Progress" -Status "$($ProgressPercent)% - Time Left: ~$($TimeLeft.Hours):$($TimeLeft.Minutes.ToString().Padleft(2,"0")):$($TimeLeft.Seconds.ToString().Padleft(2,"0"))" -PercentComplete $ProgressPercent
        $ProgressCountCurrent++
        $TimeLeft = $TimeLeft - (New-TimeSpan -Seconds 15)
        Write-Progress -ID 3 -Activity "Running Disk Benchmark Tests" -Status "50% - Sequential Read - About 15 Seconds" -PercentComplete 50

        $Sequential1MBRead = & "$ExeFolder\diskspd.exe" -b1M -d10 -t1 -O8 -s -W2 -C2 -w0 -Z -c1G -Su "$($DLetter):\Integris\PowerShell Module\Get-DiskPerformance\Temp\test.dat"
        $Sequential1MBReadResults = $Sequential1MBRead[-8] | convertfrom-csv -Delimiter "|" -Header Bytes,IO,Mib,IOPS,File | Select-Object IO,MIB,IOPs
    
        [int]$SequentialReadMBs = $Sequential1MBReadResults.Mib

        IF ($SequentialReadMBs -gt 1500) { $ResultRating = 9; $ResultRatingString = "Crazy Fast" }
        ELSEIF ($SequentialReadMBs -gt 900) { $ResultRating = 8; $ResultRatingString = "Extremely Fast" }
        ELSEIF ($SequentialReadMBs -gt 450) { $ResultRating = 7; $ResultRatingString = "Very Fast" }
        ELSEIF ($SequentialReadMBs -gt 225) { $ResultRating = 6; $ResultRatingString = "Fast" }
        ELSEIF ($SequentialReadMBs -gt 150) { $ResultRating = 5; $ResultRatingString = "Good" }
        ELSEIF ($SequentialReadMBs -gt 90) { $ResultRating = 4; $ResultRatingString = "Acceptable" }
        ELSEIF ($SequentialReadMBs -gt 50) { $ResultRating = 3; $ResultRatingString = "Slow" }
        ELSEIF ($SequentialReadMBs -gt 25) { $ResultRating = 2; $ResultRatingString = "VerySlow" }
        ELSE { $ResultRating = 1; $ResultRatingString = "Extremely Slow" }
    
        $IOType = "Sequential"
        $AccessType = "Read"
        $Results += New-Object PSObject -WarningAction SilentlyContinue -Property @{
            DriveLetter = $DLetter
            IOType = $IOType
            AccessType = $AccessType
            ResultMBs = [int]$Sequential1MBReadResults.Mib
            ResultIOPS = [int]$Sequential1MBReadResults.IOPS
            ResultRating = $ResultRating
            ResultRatingString = $ResultRatingString
        }

        $ProgressPercent = [MATH]::Round(($ProgressCountCurrent / $ProgressCountTotal * 100),0)
        Write-Progress -ID 15 -Activity "Total Progress" -Status "$($ProgressPercent)% - Time Left: ~$($TimeLeft.Hours):$($TimeLeft.Minutes.ToString().Padleft(2,"0")):$($TimeLeft.Seconds.ToString().Padleft(2,"0"))" -PercentComplete $ProgressPercent
        $ProgressCountCurrent++
        $TimeLeft = $TimeLeft - (New-TimeSpan -Seconds 15)
        Write-Progress -ID 3 -Activity "Running Disk Benchmark Tests" -Status "75% - Sequential Write - About 15 Seconds" -PercentComplete 75

        ## Sequential Write
        $Sequential1MBWrite = & "$ExeFolder\diskspd.exe" -b1M -d10 -t1 -O8 -s -W2 -C2 -w100 -Z -c1G -Su "$($DLetter):\Integris\PowerShell Module\Get-DiskPerformance\Temp\test.dat"
        $Sequential1MBWriteResults = $Sequential1MBWrite[-15] | convertfrom-csv -Delimiter "|" -Header Bytes,IO,Mib,IOPS,File | Select-Object IO,MIB,IOPs
    
        [int]$SequentialWriteMBs = $Sequential1MBWriteResults.Mib

        IF ($SequentialWriteMBs -gt 1500) { $ResultRating = 9; $ResultRatingString = "Crazy Fast" }
        ELSEIF ($SequentialWriteMBs -gt 900) { $ResultRating = 8; $ResultRatingString = "Extremely Fast" }
        ELSEIF ($SequentialWriteMBs -gt 450) { $ResultRating = 7; $ResultRatingString = "Very Fast" }
        ELSEIF ($SequentialWriteMBs -gt 225) { $ResultRating = 6; $ResultRatingString = "Fast" }
        ELSEIF ($SequentialWriteMBs -gt 150) { $ResultRating = 5; $ResultRatingString = "Good" }
        ELSEIF ($SequentialWriteMBs -gt 90) { $ResultRating = 4; $ResultRatingString = "Acceptable" }
        ELSEIF ($SequentialWriteMBs -gt 50) { $ResultRating = 3; $ResultRatingString = "Slow" }
        ELSEIF ($SequentialWriteMBs -gt 25) { $ResultRating = 2; $ResultRatingString = "VerySlow" }
        ELSE { $ResultRating = 1; $ResultRatingString = "Extremely Slow" }
        Write-Progress -ID 3 -Activity "Running Disk Benchmark Tests" -Status "100% - Finalizing Results" -PercentComplete 100 -Completed
        $IOType = "Sequential"
        $AccessType = "Write"
        $Results += New-Object PSObject -WarningAction SilentlyContinue -Property @{
            DriveLetter = $DLetter
            IOType = $IOType
            AccessType = $AccessType
            ResultMBs = [int]$Sequential1MBWriteResults.Mib
            ResultIOPS = [int]$Sequential1MBWriteResults.IOPS
            ResultRating = $ResultRating
            ResultRatingString = $ResultRatingString
        }
    
        [string]$ExportPath = "$ModuleExportDir\Get-DiskPerformance\Results\" 
        MKDIR $ExportPath -ErrorAction SilentlyContinue | Out-Null
        $ExportPath += "$($DLetter) Volume"
        $ExportPath += " - "
        $ExportPath += "RR$($RandomMBs.ToString().PadLeft(4," "))"
        $ExportPath += " | "
        $ExportPath += "RW$($RandomWriteMBs.ToString().PadLeft(4," "))"
        $ExportPath += " | "
        $ExportPath += "SR$($SequentialReadMBs.ToString().PadLeft(5," "))"
        $ExportPath += " | "
        $ExportPath += "SW$($SequentialWriteMBs.ToString().PadLeft(5," "))"
        $ExportPath += " | "
        $ExportPath += (Get-Date).ToString("yyy_MM_dd HH_mm_ss")
        $ExportPath += ".csv"

        $Results | Select-Object DriveLetter, IOType, AccessType, ResultMBs, ResultRating, ResultRatingString | Export-CSV -Path $ExportPath -Force -NoTypeInformation
    }

    Write-Progress -ID 15 -Activity "Total Progress" -Status "100%" -PercentComplete 100
     
    RETURN $Results | Select-Object DriveLetter, IOType, AccessType, ResultMBs, ResultRating, ResultRatingString
}