1 min read
Created on
Updated on

Microsoft Defender for Cloud - Create exemptions using REST API


Intro

This article is part of a series: Navigate to series page

In this post I show how to create a Defender for Cloud recommendation exemption (standard assignment with effect set to Exempt) by using REST API.

If you did not read part 1 yet, start there for authentication and for how to retrieve existing exemptions:

Microsoft Defender for Cloud - Get exemptions using REST API

Prepare variables

$displayName = "ckj-test-exemption-privileged-role-subscription-level"
$description = "Exemption of: Privileged roles should not have permanent access at the subscription and resource group level"
$identityObjectId = "12345678-1234-1234-ab12-12345678abcd" # Entra object ID for the user, group, or service principal you are exempting
$assessmentKey = "706b33f0-129e-4ed0-a179-f450b9ee4145" # Recommendation assessment key (maps to a specific Defender for Cloud recommendation)

If you do not have the Entra object ID, look it up for the target user, group, or service principal.
For assessmentKey, it is often easiest to inspect an existing exemption from part 1.

Deterministic assignment name helper

Using a deterministic GUID for standardAssignmentName is a good idea in automation.
It lets you recalculate the same assignment ID later for update/delete operations and helps avoid duplicate assignments caused by random GUID generation.

function New-DeterministicGuidFromText {
    param([Parameter(Mandatory)][string]$InputText)

    $bytes = [System.Text.Encoding]::UTF8.GetBytes($InputText)
    $hash = [System.Security.Cryptography.SHA256]::Create().ComputeHash($bytes)
    $hashHex = ($hash | ForEach-Object { $_.ToString("x2") }) -join ""
    return "{0}-{1}-{2}-{3}-{4}" -f $hashHex.Substring(0, 8), $hashHex.Substring(8, 4), $hashHex.Substring(12, 4), $hashHex.Substring(16, 4), $hashHex.Substring(20, 12)
}

Create the exemption

# For endpoint details, see:
# https://learn.microsoft.com/en-us/rest/api/defenderforcloud/standard-assignments/create?view=rest-defenderforcloud-2024-08-01
$apiVersion = "2024-08-01"

# Deterministic assignment name (same input = same GUID)
# Useful when you want to recalculate the same ID later for update/delete automation
$assignmentSeed = "$subscriptionId|$identityObjectId|$assessmentKey|$displayName"
$standardAssignmentName = New-DeterministicGuidFromText -InputText $assignmentSeed

# Resource scope where the exemption is created
# Here we use CloudPosture/securityentitydata and target the Entra object ID
$resourceScope = "/subscriptions/$subscriptionId/providers/Microsoft.Security/pricings/CloudPosture/securityentitydata/$identityObjectId"
# Full ARM endpoint for creating the standard assignment (exemption)
$uri = "https://management.azure.com/$resourceScope/providers/Microsoft.Security/standardAssignments/$standardAssignmentName?api-version=$apiVersion"

$body = @{
    properties = @{
        description = $description
        displayName = $displayName
        effect = "Exempt" # Exemption mode
        exemptionData = @{
            exemptionCategory = "Waiver" # Common values: Waiver or Mitigated
            assignedAssessment = @{
                assessmentKey = $assessmentKey # Recommendation to exempt
            }
        }
    }
}

$bodyJson = $body | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri $uri -Method Put -Headers $headers -Body $bodyJson

The output should look similar to this:

Create a time-bound exemption

In many cases, it is better to create exemptions with an expiry date, so they are reviewed automatically.

$displayName = "ckj-test-exemption-timebound"
$description = "Temporary exemption while remediation is in progress"
$identityObjectId = "12345678-1234-1234-ab12-12345678abcd"
$assessmentKey = "706b33f0-129e-4ed0-a179-f450b9ee4145"
$expiresOn = (Get-Date).ToUniversalTime().AddDays(30).ToString("o") # Expires in 30 days (UTC ISO 8601)

$apiVersion = "2024-08-01"

# Reuse deterministic naming so the assignment ID is reproducible
$assignmentSeed = "$subscriptionId|$identityObjectId|$assessmentKey|$displayName"
$standardAssignmentName = New-DeterministicGuidFromText -InputText $assignmentSeed
$resourceScope = "/subscriptions/$subscriptionId/providers/Microsoft.Security/pricings/CloudPosture/securityentitydata/$identityObjectId"
$uri = "https://management.azure.com/$resourceScope/providers/Microsoft.Security/standardAssignments/$standardAssignmentName?api-version=$apiVersion"

$body = @{
    properties = @{
        description = $description
        displayName = $displayName
        effect = "Exempt"
        expiresOn = $expiresOn # Time-bound exemption
        exemptionData = @{
            exemptionCategory = "Waiver"
            assignedAssessment = @{
                assessmentKey = $assessmentKey
            }
        }
    }
}

$bodyJson = $body | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri $uri -Method Put -Headers $headers -Body $bodyJson

You can adjust AddDays(30) to your preferred review cycle.

In the next post I show how to delete an existing exemption:

Microsoft Defender for Cloud - Delete exemptions using REST API