Skip to content

Implement MFA

We will create a conditional access policy to enforce all MFA on all users excluding an emergency Azure cloud-only Account.
Below is the PowerShell script for the same. If the emergency account does not exist, It will create the account before creating the conditional access policy.

The PowerShell script to enforce MFA

$TenantDetails = $NULL
$CurrentlyLoggedInUser = $NULL
$OnMicrosoftDomain = $NULL
$connection = $NULL
try { 
    $AzureADSession = Get-AzureADCurrentSessionInfo
    $CurrentlyLoggedInUser = $AzureADSession.Account | select -ExpandProperty Id
    Write-Host "Already connected to : $AzureADSession.TenantDomain as $CurrentlyLoggedInUser"

catch [Microsoft.Open.Azure.AD.CommonLibrary.AadNeedAuthenticationException] { 
    Write-Host "You're not connected to AzureAD. Connecting now..."; 
    $connection = Connect-AzureAD
    $AzureADSession = Get-AzureADCurrentSessionInfo
    $CurrentlyLoggedInUser = $AzureADSession.Account | select -ExpandProperty Id
    Write-Host "Connected to : $AzureADSession.TenantDomain as $CurrentlyLoggedInUser"
$CurrentUserId = Get-AzureADUser -Filter "UserPrincipalName eq '$CurrentlyLoggedInUser'" | select -ExpandProperty ObjectId
$EmergencyUserUPNPrefix = "EmUser"
$UserDisplayName = "EmergencyUser"
$TenantDomain = $Connection.TenantDomain
$OnMicrosoftDomain = $CurrentlyLoggedInUser.substring($CurrentlyLoggedInUser.IndexOf('@'))
$UserPrincipalName = $EmergencyUserUPNPrefix+$OnMicrosoftDomain

$EmUser = $NULL

    $EmUser = Get-AzureADUser -ObjectId $UserPrincipalName -ErrorAction silentlycontinue
    write-output("Emergency user " + $UserPrincipalName + ' already exists.')
    write-output("Emergency user " + $UserPrincipalName + ' does not exist. Creating now..')
    Add-Type -AssemblyName System.Web
    $UserPassword = [System.Web.Security.Membership]::GeneratePassword(32,2)
    $PasswordProfile = New-Object -TypeName Microsoft.Open.AzureAD.Model.PasswordProfile
    $PasswordProfile.Password = $UserPassword
    $NewUser = New-AzureADUser -DisplayName $UserDisplayName -PasswordProfile $PasswordProfile -AccountEnabled $true -UserPrincipalName $UserPrincipalName -MailNickName "EmUser"

    # Adding the new user to Global Admin role
    Write-output "Adding the new user to Global Admin r+ole"
    $GlobalAdminRole = Get-AzureADDirectoryRole | Where-Object { $_.displayName -eq  "Global Administrator"} 
    Add-AzureADDirectoryRoleMember -ObjectId $GlobalAdminRole.ObjectId -RefObjectId $NewUser.ObjectId 

    # Saving the details for the app
    $temp_location = (get-location).Drive.Root+"office365_temp\"
    $temp_file = $temp_location + $CurrentTimestamp+".txt"
    New-Item $temp_location -ItemType Directory -Force | Out-Null
    New-Item $temp_file -ItemType File -Force | Out-Null
    Add-Content $temp_file ("Tenant Domain: " + $AzureADSession.TenantDomain)
    Add-Content $temp_file ("Tenant Id: " + $AzureADSession.TenantId)
    Add-Content $temp_file ("Username: " + $UserPrincipalName)
    Add-Content $temp_file ("Password: " + $UserPassword)
    .\notepad.exe $temp_file
    write-output("User details written to the file\r\n")

    $conditions = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessConditionSet
    $conditions.Applications = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessApplicationCondition
    $conditions.Applications.IncludeApplications = "all"
    $conditions.Users = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessUserCondition
    $conditions.Users.IncludeUsers = "all"
    $conditions.Users.ExcludeUsers = $EmUser.ObjectId
    $controls = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessGrantControls
    $controls._Operator = "OR"
    $controls.BuiltInControls = "mfa"
    New-AzureADMSConditionalAccessPolicy -DisplayName "Enforce MFA on all users" -State "Enabled" -Conditions $conditions -GrantControls $controls
    Write-Output "Conditional Access policy created"

Conditional Access policy is created.

MFA is enforced for all apps.

MFA is enforced for all users except the emergency global admin user.

Number matching (Currently in preview)

As of the day of writing this (5/23/2022), this feature is in preview and not GA.
However, It will be enabled by default when It goes GA just because of added security and great user experience.

Just like password fatigue caused users to re-use passwords and get compromised If we use push notifications for MFA, and if an attacker constantly sends push notifications to users with compromised credentials, users would eventually push and that would lead to the attacker accessing the user's cloud resources.

To counter "push fatigue" [ a made-up term :)], Microsoft launched a new feature named "Number matching" (currently in preview).

Login to Azure Portal > Azure Active Directory > Security > Authentication methods and click on the "Microsoft Authenticator"

Click on the "..." and then configure.

Make sure "Require number matching" is set to "Enabled".
Also, make sure the "Show additional context in notification" is also enabled.
This provides for which app the login is being performed.

Now when the user logs in, after the user name and password has been validated, the browser shows a number and a notification is sent to the Microsoft Authenticator. We need to enter the same number in the authenticator to be successfully and fully authenticated via MFA.

Bworser shows the number during log in process Enter the same number in the Microsoft Authenticator app

I notice the number is always double-digit (not 5 or 6 digits).
So, not only It adds to security by having the user enter the number (instead of an ignorant push) but also Its a great user experience because It's just two digits and easy to remember.