Public/Search-FDALog.ps1
|
function Search-FDALog { <# .SYNOPSIS Search captured logs by level, time window, user, text, or raw KQL. .DESCRIPTION Runs against the curated tables. Either pass a structured filter set or supply -KQL for an arbitrary query. .PARAMETER Table Which curated table to search. Default: FDAInteractions. Use 'FDALogEvents' for generic / custom-level events, 'FDAExecutions' for downstream telemetry, 'FDAAuthEvents' for governance, 'FDACostMetering' for usage. .PARAMETER MinLevel Filter to entries at or above this level (built-in or custom). .PARAMETER MaxLevel Filter to entries at or below this level. .PARAMETER Last Time window expression accepted by KQL (1h, 24h, 7d, 30d, etc.). .PARAMETER UserPrincipalName Filter by UPN. .PARAMETER Contains Substring search across the primary text columns of the chosen table. .PARAMETER CorrelationId Exact match. .PARAMETER InteractionId Exact match. .PARAMETER Top Row cap. Default 100. .PARAMETER KQL Raw KQL to execute. Bypasses all other filters. .EXAMPLE Search-FDALog -MinLevel Warning -Last 1h .EXAMPLE Search-FDALog -Table FDAInteractions -UserPrincipalName 'p@m.com' -Contains 'revenue' .EXAMPLE Search-FDALog -KQL 'FDAInteractions | where Status == "Error" | top 10 by Timestamp desc' #> [CmdletBinding(DefaultParameterSetName = 'Filter')] param( [Parameter(ParameterSetName = 'Filter')] [ValidateSet('FDAInteractions', 'FDAExecutions', 'FDAAuthEvents', 'FDACostMetering', 'FDALogEvents')] [string] $Table = 'FDAInteractions', [Parameter(ParameterSetName = 'Filter')] [object] $MinLevel, [Parameter(ParameterSetName = 'Filter')] [object] $MaxLevel, [Parameter(ParameterSetName = 'Filter')] [string] $Last = '24h', [Parameter(ParameterSetName = 'Filter')] [string] $UserPrincipalName, [Parameter(ParameterSetName = 'Filter')] [string] $Contains, [Parameter(ParameterSetName = 'Filter')] [string] $CorrelationId, [Parameter(ParameterSetName = 'Filter')] [string] $InteractionId, [Parameter(ParameterSetName = 'Filter')] [int] $Top = 100, [Parameter(Mandatory, ParameterSetName = 'KQL')] [string] $KQL ) if (-not $script:FDAState.Connected) { throw 'Not connected. Call Connect-FDAObservability first.' } if ($PSCmdlet.ParameterSetName -eq 'KQL') { return Invoke-KQLQuery -Query $KQL } $clauses = @() if ($Last) { $clauses += "Timestamp > ago($Last)" } if ($MinLevel) { $lvl = Resolve-LogLevel -Level $MinLevel $clauses += "LevelNumeric >= $($lvl.Numeric)" } if ($MaxLevel) { $lvl = Resolve-LogLevel -Level $MaxLevel $clauses += "LevelNumeric <= $($lvl.Numeric)" } if ($UserPrincipalName) { $upn = $UserPrincipalName.Replace("'", "''") $clauses += "UserPrincipalName =~ '$upn'" } if ($CorrelationId) { $c = $CorrelationId.Replace("'", "''") $clauses += "CorrelationId == '$c'" } if ($InteractionId -and $Table -in 'FDAInteractions', 'FDAExecutions', 'FDACostMetering') { $c = $InteractionId.Replace("'", "''") $clauses += "InteractionId == '$c'" } if ($Contains) { $needle = $Contains.Replace("'", "''") switch ($Table) { 'FDAInteractions' { $clauses += "(Question contains_cs '$needle' or GeneratedDAX contains_cs '$needle' or Answer contains_cs '$needle')" } 'FDAExecutions' { $clauses += "ExecutedDAX contains_cs '$needle'" } 'FDAAuthEvents' { $clauses += "(UserPrincipalName contains_cs '$needle' or ClientApp contains_cs '$needle')" } 'FDACostMetering' { $clauses += "UserPrincipalName contains_cs '$needle'" } 'FDALogEvents' { $clauses += "Message contains_cs '$needle'" } } } $where = if ($clauses.Count -gt 0) { 'where ' + ($clauses -join ' and ') } else { '' } $kql = "$Table | $where | top $Top by Timestamp desc" Invoke-KQLQuery -Query $kql } |