Public/Get-CustomDetectionIds.ps1
|
function Get-CustomDetectionIds { <# .SYNOPSIS Lists detection rule IDs with their description tags. .DESCRIPTION Queries Microsoft Graph API to retrieve all detection rules and returns their detection rule ID, the detector ID the API assigned, the display name, the UUID from the description tag (if present), and the tag prefix (if present). Results are cached for the duration specified by CacheTtlMinutes (default: 60 minutes). .PARAMETER CacheTtlMinutes How long (in minutes) to keep the cached result before re-querying the API. Defaults to 60 minutes. .PARAMETER Force Bypass the cache and force a fresh API call. .EXAMPLE Get-CustomDetectionIds Returns a list of detection rule IDs, display names and description tags (cached for 60 min). .EXAMPLE Get-CustomDetectionIds -Force Forces a fresh API call, ignoring any cached data. .EXAMPLE Get-CustomDetectionIds -CacheTtlMinutes 10 Returns the list, caching results for 10 minutes. .NOTES Requires the Microsoft.Graph.Authentication module and an active Graph API session. Use Connect-MgGraph before calling this function. #> [CmdletBinding()] [OutputType([PSCustomObject])] param ( [Parameter()] [ValidateRange(0, [int]::MaxValue)] [int]$CacheTtlMinutes = 60, [Parameter()] [switch]$Force ) begin { Assert-MgGraphConnection } process { # Return cached data if still valid $cacheValid = (-not $Force) -and ($null -ne $script:DetectionIdsCache.Data) -and ([datetime]::UtcNow -lt $script:DetectionIdsCache.ExpiresAt) if ($cacheValid) { Write-Verbose 'Returning cached detection IDs (use -Force to refresh).' return $script:DetectionIdsCache.Data } # After a timeout and a failed retry the list is not asked again for five minutes, so a run does not pay the timeout once per rule $failedAt = $script:DetectionIdsCache.FailedAt if (-not $Force -and $failedAt -and ([datetime]::UtcNow - $failedAt) -lt [timespan]::FromMinutes(5)) { throw "The rule list is unavailable since $($failedAt.ToString('HH:mm:ss')) UTC and is asked again five minutes later." } try { # Query the Microsoft Graph API with pagination support. The projection names the properties the lookups read $listUri = 'https://graph.microsoft.com/beta/security/rules/detectionRules?$select=id,detectorId,displayName,detectionAction' $retried = $false while ($true) { try { $allValues = [System.Collections.Generic.List[object]]::new() $uri = $listUri do { $response = Invoke-MgGraphRequestWithRetry -Method GET -Uri $uri if ($response.value) { $allValues.AddRange([object[]]$response.value) } $uri = $response.'@odata.nextLink' } while ($uri) break } catch { if (-not (Test-CustomDetectionListFailure -ErrorRecord $_)) { throw } if ($retried) { $script:DetectionIdsCache.FailedAt = [datetime]::UtcNow throw } $retried = $true Write-Warning 'The rule list timed out. It is asked once more.' } } $result = @($allValues | ForEach-Object { ConvertTo-CustomDetectionIdEntry -Rule $_ }) # Update the cache $script:DetectionIdsCache.Data = $result $script:DetectionIdsCache.ExpiresAt = [datetime]::UtcNow.AddMinutes($CacheTtlMinutes) $script:DetectionIdsCache.FailedAt = $null return $result } catch { Write-Error "Error querying Microsoft Graph API: $($_.Exception.Message)" throw } } } |