How can I use a System Managed Identity with Connect-MsolService

Copper Contributor

Does anyone know how to use Connect-MsolService with a system-assigned managed identity? I need it for the purpose of getting MFA status data about the organisation's employees. MSGraph seems to have zero data on this, except for the User Registration Details report, which again does not show MFA status.

11 Replies

@Jonesy6123 

 

Hi, Jack.

 

Independent of whether the Graph modules have precisely what you seek, the MSOnline module is right on the cusp of deprecation (March 2024).

 

 

They won't cease working for at least six months after deprecation, however, building solutions on MSOnline now simply means the solution will need to be recreated using Graph potentially as soon as seven months down the track (from the time of writing).

 

With respect to Graph, you can obtain some authentication details via the Get-MgUserAuthenticationMethod commandlet. It's clunky and inefficient to use (this data really should by now be rolled up unto the /user endpoint, filterable and compliant with the delta endpoint, but it's currently none of those things), but it does exist and can be used to determine which authentication methods a user has enabled and when.

 

 

In order to use a system-assigned managed identity with MSOnline, you'd need to:

 

  1. Connect to Graph using either the Microsoft.Graph.Authentication or Az.Accounts module (other options do exist but I'm sticking to commonly-used modules for brevity);
  2. Obtain the token (harder to do with the Graph module than Az, but still doable);
  3. Use that token in the Connect-MSOnline call.

 

Some references:

 

 

Cheers,

Lain

Hey there,

Thank you so much for responding to this. Your information was very well written and easy to understand.

I have in fact tried all of the above approaches prior to writing this post. It appears that calling for an API access token from MSGraph does not in fact work for MSOnline. I get an error regarding "the key was not found in the dictionary.", which means that an expected array of access tokens did not contain my specified token. I searched this up and found that it's purely because it expects and Az graph access token. The API's and these modules that organisations rely on are clearly a shambles.

As far as you're aware, the only way to get this kind of information is with your Get-MgUser.. method? I am currently access the UserRegistrationDetails environment, but no such StrongAuthenticationRequirements parameter can be found, that IS found in the MSOnline module. (Which is what I really need)

Can you please just confirm to your best knowledge that it is currently not possible to get that level of information until MS migrates that data over to the MSGraph? I want to iterate that I can do it with my own access credentials (with any method including using MSOnline module) but I need to be able to automate it in a weekly report.

@Jonesy6123 

 

Hi, Jack.

 

First, here's how you can procure an Azure AD Graph token for use with the MSOnline module.

 

I should add a disclaimer that I have not used the MSOnline module for many years now and only ran a quick Get-MsolUser comamndlet post sign-in as confirmation the sign-in was valid.

 

The version of MSOnline I used for this authentication test is 1.1.183.57.

 

$Token = Get-AzAccessToken -ResourceUrl "https://graph.windows.net";
Connect-MsolService -AdGraphAccessToken ($Token).Token;

 

Output

LainRobertson_0-1708474013241.png

 

So, if I'd signed into Az first with the servicePrincipal, then the above steps show how to procure a token that's usable in MSOnline - or it should (I'' double-check with a servicePrincipal and CBA later when time permits).

 

Next, when it comes to replicating the functionality of MSOnline, I can't answer that as I don't use MSOnline and therefore have no point of comparison.

 

I'd imagine that most if not all of it could be emulated using Graph, whether that's with the Microsoft.Graph.* commandlets or if they have gaps, the native REST calls. It may just be the case that there's no single Microsoft.Graph commandlet that behaves precisely as the MSOnline commandlet, meaning some extra effort may be required in joining things together.

 

Cheers,

Lain

Hey Lain,

The below error is thrown when I use the system managed identity to connect to Az like so:

Connect-AzAccount -Identity

Error thrown:
Exception of type 'Microsoft.Online.Administration.Automation.MicrosoftOnlineException' was thrown.

The system managed identity has all of the necessary permissions. Could I instead use an app registration?

@Jonesy6123 

 

Hi, Jack.

 

I haven't yet had a reason to use managed service identities, however, it seems it should be possible.

 

 

I'll have a proper look when time permits.

 

Cheers,

Lain

Appreciate it mate.

It's worth noting I only need MSOnline in order to get MFA Status using the StrongAuthentication property for a weekly report. I have got working scripts that use MSGraph, I just can't find the same level of information.

@Jonesy6123 

 

Hi, Jack.

 

I've had a quick read - which means I'm far from being accomplished in this area - and come to the conclusion I cannot test this specific scenario.

 

 

Prior to finding the article above, I was thinking I might be able to utilise the user-assigned managed identity, however, that's not possible.

 

Both types (system- and user-assigned) of managed identity are only useful in the scenario where they are being used by an Azure resource, not by a user like me sitting in front of an interactive PowerShell console.

 

I work in the identity space, not the Azure resource space, meaning I have no means for conducting such a test (such as calling an Azure runbook from an Azure virtual machine).

 

So, that rules managed identities entirely out for me and the work that I do (where certificate-based authentication achieves the similar end of not having credentials in the script).

 

To be clear though, conceptually, you absolutely can do what I covered in my earlier post using managed identities, which was:

 

  • Sign into Graph;
  • Obtain an Azure Graph (as distinct from a Microsoft Graph, which didn't work in my limited testing and I didn't investigate why) token;
  • Pass to MSOL via the -AdGraphAccessToken parameter.

 

Perhaps someone else with Azure resource access could test this, though again, this approach make not have a long lifetime ahead of it with the imminent deprecation of the Azure AD Graph endpoint.

 

Moving on from the MSOnline module topic...

 

Here's a basic script that pulls MFA data using the Microsoft.Graph (notably the "beta") modules - noting that each different type of MFA has its own property structure.

 

Note, you can also filter for explicit MFA types on the Get-MgUserAuthenticationMethod commandlet directly, which would be more efficient than client-side filtering (which is horribly inefficient).

 

The script will necessarily be wickedly slow in large environments for reasons I've explained in a previous post above, but you can always filter down the users on the first line through leveraging the server-side filtering provided by the -Filter parameter (rather than using the -All parameter as I have for this example).

 

Example

 

Get-MgBetaUser -All -Property id, userPrincipalName |
    ForEach-Object {
        $user = $_;

        $Hash = @{
            userId = $user.id;
            userPrincipalName = $user.UserPrincipalName;
            mfaEnabled = $false;
            authMethodId = $null;
            authMethod = $null;
        };

        if ($null -ne ($mfaData = (Get-MgBetaUserAuthenticationMethod -UserId $user.id | Where-Object { $_.id -ne "28c10230-6103-485e-b985-444c60001490"})))
        {
            $Hash["mfaEnabled"] = $true;

            $mfaData |
                ForEach-Object {
                    $HashCopy = $Hash.Clone();

                    $ap = $_.AdditionalProperties;
                    $HashCopy["authMethodId"] = $_.id;
                    $HashCopy["authMethod"] = $ap["@odata.type"].Split(".")[-1];
                    $null = $ap.Remove("@odata.type");
        
                    $ap.Keys.ForEach({
                        $null = $HashCopy.Add($_, $ap[$_]);
                    });
        
                    [PSCustomObject] $HashCopy;
                }
        }
        else
        {
            [PSCustomObject] $Hash;
        }
    };

 

 

Example output #1

This is from using Format-List and shows the extra, per-MFA type attributes available. Format-Table won't show them all since its heading is derived from the first returned result.

 

LainRobertson_1-1708505878558.png

 

 

Example output #2

This is from the Format-Table appendage and only shows the properties common to all rows.

 

You wouldn't use Format-Table to generate a report but it does illustrate how a common format could look if you weren't interested in the additional data Graph provides.

 

LainRobertson_3-1708505000967.png

 

 

Because I do not use MSOnline, I don't know to what extent the above examples compare to what you can achieve with MSOnline, and without a report sample to compare to, I can only make assumptions.

 

You can readily add more data from the user account but the purpose of these examples is to illustrate what kind of raw MFA data is currently available via Graph.

 

Cheers,

Lain

@Jonesy6123 

 

I need to form the habit of checking the REST APIs first, not after I've posted based on the modules.

 

That entire script above could easily enough be replaced with a one-liner that calls an endpoint that I can't easily see represented in the Graph modules.

 

 

Not only does this trivialise the code, it's significantly faster and comes with a much wider array of status flags - which I imagine might even be more than MSOnline provides.

 

 

 

(Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails" -OutputType PSObject).value;

 

 

Edited: I didn't have my thinking cap on when I wrote "one-liner", since of course, the result size could readily be larger than the page size.

 

Here's a "proper" implementation of the REST approach:

 

$uri = "https://graph.microsoft.com/beta/reports/authenticationMethods/userRegistrationDetails";

while ($null -ne ($Response = Invoke-MgGraphRequest -Method GET -Uri $uri -OutputType PSObject)) {
    $Response.value;
    
    if (-not $Response.'@odata.nextLink')
    {
        break;
    }

    $uri = $Response.'@odata.nextLink';
}

 

Cheers,

Lain

This endpoint is remarkably good - so much so that I can leverage it in the identity management space,.

 

Where the previous approach of having to fetch a user before fetching their MFA details (using Get-MgBetaUserAuthenticationEmailMethod) was prohibitively slow, the speed from using this endpoint is blindingly fast.

 

I've never run the former approach over a large tenant, as even in a small tenant it takes minutes just to get a thousand people's MFA details. However, using the userRegistrationDetails endpoint, 117k accounts were enumerated in 3min 30 sec, which is incomparably faster.

 

This allows an IDM to make access and authorisation decisions that incorporate granular MFA settings, which is something not achievable using Azure dynamic groups (as one example).

 

Cheers,

Lain

Hey Lain,

Once again, I want to seriously thank you for your commitment to this post!

I am currently using the last approach (ListUserRegistrationDetails), but it has been deemed insufficient in comparison to the report that we used to use where a specific property of "Enabled", "Disabled" or "Enforced" can be found using the StrongAuthentication.State property.

Without that property, or anything similar in MSGraph, I'm currently unable to report on user MFA status within our organisation, which has again been deemed insufficient. As MSOnline is being deprecated, my hope is that this kind of information will eventually be available in MSGraph.

The previous method you mentioned around assuming a person's MFA status based on registered methods is also insufficient, as it's reporting on the tokens used. In our case, we need to specifically check whether a user CURRENTLY has MFA enabled, not whether they have EVER had it enabled in the past. My only solution to that problem is that we delete the token history everytime these situations of users having new devices and needing to re-register, etc. Do you have any thoughts on this?

@Jonesy6123 

 

Hi, Jack.

 

Having a read of the per user endpoint (doco below), it's implied that only enabled methods are returned:

 

 

I ran a quick test against my account using the "authenticationMethods" endpoint by adding, checking, removing and re-checking (waiting a couple of minutes between checks as things don't update instantly) the Microsoft Authenticator method via the user interface located under avatar > Settings > Security Info, and that does indeed seem to be the case.

 

Considering the notion of what constitutes "MFA strength" (doco below) is fluid, it seems to me (with the usual disclaimer that I am no longer familiar with MSOnline and therefore may well be wrong) that there is enough data there for you to make the determination of whether strong authentication is enabled, it's just that unlike what you're describing in MSOnline, you have to do that coding yourself rather than leveraging the convenient MSOnline-provided rollup contained within StrongAuthentication.

 

 

The top result is after I added MS Authenticator as a sign-in method and the bottom is after I removed it again.

 

LainRobertson_0-1709086233595.png

 

 

Cheers,

Lain