Table of Contents
Using the Graph APIs Instead of MSOL Cmdlets
As regular readers of this blog know, I am keen that Microsoft 365 tenants protect user accounts with multi-factor authentication (MFA), especially user accounts with Entra ID admin roles. Despite all the promises made by security vendors about products to protect your tenants, the single most effective step that an organization can take is to make sure that accounts use MFA to sign into Microsoft 365. Some years ago, Microsoft reported that MFA blocks 99.9% of account compromise attacks. That statistic remains unquestioned.
Over the years, I’ve written several scripts to help tenant administrators understand the use of MFA within user accounts. The original 2018 article focused on reporting the enablement of accounts to use MFA. I followed up in 2019 with an article explaining how to find unprotected accounts that hold an administrative role. The downside of both articles is that the PowerShell code described in the text uses cmdlets from the old Microsoft Online Services (MSOL) module, which Microsoft is in the process of deprecating. The code still works, but the articles are good examples of advice you can find on the internet that is degrading and will soon be obsolete, as discussed yesterday.
Microsoft would like everyone to rewrite their PowerShell scripts to use Graph API requests or the cmdlets in the Microsoft Graph PowerShell SDK. The unavailability of equivalent API support undermined Microsoft’s aspiration. It’s been possible to report the authentication methods used by Entra ID accounts, but replicating a report showing administrative accounts and their MFA status has been harder.
Graph Registration Details
Enter the Microsoft Graph userRegistrationDetails resource type. This is a beta API that returns a list of authentication methods for users in a manner that’s easier to process than before. Two requests are available:
- List: Returns the authentication methods for every user account in the organization. This isn’t as useful as it seems because the data returned includes guest accounts and unlicensed member accounts used for purposes like room mailboxes.
- Get: Returns the authentication methods for a single user account. In practice, this API is much more useful.
The authentication methods data returned for a user account is shown below. In this case, the account is not MFA-enabled.
Name Value ---- ----- userPrincipalName Andy.Ruth@office365itpros.com defaultMfaMethod none isMfaCapable False isSsprCapable False @odata.context https://graph.microsoft.com/beta/$metadata#reports/authenticationMethods/userRegistrationDetails/$entity isSsprEnabled False id fdc6b121-44b8-4262-9ca7-3603a16caa3e methodsRegistered {email} isMfaRegistered False isPasswordlessCapable False isSsprRegistered True userDisplayName Andy Ruth (Director)
The Microsoft Graph PowerShell SDK includes the Get-MgReportAuthenticationMethodUserRegistrationDetail cmdlet (you’ve got to love the automatically generated cmdlet names!). If you run the cmdlet without any parameters, you get the same data as returned by the List API. To return the data for an individual account, include a filter to specify their User Principal Name. For example:
Get-MgReportAuthenticationMethodUserRegistrationDetail -Filter "UserPrincipalName eq 'Sean.Landy@office365itpros.com'" | Fl DefaultMfaMethod : mobilePhone Id : 08dda855-5dc3-4fdc-8458-cbc494a5a774 IsMfaCapable : True IsMfaRegistered : True IsPasswordlessCapable : False IsSsprCapable : False IsSsprEnabled : False IsSsprRegistered : True MethodsRegistered : {mobilePhone} UserDisplayName : Sean Landy UserPrincipalName : Sean.Landy@office365itpros.com AdditionalProperties : {}
Building a Report of Entra ID Admin Roles
Knowing how to get information about an individual account, we can consider how to check all user accounts paying special attention to those holding one or more administrative roles. I took these steps, using a mixture of Graph API requests and cmdlets from the Microsoft Graph PowerShell SDK:
- Connect to the Microsoft Graph SDK. You’ll need to specify the UserAuthenticationMethod.Read.All and AuditLog.Read.All permissions.
- Select the beta profile (Select-MgProfile Beta) to ensure that the Graph retrieves the registration information.
- Run Get-MgDirectoryRole to find the set of available roles. Note: this cmdlet reports the set of administrative roles assigned in the tenant. There are usually a bunch of unassigned roles that don’t show up. The full set of roles defined in Entra ID can be found by running Get-MgDirectoryRoleTemplate | Select-Object DisplayName, Id | Sort-Object DisplayName. Don’t use role template identifiers as an input to Get-MgDirectoryRoleMember as they won’t work.
- For each role, run Get-MgDirectoryRoleMember to find the current holders of the role.
- Create an array of accounts that hold administrative roles.
- Run Get-MgUser to find the set of licensed user accounts (it’s reasonable to assume that administrative accounts have at least one license).
- For each account, get the authentication methods. You can use the Graph API request or the Get-MgReportAuthenticationMethodUserRegistrationDetail cmdlet.
- Check if the account holds administrative roles and if so, fetch the roles held by the account.
- Generate a report in an Excel worksheet (using the Import-Excel module).
- Output a warning message if the script detects any unprotected administrative accounts (Figure 1).

The Excel worksheet (Figure 2) is particularly useful in terms of being able to slice and dice the data to generate whatever report you need. For instance, you can see that many of the accounts listed use mobile phones (SMS messages) for the second authentication method. The Microsoft authenticator app is a better option, so you could pull the data about accounts currently using mobile phones to encourage them to move to the authenticator app.

Download Script from GitHub
You can download the full script to report Entra ID admin roles and unprotected accounts from GitHub. Remember that this is code written to demonstrate a principal instead of an off-the-shelf solution. The code is basic PowerShell and can be altered to fit your needs. Enjoy
——————-
So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.!
This doesn’t take into account Admin Roles assigned to groups so additional code would have to be written to check groups were returned and expand them. it also makes a false assumption that admin accounts are licensed. Best practice that Microsoft advises is use a separate admin account for admin work which is more likely not to be licensed.
Correct. It’s a PowerShell script that doesn’t proclaim itself to be an off-the-shelf solution. The intention is to show people how to approach the problem. As to admin accounts being unlicensed, that might be true in some circumstances, but as I discuss in this article https://practical365.com/protecting-administrator-mailboxes-from-threats/, admin accounts often need to receive email and therefore will have Exchange Online licenses (albeit with controls to stop people sending email to those accounts). Like most of the stuff I post, it’s here for people to read, get ideas from, and hopefully make use of the concepts in their environment.
I can also recommend this article/script on the topic of finding MFA users: https://lazyadmin.nl/powershell/list-office365-mfa-status-powershell/ or https://github.com/ruudmens/LazyAdmin/blob/master/Office365/MFAStatus.ps1
I have installed the Microsoft.Graph module and connected but I am getting this error when trying to run the script.
Get-MgReportAuthenticationMethodUserRegistrationDetail: The term ‘Get-MgReportAuthenticationMethodUserRegistrationDetail’ is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
What version of the module are you using? I see the cmdlet in 1.10.0
get-module
ModuleType Version Name ExportedCommands
———- ——- —- —————-
Script 1.10.0 Microsoft.Graph.Applications {Add-MgApplicationKey, Add-MgApplicationPassword, Add-MgSer…
Script 1.10.0 Microsoft.Graph.Authentication {Add-MgEnvironment, Connect-MgGraph, Disconnect-MgGraph, Ge…
Script 1.10.0 Microsoft.Graph.Groups {Add-MgGroupDriveListContentTypeCopy, Add-MgGroupDriveListC…
Script 1.10.0 Microsoft.Graph.Identity.Directo… {Complete-MgDirectoryImpactedResource, Complete-MgDirectory..
You will have to use Beta version of Microsoft Graph PowerShell for this command
Run Select-MgProfile -Name “beta” and then Get-MgReportAuthenticationMethodUserRegistrationDetail for it to work.
The problem that I’m finding is that the MS Graph API doesn’t report the true status of MFA for a user: that only seems to be available via the old MSOL module in PowerShell. I have users who the Graph API returns IsMfaRegistered as True, with default methods showing, etc, but their MFA Status shows as “Disabled” in the M365 MFA web UI, and sign-in logs confirm that they’re only using Single Factor Authentication.
The information from the MS Graph API doesn’t seem to currently be capable of distinguishing between whether someone is actually using MFA, or if they’re just fully registered and configured, but not actually using it. I’m shocked that’s the case, but from everything I’m seeing, that’s the way it is.
There are definitely some issues that Microsoft is working on. It would be good if you could report the problem formally to Microsoft Support so that they can gather the details and escalate to engineering. If you give me the support ticket number, I’ll share it with some folks I know who might be able to accelerate fixes. The Graph SDK is built every month and I know Microsoft is working on a major release, so you never know when a fix might come.
Thanks, Tony! This seems like it’s not really a bug that I could report to Support, though, it’s a lack of functionality….they used to use UserVoice for enhancement requests for lack of functionality like this, but I think they’ve transitioned to their own feedback portal. After some searching, I think this item is related to the current lack of functionality, but the comment from Stephen Talley is the thing that I’m talking about: the functionality from the Per-User MFA screen in MS365 Admin is just missing from the Graph API. https://feedbackportal.microsoft.com/feedback/idea/12d10bfb-10e3-ec11-a81b-000d3a03dba2
I could create a separate feedback item with just what I’ve noticed, if file a Support request if you’re sure that’s the way to go on this.
I’ve asked the SDK architect if this specific functionality is due to come to the SDK. Stay tuned.
Hi Tony, I wondered if you ever got a reply from the SDK architect regarding this. Unless it’s coming in the near future, I’m going to have to hack something with a webserver with PHP calling PowerShell in order to get accurate data on whether MFA is enforced for a user. Thanks!
Nothing so far. Sorry!
This doesn’t work for me. After running the script, a browser window opens asking for various access to 365, which I grant, but then the next page doesn’t load:
Secure Connection Failed
An error occurred during a connection to localhost:48722. SSL received a record that exceeded the maximum permissible length.
Error code: SSL_ERROR_RX_RECORD_TOO_LONG
The page you are trying to view cannot be shown because the authenticity of the received data could not be verified.
Please contact the web site owners to inform them of this problem.
Congratulations! You’ve hit an error I have never seen before. This is a PowerShell script, so you can extract lines and run sections at a time until you find where the problem lies and then we might know how to solve the issue.
Hello Tony
Is it possible for Powershell to query the Web API (Graph Explorer) and parse the data as a table?
MgGraph is really slow at reporting data. Not sure if it is Powershell that makes it this way or just how the module works but if I run your script (your script is not the issue) or query anything tennant wide on MgGraph, it will take between 2 and 5 minutes due to the hundreds of accounts.
When querying the API directly, results are displayed in less than 2 seconds (see example below):
https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$filter=isAdmin+eq+true
Like:
$uri = “https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails?$filter=isAdmin+eq+true”
[array]$Data = Invoke-MgGraphRequest -Uri $Uri
$Data = $Data.Value
$Data is an array holding the results of the API request.
Hey this works great and performance is miles ahead of the Graph PsModule. Sure, I now have to go trough the trouble to create my Objects but this is well worth it
Thanks
Excellent sample code.
More efficiency gained by commenting out the following (particularly in larger organizations):
#[array]$Users = Get-MgUser -Filter “assignedLicenses/`$count ne 0 and userType eq ‘Member'” -ConsistencyLevel eventual -CountVariable Records -All
and replacing it with the following….
$Users = [System.Collections.Generic.List[Object]]::new()
ForEach ($UniqueAdmin in $UniqueAdminRoleHolders) {
$Admin = Get-MgUser -UserID $UniqueAdmin.Id
$Users.Add($Admin)
}
Good example of taking code published here and adapting to make it better… I always emphasize that I am not a pro developer. My code invariably can be improved…
I reported the commands are not working for Update-MgReportAuthenticationMethodUserRegistrationDetail or New-MgReportAuthenticationMethodUserRegistrationDetail so Microsoft removed the Technet articles and now its a broken 404 page
Welcome to the cloud!