functions/Invoke-XdrXspmHuntingQuery.ps1
|
function Invoke-XdrXspmHuntingQuery { <# .SYNOPSIS Executes a hunting query against the Microsoft Defender XDR XSPM attack surface API. .DESCRIPTION Executes a custom hunting query against the XSPM (Extended Security Posture Management) attack surface API. Supports pagination through top and skip parameters. This function is designed to be used as a base for more specific attack surface queries. .PARAMETER Query The hunting query string to execute. Should follow XSPM query syntax. .PARAMETER Top The maximum number of records to return. Default is 100. .PARAMETER Skip The number of records to skip for pagination. Default is 0. .PARAMETER ScenarioName The scenario name to include in the request header (x-ms-scenario-name). This might be used for telemetry or logging purposes on the server side. Be careful out there. .PARAMETER Force Bypasses the cache and forces a fresh retrieval from the API. .EXAMPLE Invoke-XdrXspmHuntingQuery -Query "AttackPathsV2 | take 10" Executes a query to retrieve 10 attack paths. .EXAMPLE Invoke-XdrXspmHuntingQuery -Query "AttackPathsV2" -Top 50 -Skip 100 Executes a query with pagination, skipping the first 100 records and returning up to 50. .EXAMPLE Invoke-XdrXspmHuntingQuery -Query "AttackPathsV2 | where RiskLevel == 'High'" -Force Executes a filtered query, bypassing the cache. .EXAMPLE Invoke-XdrXspmHuntingQuery -Query "AttackPathsV2" -ScenarioName "CustomAnalysis" Executes a query with a custom scenario name in the request header. .OUTPUTS Object Returns the query results including totalRecords, count, skipToken, and data array. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Query, [Parameter()] [int]$Top = 0, [Parameter()] [int]$Skip = 0, [Parameter(Mandatory)] [string]$ScenarioName, [Parameter()] [switch]$Force ) begin { Update-XdrConnectionSettings } process { # Create cache key based on query and pagination parameters $cacheKeySuffix = "$($Query.GetHashCode())-$Top-$Skip" $cacheKey = "XdrXspmHuntingQuery-$cacheKeySuffix" try { $currentCacheValue = Get-XdrCache -CacheKey $cacheKey -ErrorAction SilentlyContinue } catch { Write-Verbose "Cache retrieval failed: $_" } if (-not $Force -and $currentCacheValue.NotValidAfter -gt (Get-Date)) { Write-Verbose "Using cached XSPM hunting query results" return $currentCacheValue.Value } elseif ($Force) { Write-Verbose "Force parameter specified, bypassing cache" Clear-XdrCache -CacheKey $cacheKey } else { Write-Verbose "XSPM hunting query cache is missing or expired" } # Build the request body $body = @{ query = $Query options = @{ top = $Top skip = $Skip } apiVersion = "v2" } $Uri = "https://security.microsoft.com/apiproxy/mtp/xspmatlas/attacksurface/query" Write-Verbose "Executing XSPM hunting query (Top: $Top, Skip: $Skip)" Write-Verbose "Query: $Query" # Get tenant context for X-Tid header $XdrTenantId = Get-XdrCache -CacheKey "XdrTenantId" -ErrorAction SilentlyContinue $tenantId = $XdrTenantId.Value # Build custom headers $customHeaders = $script:headers.Clone() if ($tenantId) { $customHeaders['X-Tid'] = $tenantId Write-Verbose "Added X-Tid header: $tenantId" } $customHeaders['x-ms-scenario-name'] = $ScenarioName Write-Verbose "Added x-ms-scenario-name header: $ScenarioName" try { $result = Invoke-RestMethod -Uri $Uri -Method Post -ContentType "application/json" -Body ($body | ConvertTo-Json -Depth 10) -WebSession $script:session -Headers $customHeaders Set-XdrCache -CacheKey $cacheKey -Value $result -TTLMinutes 30 return $result } catch { Write-Error "Failed to execute XSPM hunting query: $_" throw } } end { } } |