Private/Get-AutomationRules.ps1
|
function Get-AutomationRules { <# .SYNOPSIS Fetches Sentinel automation rules and extracts close-incident targeting hints. .OUTPUTS Array of PSCustomObject automation rules. #> [CmdletBinding()] param( [Parameter(Mandatory)][PSCustomObject]$Context ) $headers = @{ Authorization = "Bearer $($Context.ArmToken)" } $uri = "$(Get-LogHorizonEndpoint -Name Arm -Context $Context)$($Context.ResourceId)" + "/providers/Microsoft.SecurityInsights/automationRules?api-version=2025-09-01" $allRules = [System.Collections.Generic.List[object]]::new() $maxPages = 1000 $pageCount = 0 do { $pageCount++ $response = Invoke-AzRestWithRetry -Uri $uri -Headers $headers foreach ($rule in $response.value) { $allRules.Add($rule) } $uri = $response.nextLink if ($pageCount -ge $maxPages) { Write-Warning 'Pagination limit reached fetching automation rules. Stopping to avoid infinite loop.' break } } while ($uri) $normalized = foreach ($rule in $allRules) { $props = $rule.properties $actions = @($props.actions) $conditions = @($props.triggeringLogic.conditions) # Detect close action: check for Closed or Resolved status $closeAction = $actions | Where-Object { $_.actionType -eq 'ModifyProperties' -and $_.actionConfiguration -and ($_.actionConfiguration.status -eq 'Closed' -or $_.actionConfiguration.status -eq 'Resolved') } # Detect playbook action: automation rule triggers a playbook (may close incidents indirectly) $playbookAction = $actions | Where-Object { $_.actionType -eq 'RunPlaybook' } # Title conditions are kept as (Value, Operator) pairs so de-duplication never misaligns them $titleConditions = [System.Collections.Generic.List[object]]::new() $titleSeen = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) $ruleIdFilters = [System.Collections.Generic.List[string]]::new() $severityConditions = [System.Collections.Generic.List[object]]::new() $hasConditions = $conditions.Count -gt 0 # Recursively extract Property conditions (handles Boolean wrappers) $propertyConditions = [System.Collections.Generic.List[object]]::new() $condStack = [System.Collections.Generic.Stack[object]]::new() foreach ($c in $conditions) { $condStack.Push($c) } while ($condStack.Count -gt 0) { $cond = $condStack.Pop() $condType = "$($cond.conditionType)" if ($condType -eq 'Property') { [void]$propertyConditions.Add($cond) } elseif ($condType -eq 'Boolean' -and $cond.conditionProperties.innerConditions) { foreach ($inner in $cond.conditionProperties.innerConditions) { $condStack.Push($inner) } } } foreach ($cond in $propertyConditions) { $propertyName = "$($cond.conditionProperties.propertyName)" $operator = "$($cond.conditionProperties.operator)" # Title conditions if ($propertyName -match 'Title') { foreach ($item in @($cond.conditionProperties.propertyValues)) { if ([string]::IsNullOrWhiteSpace("$item")) { continue } if ($titleSeen.Add("$operator|$item")) { [void]$titleConditions.Add([PSCustomObject]@{ Value = "$item"; Operator = $operator }) } } } # Analytic rule ID conditions if ($propertyName -match 'AnalyticRuleIds') { foreach ($item in @($cond.conditionProperties.propertyValues)) { if (-not [string]::IsNullOrWhiteSpace("$item")) { [void]$ruleIdFilters.Add("$item") } } } # Severity conditions (Equals / NotEquals with one or more severities) if ($propertyName -match 'Severity') { $values = @(@($cond.conditionProperties.propertyValues) | Where-Object { -not [string]::IsNullOrWhiteSpace("$_") } | ForEach-Object { "$_" }) if ($values.Count -gt 0) { [void]$severityConditions.Add([PSCustomObject]@{ Values = $values; Operator = $(if ($operator) { $operator } else { 'Equals' }) }) } } } # The API exposes the enabled flag under triggeringLogic.isEnabled $isEnabled = if ($null -ne $props.triggeringLogic -and $null -ne $props.triggeringLogic.isEnabled) { [bool]$props.triggeringLogic.isEnabled } elseif ($null -ne $props.isEnabled) { [bool]$props.isEnabled } else { $false } [PSCustomObject]@{ AutomationRuleId = $rule.name DisplayName = $props.displayName Enabled = $isEnabled Order = [int]$props.order TriggersOn = $props.triggeringLogic.triggersOn TriggersWhen = $props.triggeringLogic.triggersWhen IsCloseIncidentRule = $null -ne $closeAction HasPlaybookAction = $null -ne $playbookAction HasConditions = $hasConditions TitleConditions = @($titleConditions) TitleFilters = @($titleConditions | ForEach-Object Value) TitleOperators = @($titleConditions | ForEach-Object Operator) RuleIdFilters = @($ruleIdFilters | Select-Object -Unique) SeverityConditions = @($severityConditions) Conditions = $conditions # Actions are projected: actionConfiguration can carry an assigned owner identity Actions = @($actions | ForEach-Object { [PSCustomObject]@{ actionType = $_.actionType; order = $_.order; status = $_.actionConfiguration.status } }) } } @($normalized) } |