|
| 1 | +//====================================================================== |
| 2 | +// VM Disk Performance & Capacity Report |
| 3 | +// Scope: All subscriptions selected in the Log Analytics workspace |
| 4 | +// Shows correlated performance metrics at the moment of peak read |
| 5 | +// and peak write throughput for each drive on each VM |
| 6 | +// |
| 7 | +// COLUMNS: |
| 8 | +// PW_ prefix = correlated value at peak WRITE time |
| 9 | +// PR_ prefix = correlated value at peak READ time |
| 10 | +// Null values = metric sample didn't align at that exact timestamp |
| 11 | +//====================================================================== |
| 12 | +let baseData = InsightsMetrics // All perf metrics, filtered to real drives |
| 13 | +| where Namespace == "LogicalDisk" and Name in ("ReadBytesPerSecond", "WriteBytesPerSecond", "ReadsPerSecond", "WritesPerSecond", "ReadLatencyMs", "WriteLatencyMs") |
| 14 | +| extend DiskDetails = parse_json(Tags) |
| 15 | +| extend Drive = tostring(DiskDetails["vm.azm.ms/mountId"]) |
| 16 | +| where Drive !in ("", "/mnt", "/mnt/resource") |
| 17 | +| where Drive !startswith "/snap/" |
| 18 | +| where Drive !startswith "/boot" |
| 19 | +| where Drive !startswith "/sys/"; |
| 20 | +let driveInfo = InsightsMetrics // Capacity data (size, used, free, pct used) |
| 21 | +| where Namespace == "LogicalDisk" and Name == "FreeSpaceMB" |
| 22 | +| extend DiskDetails = parse_json(Tags) |
| 23 | +| extend Drive = tostring(DiskDetails["vm.azm.ms/mountId"]) |
| 24 | +| extend DiskSizeMB = todecimal(DiskDetails["vm.azm.ms/diskSizeMB"]) |
| 25 | +| where Drive !in ("", "/mnt", "/mnt/resource") |
| 26 | +| where Drive !startswith "/snap/" |
| 27 | +| where Drive !startswith "/boot" |
| 28 | +| where Drive !startswith "/sys/" |
| 29 | +| summarize FreeSpaceMB = avg(Val), DiskSizeMB = max(DiskSizeMB) by Computer, Drive, _ResourceId |
| 30 | +| extend UsedSpaceMB = DiskSizeMB - FreeSpaceMB |
| 31 | +| extend FreeSpaceGB = round(FreeSpaceMB / 1024, 2) |
| 32 | +| extend UsedSpaceGB = round(UsedSpaceMB / 1024, 2) |
| 33 | +| extend DiskSizeGB = round(DiskSizeMB / 1024, 2) |
| 34 | +| extend PctUsed = round((UsedSpaceMB / DiskSizeMB) * 100, 1); |
| 35 | +let peakWrite = baseData // Timestamp and value of max write throughput |
| 36 | +| where Name == "WriteBytesPerSecond" |
| 37 | +| summarize arg_max(Val, TimeGenerated) by Computer, Drive, _ResourceId |
| 38 | +| project Computer, Drive, _ResourceId, PeakWriteTime = TimeGenerated, MaxWriteMBps = round(Val / 1048576, 2); |
| 39 | +let peakRead = baseData // Timestamp and value of max read throughput |
| 40 | +| where Name == "ReadBytesPerSecond" |
| 41 | +| summarize arg_max(Val, TimeGenerated) by Computer, Drive, _ResourceId |
| 42 | +| project Computer, Drive, _ResourceId, PeakReadTime = TimeGenerated, MaxReadMBps = round(Val / 1048576, 2); |
| 43 | +let allMetrics = baseData // Flattened lookup table for timestamp correlation |
| 44 | +| extend MBps = round(Val / 1048576, 2) |
| 45 | +| extend RawVal = Val |
| 46 | +| project Computer, Drive, _ResourceId, TimeGenerated, Name, MBps, RawVal; |
| 47 | +peakWrite // Assembly: join all blocks and correlate metrics at peak times |
| 48 | +| join kind=leftouter peakRead on Computer, Drive, _ResourceId |
| 49 | +| join kind=leftouter driveInfo on Computer, Drive, _ResourceId |
| 50 | +| extend SubscriptionId = tostring(split(_ResourceId, "/")[2]) |
| 51 | +| extend ResourceGroup = tostring(split(_ResourceId, "/")[4]) |
| 52 | +| extend DriveType = case( |
| 53 | + Drive == "/" or Drive == "C:", "OS", |
| 54 | + Drive == "D:" and DiskSizeGB <= 16, "Temp", |
| 55 | + "Data" |
| 56 | +) |
| 57 | +// Correlated metrics at PEAK WRITE time (PW_ prefix) |
| 58 | +| join kind=leftouter ( |
| 59 | + allMetrics | where Name == "ReadBytesPerSecond" |
| 60 | + | project Computer, Drive, _ResourceId, TimeGenerated, PW_ReadMBps = MBps |
| 61 | +) on Computer, Drive, _ResourceId, $left.PeakWriteTime == $right.TimeGenerated |
| 62 | +| join kind=leftouter ( |
| 63 | + allMetrics | where Name == "ReadsPerSecond" |
| 64 | + | project Computer, Drive, _ResourceId, TimeGenerated, PW_ReadIOPS = round(RawVal, 0) |
| 65 | +) on Computer, Drive, _ResourceId, $left.PeakWriteTime == $right.TimeGenerated |
| 66 | +| join kind=leftouter ( |
| 67 | + allMetrics | where Name == "WritesPerSecond" |
| 68 | + | project Computer, Drive, _ResourceId, TimeGenerated, PW_WriteIOPS = round(RawVal, 0) |
| 69 | +) on Computer, Drive, _ResourceId, $left.PeakWriteTime == $right.TimeGenerated |
| 70 | +| join kind=leftouter ( |
| 71 | + allMetrics | where Name == "ReadLatencyMs" |
| 72 | + | project Computer, Drive, _ResourceId, TimeGenerated, PW_ReadLatMs = round(RawVal, 2) |
| 73 | +) on Computer, Drive, _ResourceId, $left.PeakWriteTime == $right.TimeGenerated |
| 74 | +| join kind=leftouter ( |
| 75 | + allMetrics | where Name == "WriteLatencyMs" |
| 76 | + | project Computer, Drive, _ResourceId, TimeGenerated, PW_WriteLatMs = round(RawVal, 2) |
| 77 | +) on Computer, Drive, _ResourceId, $left.PeakWriteTime == $right.TimeGenerated |
| 78 | +// Correlated metrics at PEAK READ time (PR_ prefix) |
| 79 | +| join kind=leftouter ( |
| 80 | + allMetrics | where Name == "WriteBytesPerSecond" |
| 81 | + | project Computer, Drive, _ResourceId, TimeGenerated, PR_WriteMBps = MBps |
| 82 | +) on Computer, Drive, _ResourceId, $left.PeakReadTime == $right.TimeGenerated |
| 83 | +| join kind=leftouter ( |
| 84 | + allMetrics | where Name == "ReadsPerSecond" |
| 85 | + | project Computer, Drive, _ResourceId, TimeGenerated, PR_ReadIOPS = round(RawVal, 0) |
| 86 | +) on Computer, Drive, _ResourceId, $left.PeakReadTime == $right.TimeGenerated |
| 87 | +| join kind=leftouter ( |
| 88 | + allMetrics | where Name == "WritesPerSecond" |
| 89 | + | project Computer, Drive, _ResourceId, TimeGenerated, PR_WriteIOPS = round(RawVal, 0) |
| 90 | +) on Computer, Drive, _ResourceId, $left.PeakReadTime == $right.TimeGenerated |
| 91 | +| join kind=leftouter ( |
| 92 | + allMetrics | where Name == "ReadLatencyMs" |
| 93 | + | project Computer, Drive, _ResourceId, TimeGenerated, PR_ReadLatMs = round(RawVal, 2) |
| 94 | +) on Computer, Drive, _ResourceId, $left.PeakReadTime == $right.TimeGenerated |
| 95 | +| join kind=leftouter ( |
| 96 | + allMetrics | where Name == "WriteLatencyMs" |
| 97 | + | project Computer, Drive, _ResourceId, TimeGenerated, PR_WriteLatMs = round(RawVal, 2) |
| 98 | +) on Computer, Drive, _ResourceId, $left.PeakReadTime == $right.TimeGenerated |
| 99 | +// Output: Identity > Capacity > Peak Write snapshot > Peak Read snapshot |
| 100 | +| project SubscriptionId, ResourceGroup, Computer, Drive, DriveType, DiskSizeGB, UsedSpaceGB, FreeSpaceGB, PctUsed, |
| 101 | + PeakWriteTime, MaxWriteMBps, PW_ReadMBps, PW_WriteIOPS, PW_ReadIOPS, PW_WriteLatMs, PW_ReadLatMs, |
| 102 | + PeakReadTime, MaxReadMBps, PR_WriteMBps, PR_ReadIOPS, PR_WriteIOPS, PR_ReadLatMs, PR_WriteLatMs, |
| 103 | + _ResourceId |
| 104 | +| order by SubscriptionId asc, Computer asc, Drive asc |
0 commit comments