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 = "tenant.onmicrosoft.com" #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         = "https://graph.microsoft.com/.default"
client_secret = $client_secret
grant_type    = "client_credentials"
}

$authenticationResult = Invoke-WebRequest -Method Post -Uri "https://login.microsoftonline.com/$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 "https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy"
$result = ($res.Content | ConvertFrom-Json)
$result

@odata.context : https://graph.microsoft.com/beta/$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 "https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy" -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.

10 thoughts on “Toggle Azure AD Security Defaults on or off programmatically

  1. Nathan Mitchell says:

    I’m getting the following error after trying to enable Security Defaults. The Get portion is returning the Security Defaults status correctly though. Any advice?

    Invoke-WebRequest : The remote server returned an error: (400) Bad Request.
    + $res = Invoke-WebRequest -Headers $AuthHeader -Uri “https://graph.mic …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebExc
    eption
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

    Reply
    1. Vasil Michev says:

      Do you have any CA policies in place? This will prevent enabling security defaults. Check the full error message, it will give you an idea:

      {
      “error”: {
      “code”: “BadRequest”,
      “message”: “Conditional access, Sign-in risk, and Classic policies are enabled. Please disable and try again.”,
      “innerError”: {
      “date”: “2021-10-02T05:32:23”,
      “request-id”: “8267a9b3-f24e-4ba8-baa4-96d031789da4”,
      “client-request-id”: “e1827cbb-bddd-10a3-631f-1efbe9d2af16”
      }
      }
      }

      Other than that, check the request, or just toggle them via the portal.

      Reply
  2. Rens says:

    Thank you very much , this is what I needed.
    But can you explain me a little bit more.

    Do I need to install the Graph Api.
    I just want to turn off the Security defaults in my powershell script for my Azure Active Directory Environment.

    Which App ID do i have to use and where can i find the secrey key?

    Thank you for your hulp

    Reply
      1. Ajith Kumar Rajendran says:

        Is there a way to get the security defaults status via api/python sdk?

        Reply
  3. 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?

    Reply
    1. Vasil Michev says:

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

      Reply

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.