Forum Discussion

pecific147's avatar
pecific147
Copper Contributor
Dec 08, 2022

Bulk Closure of old Incidents via PowerShell

Hi All, 

 

I am trying to close all MS Sentinel incidents via PowerShell using below script.

 

Get-AzSentinelIncident -WorkspaceName "XXXXXX_XXXXXX" -All | Where-Object {$_.status -eq "New"} | ForEach-Object {update-AzSentinelIncident  -WorkspaceName "XXXXXXXXXXXXXXXX" -CaseNumber $_.CaseNumber -Status Closed -CloseReason FalsePositive -ClosedReasonText "Bulk Closure " -Confirm:$false}

 

This works fine for incidents, triggered in last 48 hr.  For older incident (older than 48 hr) it is giving below error.


Update-AzSentinelIncident: Unable to update Incident 7833 with error message Response status code does not indicate success: 500 (Internal Server Error).

 

Please help me over here, as I need to close over 8k old incidents.

 

Rod Trent 

7 Replies

  • ITProfessor's avatar
    ITProfessor
    Brass Contributor

     

    Features

    • 🚀 Bulk closure of all open incidents regardless of severity
    • ⏱️ Pagination handling for reliable processing of large volumes
    • 🔄 Automatic retries for failed operations
    • 📊 Detailed reporting with success/failure counts
    • ⚙️ Configurable parameters for batch size and delays

     

    <#
    .SYNOPSIS
    Bulk closes ALL Microsoft Sentinel incidents (all severities) without manual intervention.
    .DESCRIPTION
    Closes all open incidents in batches with automatic retries and proper pagination handling.
    #>
    
    # Configuration
    $config = @{
        ResourceGroupName = "<resource_group_name"
        WorkspaceName    = "<log_analytic_workspace_name>"
        SubscriptionId   = "<subscription_id>"
        PageSize         = 100            # Reduced for better pagination handling
        RetryAttempts    = 2              # Retries per incident
        DelayBetweenCalls = 2             # Seconds between API calls
    }
    
    # Initialize counters
    $totalClosed = 0
    $failedIncidents = [System.Collections.Generic.List[string]]::new()
    
    function Close-AllIncidents {
        $continue = $true
        $nextLink = $null
        
        while ($continue) {
            try {
                # Get incidents with proper pagination handling
                if ($nextLink) {
                    $incidents = Get-AzSentinelIncident -NextLink $nextLink
                }
                else {
                    $incidents = Get-AzSentinelIncident -ResourceGroupName $config.ResourceGroupName `
                                                      -WorkspaceName $config.WorkspaceName `
                                                      -Filter "properties/status eq 'New'" `
                                                      -Top $config.PageSize
                }
    
                if (-not $incidents -or $incidents.Count -eq 0) { 
                    $continue = $false
                    break 
                }
    
                # Store nextLink for pagination
                $nextLink = $incidents.NextLink
    
                # Process incidents
                foreach ($incident in $incidents) {
                    $retryCount = 0
                    $closed = $false
    
                    while ($retryCount -lt $config.RetryAttempts -and -not $closed) {
                        try {
                            Update-AzSentinelIncident -Id $incident.Name `
                                -ResourceGroupName $config.ResourceGroupName `
                                -WorkspaceName $config.WorkspaceName `
                                -SubscriptionId $config.SubscriptionId `
                                -Status Closed `
                                -Confirm:$false `
                                -Severity $incident.Severity `
                                -Classification Undetermined `
                                -Title $incident.Title `
                                -ErrorAction Stop
                            
                            $script:totalClosed++
                            $closed = $true
                            Write-Host "Closed incident: $($incident.Name)" -ForegroundColor Green
                        } catch {
                            $retryCount++
                            if ($retryCount -ge $config.RetryAttempts) {
                                $script:failedIncidents.Add($incident.Name)
                                Write-Host "Failed to close incident: $($incident.Name) - $($_.Exception.Message)" -ForegroundColor Red
                            }
                            Start-Sleep -Milliseconds 500
                        }
                    }
                }
    
                Write-Host "Processed $($incidents.Count) incidents | Total closed: $totalClosed"
                
                # Add delay between API calls
                Start-Sleep -Seconds $config.DelayBetweenCalls
    
            } catch {
                Write-Host "Error processing batch: $_" -ForegroundColor Red
                $continue = $false
            }
        }
    }
    
    # Main execution
    Write-Host "Starting bulk incident closure process..." -ForegroundColor Cyan
    Write-Host "Configuration:"
    Write-Host "- Page Size: $($config.PageSize)"
    Write-Host "- Retry Attempts: $($config.RetryAttempts)"
    Write-Host "- Delay Between Calls: $($config.DelayBetweenCalls)s"
    Write-Host ""
    
    Close-AllIncidents
    
    # Results
    Write-Host @"
    ============================================
    Bulk closure process completed
    Total incidents closed: $totalClosed
    Failed to close: $($failedIncidents.Count)
    ============================================
    "@ -ForegroundColor Cyan
    
    if ($failedIncidents.Count -gt 0) {
        Write-Host "Failed incident IDs:" -ForegroundColor Yellow
        $failedIncidents | ForEach-Object { Write-Host "- $_" }
    }
    
    Write-Host "Process completed at $(Get-Date)" -ForegroundColor Cyan

     

  • Ari_Kouhia's avatar
    Ari_Kouhia
    Copper Contributor

    Hey guys. Thanks pecific147 for your initial starting point and vezgeta for PS frame which I stole with pride.

    I took it and polished it and now it takes away, with time, some tens of thousands of alerts.

    First, longer has some error maneuvering that is needed if you go beyond 10k.

    Latter is to kill just some thousands. Note that you need to change the -Severity Informational to High/Medium/Low/Informational depending which category you are closing.

    # Set your parameters once up-front
    $rg  = "Your_rg_here"
    $ws  = "Your-workspace-here"
    $sub = "yoursubscriptionidgoeshere"

    Get-AzSentinelIncident -ResourceGroupName "$rg" -WorkspaceName "$ws" |
    Where-Object { $_.Status -eq "New" } |
    ForEach-Object {
        try {
            Update-AzSentinelIncident -Id $_.Name -ResourceGroupName "$rg" -WorkspaceName "$ws" -SubscriptionId "$sub" -Status Closed -Confirm:$false -Severity Informational -Classification Undetermined -Title $_.Title
        } catch {
            Write-Host "Failed to update incident $($_.Name). Skipping." -ForegroundColor Red
        }
        Start-Sleep -Milliseconds 200
    }

     

    ------------------------------------------------------------

    #Get-AzSentinelIncident -ResourceGroupName "$rg" -WorkspaceName "$ws" | Where-Object {$_.Status -eq "New"} | ForEach-Object {Update-AzSentinelIncident -Id $_.Name -ResourceGroupName "$rg" -WorkspaceName "$ws" -SubscriptionId "$sub" -Status Closed -Confirm:$false -Severity Low -Classification Undetermined -Title $_.title}

    ----------------------------------------------------------------

    Even this is late reply I hope this helps someone else.

     

    Gary

  • GBushey's avatar
    GBushey
    Iron Contributor
    Like Rod mentioned, it may be the total amount of rules you are trying to work with that is causing the issue. I have not looked at the code for "Get-AzSentinelIncident" but the REST API only returns 50 items at one time by default. You could call the REST API directly and then use the "nextLink" that gets returned as the "skipToken" for the next call and iterate through your 8K incidents that way. https://learn.microsoft.com/en-us/rest/api/securityinsights/stable/incidents/list?tabs=HTTP
    • vezgeta's avatar
      vezgeta
      Copper Contributor

      GBushey I have closed around 14K of incidents because of misconfigured analytic rule. It took some time and also I have modified the search parametar to close specific incidents with similar name.  Just replace YYY with the similar name of incident.

       

      Get-AzSentinelIncident -ResourceGroupName "xxxx" -WorkspaceName "xxxx" | Where-Object {$_.Status -eq "New"} | Where-Object {$_.title -like '*YYY*'} | ForEach-Object {Update-AzSentinelIncident -Id $_.Name -ResourceGroupName "xxxx" -WorkspaceName "xxxx" -SubscriptionId "xxxx" -Status Closed -Confirm:$false -Severity Medium -Classification Undetermined -Title $_.title}

  • vezgeta's avatar
    vezgeta
    Copper Contributor
    Run this command to close incidents (replace XXXX with needed information):
    Get-AzSentinelIncident -ResourceGroupName "xxxx" -WorkspaceName "xxxx" | Where-Object {$_.Status -eq "New"} | ForEach-Object {Update-AzSentinelIncident -Id $_.Name -ResourceGroupName "xxxx" -WorkspaceName "xxxx" -SubscriptionId "xxxx" -Status Closed -Confirm:$false -Severity Medium -Classification Undetermined -Title $_.title}
  • Hi pecific147 

     

    It might be the actual number of Incidents that are the problem versus the time range. Let me do some digging around here to find out.

     

    I understand those Incidents existing in the workspace can be annoying, but they will expire from the workspace based on your retention setting.

     

     

    • pecific147's avatar
      pecific147
      Copper Contributor
      Hi Rod_Trent,

      Did you find anything,?
      Workspace retention is 365 days, so waiting for retention to expire won't work.

Resources