Automating with PowerShell: Enabling Secure Defaults (And SD explained)

In one of the groups I am in there was some confusion about how Secure Defaults work and how to deploy the Secure Defaults centrally, so I figured I would try to help with this.

Secure Defaults is Microsoft’s answer to our questions about deploying multi factor authentication to an entire tenant, of course security defaults does a lot more than just that.

So what does Security Defaults do?

  • Requires users to register for Multi-factor authentication. This allows a user to take up to 14 days to register MFA.
  • It also Disables legacy authentication protocols
  • Protects all privileged account logons, like your global administrator.
  • It requires MFA for each login into a protected portal such as Azure, and the O365 admin portal.
  • This one is key: it requires users to logon with MFA only when the logon is seen as risky.

So that last point is pretty big; Users are not prompted for MFA each time they logon. This has been done on purpose by our friends at Microsoft. Microsoft believes that with the data they gathered around security and multi-factor authentication this is the best solution as it avoids creating a pattern of “muscle” memory where users are continually prompted for MFA and just start quickly clicking “Approve”.

Users get a little less trigger-happy on MFA prompts when they are unusual, so that should help you in your security practices. If you want to know exactly what a risky event is, click here for some more information. Microsoft has a neat little table with the exact definition.

If you still want users to get prompted as each logon, as opposed to only some. You’ll have to go to the Multifactor admin page and click ‘enable’ for each user, after enabling Security Defaults. The users will then always get prompted on the method they configured for Security Defaults, you can use the script below to enable Security Defaults on all tenants, or a single tenant.

Permissions

As always you’ll need the secure application model for this script. You’ll also need to add some permissions:

  • Go to the Azure Portal.
  • Click on Azure Active Directory, now click on “App Registrations”.
  • Find your Secure App Model application. You can search based on the ApplicationID.
  • Go to “API Permissions” and click Add a permission.
  • Choose “Microsoft Graph” and “Application permission”.
  • Search for “Policy” and click on “Policy.Read.All and Policy.ReadWrite.ConditionalAccess”. Click on add permission.
  • Do the same for “Delegate Permissions”.
  • Finally, click on “Grant Admin Consent for Company Name.

Single Tenant Script

######### Secrets #########
$ApplicationId = 'AppID'
$ApplicationSecret = 'AppSecret' | ConvertTo-SecureString -Force -AsPlainText
$RefreshToken = 'VeryLongRefreshToken'
######### Secrets #########
$CustomerTenant = "YourClient.onmicrosoft.com"
########################## Script Settings  ############################
$Baseuri = "https://graph.microsoft.com/beta"
write-host "Generating token to log into Azure AD." -ForegroundColor Green
$credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret)
$CustGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes "https://graph.microsoft.com/.default" -ServicePrincipal -Tenant $CustomerTenant
$Header = @{
    Authorization = "Bearer $($CustGraphToken.AccessToken)"
}

$SecureDefaultsState = (Invoke-RestMethod -Uri "$baseuri/policies/identitySecurityDefaultsEnforcementPolicy" -Headers $Header -Method get -ContentType "application/json")

if ($SecureDefaultsState.IsEnabled -eq $true) {
    write-host "Secure Defaults is already enabled for $CustomerTenant. Taking no action."-ForegroundColor Green
}
else {
    write-host "Secure Defaults is disabled. Enabling for $CustomerTenant" -ForegroundColor Yellow
    $body = '{ "isEnabled": true }'
    (Invoke-RestMethod -Uri "$baseuri/policies/identitySecurityDefaultsEnforcementPolicy" -Headers $Header -Method patch -Body $body -ContentType "application/json")
}

This script checks, and sets the Security Defaults to on, for a single tenant.

All tenants scripts

######### Secrets #########
$ApplicationId = 'AppID'
$ApplicationSecret = 'AppSecret' | ConvertTo-SecureString -Force -AsPlainText
$RefreshToken = 'VeryLongRefreshToken'
######### Secrets #########
$Skiplist = "Bla1.onmicrosoft.com", "bla2.onmicrosoft.com"
########################## Script Settings  ############################

$Baseuri = "https://graph.microsoft.com/beta"
write-host "Generating token to log into Azure AD." -ForegroundColor Green
$credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret)

$aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal
$graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default'

Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken

$customers = Get-MsolPartnerContract -All | Where-Object { $_.DefaultDomainName -notin $skiplist }

foreach ($customer in $customers) {
    $CustomerTenant = $customer.defaultdomainname
    $CustGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes "https://graph.microsoft.com/.default" -ServicePrincipal -Tenant $CustomerTenant
    $Header = @{
        Authorization = "Bearer $($CustGraphToken.AccessToken)"
    }

    $SecureDefaultsState = (Invoke-RestMethod -Uri "$baseuri/policies/identitySecurityDefaultsEnforcementPolicy" -Headers $Header -Method get -ContentType "application/json")

    if ($SecureDefaultsState.IsEnabled -eq $true) {
        write-host "Secure Defaults is already enabled for $CustomerTenant. Taking no action."-ForegroundColor Green
    }
    else {
        write-host "Secure Defaults is disabled. Enabling for $CustomerTenant" -ForegroundColor Yellow
        $body = '{ "isEnabled": true }'
        (Invoke-RestMethod -Uri "$baseuri/policies/identitySecurityDefaultsEnforcementPolicy" -Headers $Header -Method patch -Body $body -ContentType "application/json")
    }

}

And this one processes all tenants. If you want to skip a couple of them, just enter their default domain names in the skiplist variable.

That’s it! as always, Happy PowerShelling.

Recent Articles

The return of CyberDrain CTF

CyberDrain CTF returns! (and so do I!)

It’s been since september that I actually picked up a digital pen equivalent and wrote anything down. This was due to me being busy with life but also my side projects like CIPP. I’m trying to get back into the game of scripting and blogging about these scripts. There’s still so much to automate and so little time, right? ;)

Monitoring with PowerShell: Monitoring Acronis Backups

Intro

This is a monitoring script requested via Reddit, One of the reddit r/msp users wondered how they can monitor Acronis a little bit easier. I jumped on this because it happened pretty much at the same time that I was asked to speak at the Acronis CyberSummit so it kinda made sense to script this so I have something to demonstrate at my session there.

Monitoring with PowerShell: Monitoring VSS Snapshots

Intro

Wow! It’s been a while since I’ve blogged. I’ve just been so swamped with CIPP that I’ve just let the blogging go entirely. It’s a shame because I think out of all my hobbies it’s one I enjoy the most. It’s always nice helping others achieve their scripting target. I even got a couple of LinkedIn questions asking if I was done with blogging but I’m not. Writing always gives me some more piece of mind so I’ll try to catch up again. I know I’ve said that before but this time I’ll follow through. I’m sitting down right now and scheduling the release of 5 blogs in one go. No more whining and no more waiting.