Private/Get-VMMetricSeries.ps1
|
function Get-VMMetricSeries { <# .SYNOPSIS Pulls a raw Azure Monitor platform-metric time series for one VM, paging the window into <=30-day chunks (the per-query limit) and honouring 429/Retry-After. .DESCRIPTION This is the only function that calls Get-AzMetric. It returns a flat list of datapoints { TimeStamp, Average, Minimum, Maximum } across the whole window so callers (Measure-Percentile) can aggregate without knowing about paging. Platform metrics are retained ~93 days at 1-minute granularity; a single query may span at most 30 days. We therefore walk the window in <=30-day slices. .PARAMETER ResourceId The full ARM resource id of the VM. .PARAMETER MetricName Platform metric name, e.g. 'Percentage CPU' or 'Available Memory Bytes'. .PARAMETER StartTime UTC start of the window (inclusive). .PARAMETER EndTime UTC end of the window (exclusive). .PARAMETER TimeGrain Aggregation grain as a TimeSpan, default 00:05:00 (PT5M). .PARAMETER MaxRetry Max retries on throttling (HTTP 429), default 5. .OUTPUTS [pscustomobject[]] with TimeStamp (datetime), Average, Minimum, Maximum (double?). #> [CmdletBinding()] [OutputType([System.Collections.Generic.List[pscustomobject]])] param( [Parameter(Mandatory)] [string] $ResourceId, [Parameter(Mandatory)] [string] $MetricName, [Parameter(Mandatory)] [datetime] $StartTime, [Parameter(Mandatory)] [datetime] $EndTime, [timespan] $TimeGrain = ([timespan]'00:05:00'), [int] $MaxRetry = 5 ) $points = [System.Collections.Generic.List[pscustomobject]]::new() $chunk = [timespan]::FromDays(30) $cursor = $StartTime while ($cursor -lt $EndTime) { $sliceEnd = $cursor + $chunk if ($sliceEnd -gt $EndTime) { $sliceEnd = $EndTime } $attempt = 0 while ($true) { try { $metric = Get-AzMetric -ResourceId $ResourceId -MetricName $MetricName ` -TimeGrain $TimeGrain -StartTime $cursor -EndTime $sliceEnd ` -AggregationType Average -WarningAction SilentlyContinue -ErrorAction Stop # Get-AzMetric returns one Metric object; Average requested but Min/Max # come free on the same datapoint objects when present. foreach ($m in $metric) { foreach ($dp in $m.Data) { # Skip empty buckets (no emission in that interval). if ($null -eq $dp.Average -and $null -eq $dp.Minimum -and $null -eq $dp.Maximum) { continue } $points.Add([pscustomobject]@{ TimeStamp = [datetime] $dp.TimeStamp Average = $dp.Average Minimum = $dp.Minimum Maximum = $dp.Maximum }) } } break } catch { $status = $null if ($_.Exception.Response) { $status = [int]$_.Exception.Response.StatusCode } if ($status -eq 429 -and $attempt -lt $MaxRetry) { $attempt++ $retryAfter = 5 * $attempt $hdr = $_.Exception.Response.Headers if ($hdr -and $hdr['Retry-After']) { [int]::TryParse([string]$hdr['Retry-After'], [ref]$retryAfter) | Out-Null } Write-Warning "Throttled (429) on '$MetricName' for $ResourceId; retry $attempt/$MaxRetry after ${retryAfter}s." Start-Sleep -Seconds $retryAfter continue } throw } } $cursor = $sliceEnd } return $points } |