Microsoft Sentinel: How to Nuke a Noisy Incident Queue

Alright, class.
We've all had that moment. You log in on a Monday morning, a fresh coffee in hand, feeling a dangerous glimmer of hope for the week. You open the Sentinel incident queue, ready to hunt for the latest digital monsters.
And then you see it. 10k new incidents.
Your heart sinks. Your coffee suddenly tastes like disappointment. You know exactly what happened.
Maybe you inherited a Sentinel workspace that was "tested" and abandoned, leaving a graveyard of alerts. Or maybe a well-meaning teammate ...or a "smart" client who watched a 5-minute YouTube tutorial and created a new analytic rule that proceeded to have a complete meltdown. You see over 10,000 alerts all named "Sign-in (Potentially Malicious Source)," and you wish you could activate your Sharingan, look them in the eye over Teams, and gently suggest they never touch the analytics blade again.

But since that's an HR incident waiting to happen, we'll use a script to clean up their mess instead.

You're now staring at an unwinnable battle. The Sentinel UI only lets you close 50 incidents at a time. The thought of spending the rest of your day in a miserable cycle of "select all, close, next page, repeat" is enough to make you want to throw your laptop out the window.
You'd rather spend two days figuring out how to automate it than one day doing that.
Well, I've had that rage-quit moment. And I wrote the script. Today, I'm giving you the big red button to reclaim your queue and your sanity.
Why Manual Closing is a Soul-Crushing Trap
Let's be honest. This isn't about being lazy. It's about being efficient. You are a detective, a hunter, a digital forensics expert. Your brainpower is a valuable resource, meant for solving complex puzzles, not performing a clicking marathon. Every minute spent on this administrative nonsense is a minute a real attacker could be digging deeper into a network.
The Solution: A PowerShell Script for Total Annihilation
After spending some quality time with the Azure documentation and a heroic amount of coffee, I built a PowerShell script to do the dirty work. It connects to your workspace and acts as a programmable janitor, sweeping away the mess based on your exact instructions.
You can find the latest version of this script on my GitHub repo here.
Here's the code. We're going to break it down so you know exactly what your new superweapon does.
# Configuration
$config = @{
ResourceGroupName = "<Resource_Group_Name(Sentinel one)>"
WorkspaceName = "<Log_Analyitcs_Workspace_ID>"
SubscriptionId = "<Subscription ID>"
PageSize = 100
RetryAttempts = 2
DelayBetweenCalls = 2
CloseMode = "All" # Options: "All", "Title", "Severity"
TargetTitle = "<Insert Title of the Incident>"
TargetSeverity = "High" # Used if CloseMode = "Severity"
Verbose = $true
}
$totalClosed = 0
$failedIncidents = [System.Collections.Generic.List[string]]::new()
function Close-Incidents {
$continue = $true
$nextLink = $null
while ($continue) {
try {
$incidents = if ($nextLink) {
Get-AzSentinelIncident -NextLink $nextLink
} else {
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
}
$nextLink = $incidents.NextLink
foreach ($incident in $incidents) {
$shouldClose = switch ($config.CloseMode) {
"All" { $true }
"Title" { $incident.Title -eq $config.TargetTitle }
"Severity" { $incident.Severity -eq $config.TargetSeverity }
default { $false }
}
if (-not $shouldClose) { continue }
$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
if ($config.Verbose) { Write-Host "Closed incident: $($incident.Name) - $($incident.Title)" -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 so far: $totalClosed"
Start-Sleep -Seconds $config.DelayBetweenCalls
} catch {
Write-Host "Error processing batch: $_" -ForegroundColor Red
$continue = $false
}
}
}
# Main Execution
Write-Host "Starting incident closure process..." -ForegroundColor Cyan
Write-Host "Close Mode: $($config.CloseMode)" -ForegroundColor Yellow
if ($config.CloseMode -eq "Title") { Write-Host "Target Title: $($config.TargetTitle)" }
if ($config.CloseMode -eq "Severity") { Write-Host "Target Severity: $($config.TargetSeverity)" }
Close-Incidents
# Results
Write-Host @"
============================================
Incident 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
Deconstructing the Magic: Your Control Panel
1. The $config
Block: Your Mission Control
This block at the very top is your entire control panel. All the buttons and dials you need are right here, easy to find and change.
# Configuration
$config = @{
ResourceGroupName = "<Resource_Group_Name(Sentinel one)>"
WorkspaceName = "<Log_Analyitcs_Workspace_ID>"
SubscriptionId = "<Subscription ID>"
PageSize = 100
RetryAttempts = 2
DelayBetweenCalls = 2
CloseMode = "All" # Options: "All", "Title", "Severity"
TargetTitle = "<Insert Title of the Incident>"
TargetSeverity = "High" # Used if CloseMode = "Severity"
Verbose = $true
}
- The Basics (
ResourceGroupName
,WorkspaceName
,SubscriptionId
): Simple enough. You're just giving the script the coordinates to your Sentinel instance on the Azure map. - The Safety Levers (
PageSize
,RetryAttempts
,DelayBetweenCalls
): This is the smart part. Instead of trying to grab 10,000 incidents at once and giving the Azure API a heart attack, the script wisely asks for them in manageable chunks (PageSize
). It also knows (don't we all?) that sometimes Azure just... has a moment. If an API call fails, it will politely try again (RetryAttempts
) and will pause between batches (DelayBetweenCalls
) to stay under the radar. This is what makes the script reliable. - The Targeting System (
CloseMode
,TargetTitle
,TargetSeverity
): This is where you aim the weapon. You have three modes:"All"
: The "Nuke It From Orbit" option. Closes every single incident with a 'New' status. Satisfying, but use with caution."Title"
: The surgical strike. Perfect for cleaning up after that one noisy rule that went rogue. It only closes incidents with a title that exactly matches yourTargetTitle
."Severity"
: The broom. Let's you sweep away all the 'Low' or 'Informational' junk so you can focus on what matters.
2. The Close-Incidents
Function: The Engine Room
You don't need to change anything in here, but understanding it is what separates the students from the professors.
- The Loop: The script runs in a
while
loop, which basically means "keep going until the job is done." It uses a clever bit of logic withGet-AzSentinelIncident
and-NextLink
to process your incidents page by page, ensuring it can handle any number without breaking a sweat. - The Bouncer: For each incident, a
switch
statement checks it against theCloseMode
you chose. If the incident doesn't match the criteria, it's thrown out of the club and the script moves on. - The Closer: If an incident is on the list, the
Update-AzSentinelIncident
command is called. It changes the status toClosed
and, critically, uses-Confirm:$false
to tell PowerShell "Yes, I'm sure, now stop asking me." It also intelligently preserves the incident's original title and severity, just changing the status, which is great for the audit trail. - The Feedback Loop: The script gives you a running commentary, printing a satisfying line for every success and a scary red one for every failure.
3. The Execution and Results: The After-Action Report
The last part of the script is just good manners. It tells you what settings you're using before it starts and gives you a final, clean summary at the end: total incidents closed, how many failed, and their specific IDs so you can deal with the stubborn ones manually.
And that's it. You're not just running a script; you're wielding a well-engineered tool designed for a very specific, very annoying problem.
Let's Push the Button
(I'm recording a quick video of this so you can experience the pure joy of watching thousands of incidents vanish in real-time.)
- Configure: Pop open the script and edit the
$config
block with your environment details and your chosenCloseMode
. - Authenticate: Open Azure CLI
- Execute: Run the script.
- Watch the Carnage: Grab your coffee, lean back, and watch the green text scroll by. Each line is an incident you didn't have to click on. It's beautiful. I might have shed a tear the first time.
When the script is finished, it will give you a final score: total incidents closed and any that failed. Refresh your Sentinel queue and breathe in that fresh, clean, empty-inbox feeling.

You didn't just clean up a mess. You took a soul-crushing, day-ruining task and automated it into oblivion. That, class, is how you win.
Class dismissed.
