public/Invoke-ZtAzureResourceGraphRequest.ps1
|
<#
.SYNOPSIS Queries Azure Resource Graph and returns results with automatic pagination. .DESCRIPTION Thin wrapper over Invoke-ZtAzureRequest that handles Azure Resource Graph's POST-based query endpoint and its $skipToken pagination model. Sends the KQL query to the ARG REST API, follows $skipToken across pages, and returns a flat array of result objects. Uses Invoke-ZtAzureRequest internally, so sovereign-cloud resolution (-Path) and error handling (throw on non-2xx) are inherited automatically. .PARAMETER Query The Kusto Query Language (KQL) query to execute against Azure Resource Graph. .PARAMETER SubscriptionId Optional array of subscription IDs to scope the query to. When omitted, the query runs at tenant scope (all accessible subscriptions). .PARAMETER ApiVersion The API version for the Resource Graph endpoint. Defaults to '2024-04-01'. .EXAMPLE Invoke-ZtAzureResourceGraphRequest -Query "Resources | summarize count() by type" Query all accessible resources at tenant scope, grouped by type. .EXAMPLE $subs = (Get-AzSubscription).Id Invoke-ZtAzureResourceGraphRequest -Query "Resources | where type =~ 'microsoft.compute/virtualmachines'" -SubscriptionId $subs Query virtual machines scoped to specific subscriptions. #> function Invoke-ZtAzureResourceGraphRequest { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [ValidateNotNullOrEmpty()] [string] $Query, [Parameter()] [string[]] $SubscriptionId, [Parameter()] [string] $ApiVersion = '2024-04-01', [Parameter()] [ValidateRange(1, 1000)] [int] $MaxPages = 100 ) process { $argPath = "/providers/Microsoft.ResourceGraph/resources?api-version=$ApiVersion" $pageSize = 1000 $pageCount = 0 $skipToken = $null $allResults = [System.Collections.Generic.List[psobject]]::new() do { #region Build request body $requestBody = @{ query = $Query options = @{ resultFormat = 'objectArray' '$top' = $pageSize } } if ($SubscriptionId) { $requestBody['subscriptions'] = @($SubscriptionId) } # $skipToken is the sole pagination cursor returned by ARG. # When present it supersedes $skip, so we only send $skip = 0 # on the very first request (implicitly, by omission on subsequent pages). if ($skipToken) { $requestBody.options['$skipToken'] = $skipToken } $bodyJson = $requestBody | ConvertTo-Json -Depth 10 #endregion Build request body #region Execute request # -DisablePaging: ARG uses $skipToken in the request body for pagination (handled by this loop), # not the standard ARM nextLink pattern that Invoke-ZtAzureRequest's -Paginate follows. $content = Invoke-ZtAzureRequest -Path $argPath -Method POST -Payload $bodyJson -DisablePaging # Accumulate results if ($content.data) { if ($content.data -is [array]) { $allResults.AddRange([psobject[]]$content.data) } else { $allResults.Add($content.data) } } #endregion Execute request #region Pagination $skipToken = $content.'$skipToken' $pageCount++ if ($pageCount -ge $MaxPages) { Write-PSFMessage "Azure Resource Graph pagination exceeded $MaxPages pages ($($allResults.Count) results so far). Stopping." -Level Warning break } #endregion Pagination } while ($skipToken) $allResults.ToArray() } } |