Created
March 8, 2022 11:10
-
-
Save celloza/a14cc5dfdd1e25f7e1318face67f1c34 to your computer and use it in GitHub Desktop.
This script iterates over results from the Azure Management API for Microsoft Defender for Cloud Security alerts, and takes the relevant action (usually to dismiss). It was created in order to dismiss thousands of Adaptive Application Control policy violations while the policy was being tuned to no longer show false positives.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.Synopsis | |
Dismisses Microsoft Defender for Cloud Security Alerts in bulk. | |
.DESCRIPTION | |
This script iterates over results from the Azure Management API for Microsoft Defender for Cloud Security alerts, and takes the relevant action (usually to dismiss). It was | |
created in order to dismiss thousands of Adaptive Application Control policy violations while the policy was being tuned to no longer show false positives. | |
.INPUTS | |
Manually set in the script. | |
.OUTPUTS | |
Host output with the results of what has happened. | |
.NOTES | |
This process is not entirely automated. It requires you to get a token manually and inject it below, and also specify the subscription name on which you'd like to run this | |
process. Future development possibility is to create a service principal, assign it a token, and then reuse the token below rather than rely on getting it manually every x | |
couple of minutes. | |
#> | |
# Get a token by going to https://resources.azure.com/api/token?plaintext=true. This assumes you're logged in via Azure Portal | |
$token = '<insert token here>' | |
Connect-AzAccount | |
# Get all the subscriptions | |
$subs = Get-AzSubscription | |
# Get the subscription Id for the sub you want | |
$sub = $subs | Where-Object {$_.Name -eq '<insert sub name here>'} | |
$subId = $sub.Id | |
# Set the URL we're going iteratively call to go through all the alerts | |
$getAlertsUri = "https://management.azure.com/subscriptions/$subId/providers/Microsoft.Security/alerts?api-version=2021-01-01" | |
# Set the URL we're going to call to dismiss an alert | |
$dismissAlertUri = "https://management.azure.com/subscriptions/$subId/providers/Microsoft.Security/locations/{ascLocation}/alerts/{alertName}/dismiss?api-version=2021-01-01" | |
# Set the headers for this call | |
$Headers = @{"Authorization" = "Bearer $token"} | |
# The alertType we want to dismiss. This can be found by querying a sample set, and checking the alertType value | |
# Windows: VM_AdaptiveApplicationControlWindowsViolationAudited | |
# Linux: VM_AdaptiveApplicationControlLinuxViolationAudited | |
# For extra kudos, modify the script to accept n number of alertTypes, and dismiss them all | |
$alertTypeToDismiss = 'VM_AdaptiveApplicationControlLinuxViolationAudited' | |
$dismissedCount = 0 | |
# Loop through pages | |
while ($null -ne $getAlertsUri) { | |
try { | |
# Get the first page of results | |
$result = Invoke-RestMethod -Uri $getAlertsUri -Method Get -Headers $Headers | |
$allAlerts = $result.value | |
# Set the next call's URI to the nextLink property from the results (if it exists) | |
$getAlertsUri = $result.nextLink | |
$totalAlerts = $allAlerts.Count | |
Write-Host "Query returned $totalAlerts rows." -ForegroundColor Blue | |
# Filter only those alerts we want to dismiss, i.e. Active alerts for the specified alertType | |
$alertsToDismiss = $result.value | Where-Object {$_.properties.alertType -eq $alertTypeToDismiss -and $_.properties.status -eq 'Active'} | |
$resultCount = $alertsToDismiss.Count | |
Write-Host "Dismissing $resultCount where the alertType is $alertTypeToDismiss..." -ForegroundColor Green | |
foreach($alertToDismiss in $alertsToDismiss) | |
{ | |
$alertName = $alertToDismiss.name | |
Write-Host "Dismissing $alertName..." | |
# Find the location from the alertUri... dunno why it isn't a property | |
$location = $alertToDismiss.properties.alertUri.Split('/')[-1] | |
$dismissAlertUriForThisAlert = $dismissAlertUri.Replace("{alertName}", $alertToDismiss.Name).Replace("{ascLocation}", $location) | |
try { | |
# Dismiss the alert | |
Invoke-RestMethod -Uri $dismissAlertUriForThisAlert -Method Post -Headers $Headers | |
$dismissedCount++ | |
} | |
catch { | |
Write-Host "Couldn't dismiss $alertName." -ForegroundColor Red | |
} | |
} | |
} | |
catch { | |
Write-Host "Error while processing: " $Error[0] -ForegroundColor Red | |
exit | |
} | |
} | |
$subName = $sub.Name | |
Write-Host "Dismissed $dismissedCount alerts of type $alertTypeToDismiss for subscription $subName." -ForegroundColor Green |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Does this still work? For me, $allAlerts returns a list of values and they don't contain any alert type:
And there isn't even a properties.status method. For example, this doesn't return anything:
$result.value | where {$_.properties.status -eq "*"}
Perhaps it's because I'm accessing the subs via Azure Lighthouse? Does the token need to use an account that has direct access to the Azure tenant?
UPDATE:
Oh NVM, it does work with Azure Lighthouse delegated subs.
$result.value | fl
will show a list of properties