← All articles Series: Microsoft 365 audit · 2/4

Microsoft 365 audit: identity and access

Most Microsoft 365 compromises start with an account. Here are the identity checks we run first, and how to reproduce them.

Global administrators

A global administrator can do anything in the tenant. Keep their number low: Microsoft recommends fewer than five, and at least two so you don’t lose access. These accounts should be separate from the accounts used day to day for email and browsing.

PowerShell
# "Global Administrator" role template (same in every tenant)
$ga = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"

Get-MgDirectoryRoleMember -DirectoryRoleId $ga.Id |
  ForEach-Object { $_.AdditionalProperties.userPrincipalName }

MFA coverage

The question is not only “is MFA turned on?” but “who still has no method registered?” An account with no registered method is one an attacker can sometimes enrol in the user’s place.

PowerShell
Get-MgReportAuthenticationMethodUserRegistrationDetail -All |
  Where-Object { -not $_.IsMfaRegistered } |
  Select-Object UserPrincipalName, IsAdmin |
  Sort-Object IsAdmin -Descending

We sort by IsAdmin so privileged accounts show up first. An administrator without MFA is always a critical finding.

Security defaults and Conditional Access

MFA must be enforced, not just available. It is enforced either by security defaults (for small tenants without a P1 licence) or by Conditional Access policies:

PowerShell
# Security defaults
Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy | Select-Object IsEnabled

# Conditional Access policies and their state
Get-MgIdentityConditionalAccessPolicy | Select-Object DisplayName, State

If security defaults are off and no policy is enabled, nothing enforces MFA. Also watch for policies left in enabledForReportingButNotEnforced mode: they log, but block nothing.

Legacy authentication

Older protocols (IMAP, POP, SMTP with basic authentication, older ActiveSync) do not support MFA. Microsoft has retired basic authentication for most Exchange Online protocols, but an explicit block policy remains the best guarantee. This command looks for an enabled policy that blocks “other clients”:

PowerShell
Get-MgIdentityConditionalAccessPolicy | Where-Object {
    $_.State -eq 'enabled' -and
    $_.Conditions.ClientAppTypes -contains 'other' -and
    $_.GrantControls.BuiltInControls -contains 'block'
} | Select-Object DisplayName

No result means no Conditional Access policy explicitly blocks these clients. In that case, check the Entra sign-in log, filtered by client app, to see whether they are still in use.

Guest accounts

Guests pile up project after project and are rarely removed. A forgotten guest account is still a way into your Teams and SharePoint sites.

PowerShell
Get-MgUser -All -Filter "userType eq 'Guest'" `
  -Property DisplayName, Mail, CreatedDateTime, SignInActivity |
  Select-Object DisplayName, Mail, CreatedDateTime,
    @{ n = 'LastSignIn'; e = { $_.SignInActivity.LastSignInDateTime } } |
  Sort-Object LastSignIn

Guests who have never signed in, or not for several months, are good candidates for review. Entra ID access reviews can automate this clean-up.

Application consent

If users can grant permissions to third-party apps themselves, a single phishing link can give an attacker lasting access to email, without any password:

PowerShell
(Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions.PermissionGrantPoliciesAssigned

If the output contains ManagePermissionGrantsForSelf.microsoft-user-default-legacy, users can consent to almost any application. We recommend limiting consent to verified publishers and low-risk permissions, or turning it off and enabling the admin consent request workflow.

Want your Microsoft 365 tenant audited?

We apply this methodology in your environment, read-only, and hand you a prioritized action plan.

Contact us