Toggle Azure AD Security Defaults on or off programmatically

Continuing our exploration of the different policies objects exposed under the /policies Graph API endpoint, in this article we will discuss how to get the current state of the Security defaults feature, and change it as needed. But first, a word or two about Security defaults.

The Security defaults feature is basically a set of pre-configured settings, intended to beef up the security of your organization. Apart from disabling basic authentication and forcing MFA for admins, it includes things such as mandatory MFA registration for users. While most enterprise customers have probably already configured all these settings, or are planning to, the biggest benefit of the feature is that it’s made available for free for all tenants, even those that are not licensed to use services such as Azure AD Identity Protection. The downside is that it lacks the customizability of the individual features, especially when compared to Conditional Access policies, and is basically an “all or nothing” toggle.

Because of the “all or nothing” approach, it’s not that uncommon for Security defaults to interfere with the normal work of users and/or admins, and there have been multiple issues reported on the different technical communities, all of which caused by the feature. While you can check the status of the feature and toggle it from the Azure AD blade, this UI-based approach is not always applicable. So, it might be useful if you have a way to programmatically report on or toggle Security defaults, which is what the /policies/identitySecurityDefaultsEnforcementPolicy endpoint enables.

Here’s how to report on the current status of the feature (note that the application you plan to use must have the Policy.Read.All and Policy.ReadWrite.ConditionalAccess scopes granted):

$tenantID = "" #your tenantID or tenant root domain
$appID = "12345678-1234-1234-1234-1234567890AB" #the GUID of your app. For best result, use app with Policy.Read.All and Policy.ReadWrite.ConditionalAccess scopes granted
$client_secret = "XXXXXXXXXXXXXXXxxxx" #client secret for the app

$body = @{
client_id     = $AppId
scope         = ""
client_secret = $client_secret
grant_type    = "client_credentials"

$authenticationResult = Invoke-WebRequest -Method Post -Uri "$tenantId/oauth2/v2.0/token" -ContentType "application/x-www-form-urlencoded" -Body $body -ErrorAction Stop
$token = ($authenticationResult.Content | ConvertFrom-Json).access_token
$authHeader = @{'Authorization'="Bearer $token"}

$res = Invoke-WebRequest -Headers $AuthHeader -Uri ""
$result = ($res.Content | ConvertFrom-Json)

@odata.context :$metadata#policies/identitySecurityDefaultsEnforcementPolicy/$entity
id             : 00000000-0000-0000-0000-000000000005
displayName    : Security Defaults
description    : Security defaults is a set of basic identity security mechanisms recommended by Microsoft. When enabled, these recommendations will be automatically enforced in your organization. Administrators and users will be better protected from common identity related attacks.
isEnabled      : False

In this case, the feature has been toggled Off, or wasn’t enabled at all because of already existing CA policies.  To toggle it On, we can use the following PATCH request (make sure that no CA policies that interfere with Security defaults are enabled though):

$body = (@{"isEnabled"="true"} | ConvertTo-Json)
$authHeader = @{'Authorization'="Bearer $token";"Content-Type" = "application/json"}
$res = Invoke-WebRequest -Headers $AuthHeader -Uri "" -Method Patch -Body $body

And that’s pretty much all there is to it. Re-runing the GET request now should show the status of the isEnabled property is set to True, thus Security defaults have been enabled.

This entry was posted in Azure AD, Graph API, Microsoft 365, Office 365, PowerShell. Bookmark the permalink.

3 Responses to Toggle Azure AD Security Defaults on or off programmatically

  1. Antonis Mladenis says:

    I am getting
    { “error”: { “code”: “AccessDenied”, “message”:
    | “You cannot perform the requested operation, required scopes
    | are missing in the token.”, “innerError”: {
    | “date”: “2021-03-08T18:06:24”, “request-id”:
    | “”,
    | “client-request-id”: “”
    | } } }

    Any clue?

    • Vasil Michev says:

      Did you make sure that the application has the Policy.ReadWrite.ConditionalAccess permission granted, and the token contains it accordingly?

  2. Pingback: My collection of useful (short) PowerShell code samples (gists) | Blog

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.