Change default Multifactor with Microsoft Graph PowerShell
Many of my customers have been using Entra ID (Azure Active Directory) MFA for ages. The problem for many of the early adopters is that many users enrolled to use SMS as the default method. Even if they have run campaigns and adoption events, some users just wont change. Some manage to configure the Authenticator app but skip to change the method to default, and keep using SMS. Previously, admins were unable to change the default MFA method. But now there is a change!
Now you can, as an admin, change the default Entra ID MFA method both in Admin portal and in Microsoft Graph!
This blog focus on Microsoft Graph way of changing the default MFA method.
Change default MFA method using graph
First you need to install microsoft graph module
install-module microsoft.graph
import-module microsoft.graph
Then you need to connect to graph. For this to work you will need the following permissions:
- UserAuthenticationMethod.ReadWrite.All
- User.Read.All
If you run this you will get a prompt to consent the permissions if using it for the first time
Connect-MgGraph -scopes "User.Read.All,UserAuthenticationMethod.ReadWrite.All"
Lets get the user to change the default method for
$MgUser = Get-MgUser -UserId "mr.tbone@tbone.se"
There is no ready command to set the default MFA method, so you need to send a graph request to Patch the setting for the user. The graph URI to Patch is this:
$uri = “https://graph.microsoft.com/beta/users/$($MgUser.id)/authentication/signInPreferences”
On this URI I have found 3 interesting properties: we can set 2 properties:
- systemPreferredAuthenticationMethod – The System-preferred multifactor authentication selected by the global setting
- isSystemPreferredAuthenticationMethodEnabled – determine if System-preferred multifactor authentication should be enforced on this user
- userPreferredMethodForSecondaryAuthentication – The default auth method selected by user or set by admin
Read more on System-preferred multifactor authentication – Authentication methods policy HERE
To get the current values for the user, we can use the same URI and post a GET
Invoke-MgGraphRequest -uri $uri -Method GET
The result could look like this:
The different MFA methods you can push into the users default setting are:
- push – push notification in Microsoft Authenticator
- oath – 6 digit code from authentication app.
- voiceMobile – Microsoft call upp your primary mobile phone
- voiceAlternateMobile – Microsoft call upp your alternate mobile phone
- voiceOffice – Microsoft call upp your office phone
- sms – 6 digit code from SMS text message to the mobile phone
As you can see there are some methods missing, but might appear in later updates.
We take the method we want to set as default and build a body to attach to the Patch:
$body = @’
{“userPreferredMethodForSecondaryAuthentication”: “push”}
‘@
If we do not want System-preferred multifactor authentication to be enforced we can also add that property:
$body = @’
{“isSystemPreferredAuthenticationMethodEnabled”: false
“userPreferredMethodForSecondaryAuthentication”: “push”}
‘@
The result can look like this:
If we then request the settings with GET we can verify the result:
Change Default MFA method on all users
I have written a script to set the users default method to the specified one.
It will fist check if the user is compliant to use that method by getting the registered methods for each user.
Script can be found on my GITHUB
<#PSScriptInfo
.SYNOPSIS
Script to set Default Password
.DESCRIPTION
This script will set the default MFA method for a specific user of for all users in the tenant to the preferred method defined in the script.
.EXAMPLE
.\set-MFADefaultMethod.ps1
Will set the default MFA method for all users in the tenant to the preferred method defined in the script.
.\set-MFADefaultMethod.ps1 -UserPrincipalName mr.tbone@tbone.se
Will set the default MFA method for the specified user
.NOTES
Written by Mr-Tbone (Tbone Granheden) Coligo AB
torbjorn.granheden@coligo.se
.VERSION
1.0
.RELEASENOTES
1.0 2023-09-18 Initial Build
.AUTHOR
Tbone Granheden
@MrTbone_se
.COMPANYNAME
Coligo AB
.GUID
00000000-0000-0000-0000-000000000000
.COPYRIGHT
Feel free to use this, But would be grateful if My name is mentioned in Notes
.CHANGELOG
1.0.2309.1 - Initial Version
#>
#region ---------------------------------------------------[Set script requirements]-----------------------------------------------
#Requires -Version 7.0
#endregion
#region ---------------------------------------------------[Script Parameters]-----------------------------------------------
param(
[Parameter(
Mandatory = $false,
ParameterSetName = "UserPrincipalName",
HelpMessage = "Enter a single UserPrincipalName",
Position = 0
)]
[string[]]$UserPrincipalName
)
#endregion
#region ---------------------------------------------------[Modifiable Parameters and defaults]------------------------------------
$UserDefaultMethod = "push" # Set the preferred MFA method here
$SystemPreferredMethodEnabled = "true" # Set to true if you want to enable the system preferred authentication method
#endregion
#region ---------------------------------------------------[Set global script settings]--------------------------------------------
Set-StrictMode -Version Latest
#endregion
#region ---------------------------------------------------[Static Variables]------------------------------------------------------
$AllMgUsers = @()
#Log File Info
#endregion
#region ---------------------------------------------------[Import Modules and Extensions]-----------------------------------------
Import-Module Microsoft.Graph.users
Import-Module Microsoft.Graph.Authentication
#endregion
#region ---------------------------------------------------[Functions]------------------------------------------------------------
#endregion
#region ---------------------------------------------------[[Script Execution]------------------------------------------------------
# Connect to Graph
Connect-MgGraph -scopes "User.Read.All,UserAuthenticationMethod.ReadWrite.All" -NoWelcome
if ($UserPrincipalName) {$AllMgUsers += Get-MgUser -UserId $UserPrincipalName}
else{$AllMgUsers = Get-MgUser -Filter "userType eq 'Member'" -all}
$TotalMgUsers = $AllMgUsers.Count
# Get all the user's authentication methods. Batches is faster than getting the user and then the methods
$starttime = get-date
$AllAuthMethods = @()
for($i=0;$i -lt $TotalMgUsers;$i+=20){
$req = @{}
# Use select to create hashtables of id, method and url for each call
if($i + 19 -lt $TotalMgUsers){
$req['requests'] = ($AllMgUsers[$i..($i+19)]
| select @{n='id';e={$_.id}},@{n='method';e={'GET'}},`
@{n='url';e={"/users/$($_.id)/authentication/methods"}})
} else {
$req['requests'] = ($AllMgUsers[$i..($TotalMgUsers-1)]
| select @{n='id';e={$_.id}},@{n='method';e={'GET'}},`
@{n='url';e={"/users/$($_.id)/authentication/methods"}})
}
$response = invoke-mggraphrequest -Method POST `
-URI "https://graph.microsoft.com/beta/`$batch" `
-body ($req | convertto-json)
$CurrentMgUser = $i
$response.responses | foreach {
if($_.status -eq 200 -and $_.body.value -ne $null){
$AuthMethod = [PSCustomObject]@{
"userprincipalname" = $AllMgUsers[$CurrentMgUser].userPrincipalName
"AdditionalProperties" = $_.body
}
$AllAuthMethods += $AuthMethod
} else {
# request failed
#write-host "the request for signInPreference failed"
}
$CurrentMgUser++
}
#progressbar
$Elapsedtime = (get-date) - $starttime
$timeLeft = [TimeSpan]::FromMilliseconds((($ElapsedTime.TotalMilliseconds / $CurrentMgUser) * ($TotalMgUsers - $CurrentMgUser)))
Write-Progress -Activity "Getting Authentication Methods for users $($CurrentMgUser) of $($TotalMgUsers)" `
-Status "Est Time Left: $($timeLeft.Hours) Hours, $($timeLeft.Minutes) Minutes, $($timeLeft.Seconds) Seconds" `
-PercentComplete $([math]::ceiling($($CurrentMgUser / $TotalMgUsers) * 100))
}
# Build the JSON Body request
$body = @'
{
"isSystemPreferredAuthenticationMethodEnabled": SystemPreferredMethodEnabled,
"userPreferredMethodForSecondaryAuthentication": "UserDefaultMethod"
}
'@
$body = $body -replace 'SystemPreferredMethodEnabled', $SystemPreferredMethodEnabled
$body = $body -replace 'UserDefaultMethod', $UserDefaultMethod
#Start the loop to set the default method for each user
$CurrentMgUser = 0
$starttime = get-date
foreach($MgUser in $AllMgUsers){
#Reset all the variables
$Authenticator = $false
$Phone = $false
$PasswordLess = $false
$Fido2 = $false
$TAP = $false
$WHFB = $false
$3part = $false
$Email = $false
$uri = "https://graph.microsoft.com/beta/users/$($Mguser.id)/authentication/signInPreferences"
#Get the current setting, if it matches the preferred method, do nothing, else set the preferred method
$CurrentDefaults = Invoke-MgGraphRequest -uri $uri -Method GET -OutputType PSObject
If ($CurrentDefaults.userPreferredMethodForSecondaryAuthentication -eq $UserDefaultMethod){}
else{
#Get the user's authentication methods
$mfaData = $AllAuthMethods | where userprincipalname -eq $mguser.UserPrincipalName | select -ExpandProperty AdditionalProperties
# Populate $userobject with MFA methods from $mfaData
if ($mfaData) {
ForEach ($method in $mfaData.value) {
Switch ($method['@odata.type']) {
"#microsoft.graph.microsoftAuthenticatorAuthenticationMethod" {
# Microsoft Authenticator App
$Authenticator = $true
}
"#microsoft.graph.phoneAuthenticationMethod" {
# Phone authentication
$Phone = $true
}
"#microsoft.graph.passwordlessMicrosoftAuthenticatorAuthenticationMethod" {
# Passwordless
$PasswordLess = $true
}
"#microsoft.graph.fido2AuthenticationMethod" {
# FIDO2 key
$Fido2 = $true
}
"microsoft.graph.temporaryAccessPassAuthenticationMethod" {
# Temporary Access pass
$TAP = $true
}
"#microsoft.graph.windowsHelloForBusinessAuthenticationMethod" {
# Windows Hello
$WHFB = $true
}
"#microsoft.graph.softwareOathAuthenticationMethod" {
# ThirdPartyAuthenticator
$3part = $true
}
"#microsoft.graph.emailAuthenticationMethod" {
# Email Authentication
$Email = $true
}
}
}
}
#Set the default method if the user has a registered method that matches the preferred method
If ((($UserDefaultMethod -eq "push") -or ($UserDefaultMethod -eq "oath"))-and ($Authenticator -eq $true))
{
Invoke-MgGraphRequest -uri $uri -Body $body -Method PATCH
}
elseIf ((($UserDefaultMethod -eq "phone") -or ($UserDefaultMethod -eq "voiceMobile") -or ($UserDefaultMethod -eq "voiceAlternateMobile") -or ($UserDefaultMethod -eq "voiceOffice") -or ($UserDefaultMethod -eq "sms")) -and ($Phone -eq $true))
{
Invoke-MgGraphRequest -uri $uri -Body $body -Method PATCH
}
else{
#User has no registered method that matches the preferred method
}
}
$CurrentMgUser++
#progressbar
if($CurrentMgUser % 100 -eq 0){
$Elapsedtime = (get-date) - $starttime
$timeLeft = [TimeSpan]::FromMilliseconds((($ElapsedTime.TotalMilliseconds / $CurrentMgUser) * ($TotalMgUsers - $CurrentMgUser)))
Write-Progress -Activity "Getting Authentication Methods for users $($CurrentMgUser) of $($TotalMgUsers)" `
-Status "Est Time Left: $($timeLeft.Hours) Hours, $($timeLeft.Minutes) Minutes, $($timeLeft.Seconds) Seconds" `
-PercentComplete $([math]::ceiling($($CurrentMgUser / $TotalMgUsers) * 100))
}
}
Disconnect-Graph
#endregion