Private/Invoke-AACLogQuery.ps1
|
function Invoke-AACLogQuery { <# .SYNOPSIS Runs a KQL query against a Log Analytics workspace or an Application Insights resource, and returns the rows as ordered hashtables. .DESCRIPTION Uses each service's documented query API, with a token for that API from the Connect-AAC sign-in (Get-AACAccessToken -Resource): Workspace POST https://api.loganalytics.azure.com/v1/workspaces/{workspace ID}/query Component POST https://api.applicationinsights.io/v1/apps/{app ID}/query -Id is the workspace ID (its customerId GUID) or the Application Insights app ID - Resolve-AACLogResource's QueryId. The body is { query, timespan }: -Timespan is an ISO 8601 duration ('PT2H') that bounds the query as well as any time filter inside it. Only the first result table is returned. Needs Log Analytics Reader (or Reader) on the workspace or resource. #> [CmdletBinding()] [OutputType([System.Collections.Specialized.OrderedDictionary])] param( [Parameter(Mandatory)] [ValidateSet('Workspace', 'Component')] [string] $Kind, [Parameter(Mandatory)] [AllowEmptyString()] [string] $Id, [Parameter(Mandatory)] [string] $Query, [string] $Timespan ) if (-not $Id) { throw "Azure Resource Graph returned no $(if ($Kind -eq 'Workspace') { 'workspace ID (customerId)' } else { 'app ID' }) for it, so it can't be queried." } $api = @{ Workspace = @{ Uri = "https://api.loganalytics.azure.com/v1/workspaces/$Id/query"; Resource = 'https://api.loganalytics.io' } Component = @{ Uri = "https://api.applicationinsights.io/v1/apps/$Id/query"; Resource = 'https://api.applicationinsights.io' } }[$Kind] $body = @{ query = $Query } if ($Timespan) { $body.timespan = $Timespan } $uri = $api.Uri Write-Verbose "POST $uri (timespan $Timespan)`n$Query" $response = Invoke-AACArmRequest -Method Post -Uri $uri -Resource $api.Resource -Body ($body | ConvertTo-Json -Depth 5) # A reply without result tables is not "no rows": say so rather than # return an empty result that looks like a quiet workspace. if (-not ($response -is [System.Collections.IDictionary] -and $response.Contains('tables') -and @($response['tables']).Count)) { $shown = if ($null -eq $response) { 'an empty reply' } else { ConvertTo-Json -InputObject $response -Depth 3 -Compress } if ($shown.Length -gt 300) { $shown = $shown.Substring(0, 300) + '...' } throw "The $Kind query API returned no result table ($shown)." } $table = @($response['tables'])[0] $columns = @($table['columns'] | ForEach-Object { [string]$_['name'] }) foreach ($row in @($table['rows'])) { $record = [ordered]@{} for ($i = 0; $i -lt $columns.Count; $i++) { $record[$columns[$i]] = $row[$i] } $record } } |