Home Finding guest user administrators
Post
Cancel

Finding guest user administrators

Guest users? As administrators? As Global Administrator even? Is that possible?!?? Yes it is! Even though it is exceedingly rare to do you can technically make a guest user in M365 any Entra ID administrator role you want. They even kind of encourage it by giving you the option in the guest user creation flow.

I’m not interested. Skip to the full script

Desktop View Fake captains, fake administrators. Couldn’t think of anything better matching

Why do they exist and how do we find them?

There are a number of reasons why you would make a guest user any administrator role. Historically before GDAP it was of course impossible to delegate certain roles to external partners. It may have made sense in some situations to create a guest user for an external partner, allthough the preferred solution would have of course been to create a regular account. In some cases however it really is just stupidity. A while back I ran into a customer who had made his personal MSN account Global Administrator. I can tell you I looked like that minion with the alarm lights on his head.

How can we find these guest users? We can’t open every tenant and check every role manually of course. We can leverage the Secure Application Model to run scripts over all the tenants that we manage. You can read more about it here and here. This post assumes you already have a SAM app in place and understand how to request tokens.

We start with a simple graph call. This gives us all administrator roles with the member property expanded.

1
$rawAdminList = (Invoke-RestMethod -Method "GET" -Headers $header -uri "https://graph.microsoft.com/beta/directoryRoles?`$expand=members").value

Given that this call will also give us system managed role(s) and role(s) with no members we will want to filter those out first. The notmatch filters out the Directory Synchronization Accounts role and the Where-Object filters out any roles where the members property is empty.

1
$adminList = $rawAdminList | Where-Object -Property roleTemplateId -notmatch 'd29b2b05-8046-44ba-8758-1e26182fcf32' | Where-Object { $_.members -ne $null }    

Next we’re going to first foreach through the roles and in the foreach we will ForEach-Object through the members. Any member that has the UserType property Guest is one where we want to alert on.

1
2
3
4
5
foreach ($adminRole in $adminList) {
    $adminRole.members | Where-Object { $_.UserType -eq 'Guest' } | ForEach-Object {
        # This is where you send out a warning/alert/etc. Important variables: $($_.userPrincipalName), $($adminRole.displayName) 
    }
}

And that’s it. Its that simple. You do this over a few hundred tenants and I’ll guarantee you will find something.

Full Script

1
2
3
4
5
6
7
8
9
$rawAdminList = (Invoke-RestMethod -Method "GET" -Headers $header -uri "https://graph.microsoft.com/beta/directoryRoles?`$expand=members").value

$adminList = $rawAdminList | Where-Object -Property roleTemplateId -notmatch 'd29b2b05-8046-44ba-8758-1e26182fcf32|9f06204d-73c1-4d4c-880a-6edb90606fd8' | Where-Object { $_.members -ne $null }    

foreach ($adminRole in $adminList) {
    $adminRole.members | Where-Object { $_.UserType -eq 'Guest' } | ForEach-Object {
        Send-TeamsWebHook -tenantId $tenantId -alertName "ExternalAccountAdministrator" -portalLink "N/A" -alertDetails "An alert was triggered because guest user $($_.userPrincipalName) is a $($adminRole.displayName) in the tenant." 
    }
}
This post is licensed under CC BY 4.0 by the author.