๐ก๏ธ Recipe ยท Security & Compliance
Investigate M365 Security Posture: Secure Score, Risky Users, and Admin MFA
Audit your Microsoft 365 tenant's security posture by reviewing Secure Score, privileged role assignments, admin MFA coverage, and high-privilege app registrations โ then prioritize remediation.
Complexity
Intermediate
Impact
security-posture + privileged-access + mfa-coverage + app-governance + compliance-audit
Context
Why This Matters
Microsoft 365 tenants drift toward insecurity over time: new admin accounts get added, apps accumulate privileged Graph permissions, Secure Score controls fall behind defaults, and MFA coverage gaps appear on the highest-value accounts. A quarterly (or incident-driven) posture review lets you catch these issues before an attacker does.
This recipe walks through the four highest-leverage checks for a standard M365 / Entra ID tenant:
- Secure Score โ Microsoft's aggregate rating of your identity, app, device, and data controls, compared against tenants of similar size.
- Privileged role membership โ who currently holds Global Administrator and other Tier-0 directory roles.
- Admin MFA registration โ confirmation that every privileged account has a phishing-resistant authentication method registered (Microsoft Authenticator, FIDO2, or Windows Hello) rather than just SMS or password.
- App registration permissions โ third-party and internal applications that hold sensitive Microsoft Graph application roles, which represent a major supply-chain and lateral-movement risk.
Run it when onboarding a new tenant, after a suspected incident, on a recurring schedule (monthly/quarterly), or any time leadership asks for a security readout.
Expected Outcomes
What you will have after completing this recipe
- A captured current Secure Score value, max score, and comparative benchmark against similar tenants.
- A list of the lowest-scoring Secure Score controls ranked by remediation impact.
- A complete roster of Global Administrators (and optionally all directory role holders) with account-enabled status.
- A per-admin MFA method inventory identifying any privileged account relying only on password or SMS.
- An app registration report flagging applications with excessive Microsoft Graph application permissions.
- A prioritized remediation list (Critical / High / Medium) you can hand to leadership or execute directly.
Risks & Considerations
Warnings and gotchas
- Premium license dependency. Risky user, risky sign-in, and some authentication-methods-report endpoints require Entra ID P1 or P2. On Entra ID Free tenants these calls return
403 Permission deniedwith a message about premium licensing โ fall back to Secure Score, directory role enumeration, and per-user authentication methods. - Read-only investigation. This recipe only reads data. Do not remediate findings (disable accounts, remove role holders, revoke app consents) without a change ticket and stakeholder sign-off โ removing a legitimate service account can cause outages.
- App registration permissions are the #1 sleeper risk. An app with
RoleManagement.ReadWrite.Directoryor broadDirectory.ReadWrite.Allroles is effectively Global Admin. Treat every high-privilege app as a privileged identity. - Don't leak user data in reports. The outputs contain UPNs, phone numbers, and device identifiers. Store them only in approved secure locations (e.g., encrypted SharePoint site with restricted access).
- Secure Score lag. Control implementation status can take up to 48 hours to reflect recent changes; re-check the next day after remediations.
Required Permissions
| Permission | Why It's Needed |
|---|---|
| SecurityEvents.Read.All | Read Microsoft Secure Score and control recommendations. |
| Directory.Read.All | Enumerate directory roles, role members, and user details. |
| RoleManagement.Read.Directory | Resolve role definitions and assignments for privileged roles. |
| UserAuthenticationMethod.Read.All | Inspect which MFA methods are registered on each administrator account. |
| Application.Read.All | Inventory app registrations and their requested Graph permissions. |
| AuditLog.Read.All | Query sign-in logs and directory audit events (requires Entra ID P1 for full data). |
| IdentityRiskyUser.Read.All | List users flagged as risky by Entra ID Protection (requires Entra ID P2). |
The fastest way to get this done: just ask Dex. Copy the prompt below and paste it into your Dex conversation.
For IT Admins
Paste into Dex Pro
Step-by-step: Investigate security posture via admin portals
1. Check Microsoft Secure Score
- Sign in to the Microsoft Defender portal at
https://security.microsoft.comas a Security Reader or Global Reader. - In the left navigation: .
- Record the current score, maximum achievable, and comparison to similar organizations.
- Open the Recommended actions tab and sort by Points achievable descending. Export the list (top-right โ Export).
2. Audit privileged role holders
- Go to the Entra admin center at
https://entra.microsoft.com. - Navigate to .
- Click into Global Administrator. Record every member's display name, UPN, and account-enabled status.
- Repeat for Privileged Role Administrator, Privileged Authentication Administrator, Exchange Administrator, SharePoint Administrator, and Application Administrator.
3. Verify MFA on each admin
- Still in the Entra admin center: .
- Select each admin identified in Step 2 โ Authentication methods in the user blade.
- Confirm a phishing-resistant method is registered: Microsoft Authenticator (push), FIDO2 key, Windows Hello, or Certificate-based. Flag any admin with only password or SMS.
4. Review app registrations with high privileges
- Navigate to .
- Open each application โ API permissions.
- Flag any app with Application-type permissions containing
.ReadWrite.All,RoleManagement.*,Directory.ReadWrite.All, orApplication.ReadWrite.All. - Cross-check Certificates & secrets for expiring or long-lived credentials.
5. Build the remediation list
Combine findings into a prioritized table (Critical / High / Medium) with owner, remediation, and target date. Share with the security owner for sign-off before taking action.
Graph API endpoints used in this recipe
1. Retrieve latest Secure Score
GET https://graph.microsoft.com/v1.0/security/secureScores?$top=1Returns the most recent daily snapshot including currentScore, maxScore, averageComparativeScores, and a controlScores array listing every control and its implementation status.
2. List Global Administrators
GET /v1.0/directoryRoles?$filter=displayName eq 'Global Administrator'&$expand=members($select=id,displayName,userPrincipalName,accountEnabled)Expands role members inline. Repeat for other privileged roles or enumerate all roles with /v1.0/directoryRoles?$expand=members.
3. Get authentication methods for a user
GET /v1.0/users/{upn}/authentication/methodsReturns an array of method objects. Inspect the @odata.type on each to identify the method class:
#microsoft.graph.passwordAuthenticationMethod#microsoft.graph.phoneAuthenticationMethod(SMS / voice โ weak)#microsoft.graph.microsoftAuthenticatorAuthenticationMethod(strong)#microsoft.graph.fido2AuthenticationMethod(strongest)#microsoft.graph.windowsHelloForBusinessAuthenticationMethod(strong)
4. Enumerate app registrations and their requested permissions
GET /v1.0/applications?$select=id,displayName,appId,requiredResourceAccessThe requiredResourceAccess array lists each API (by resourceAppId; 00000003-0000-0000-c000-000000000000 is Microsoft Graph) and the role/scope IDs requested. Resolve IDs to human-readable names via:
GET /v1.0/servicePrincipals?$filter=appId eq '00000003-0000-0000-c000-000000000000'&$select=appRoles,oauth2PermissionScopes5. (P1/P2 only) Risky users and risky sign-ins
GET /v1.0/identityProtection/riskyUsers?$filter=riskLevel ne 'low'&$top=50
GET /v1.0/auditLogs/signIns?$filter=riskLevelDuringSignIn eq 'high'&$top=50Returns 403 Permission denied โ Tenant is not a B2C tenant and doesn't have premium license on Entra ID Free.
Example response shape (Secure Score)
{
"currentScore": 184,
"maxScore": 272,
"activeUserCount": 87,
"averageComparativeScores": [
{ "basis": "AllTenants", "averageScore": 53.47 }
],
"controlScores": [
{ "controlName": "mdo_safedocuments", "score": 0, "on": "false" }
]
}PowerShell walkthrough
Use the Microsoft.Graph PowerShell module. The module ships as a metamodule โ you only need to install it once, then import the sub-modules you actually use.
1. Install and connect
# One-time install (CurrentUser scope avoids admin elevation)
Install-Module Microsoft.Graph -Scope CurrentUser
# Connect with the scopes this recipe needs
Connect-MgGraph -Scopes "SecurityEvents.Read.All",`
"Directory.Read.All",`
"RoleManagement.Read.Directory",`
"UserAuthenticationMethod.Read.All",`
"Application.Read.All"2. Pull Secure Score
$score = Get-MgSecuritySecureScore -Top 1 | Select-Object -First 1
"Current: $($score.CurrentScore) / $($score.MaxScore)"
$score.ControlScores | Where-Object { $_.Score -eq 0 } |
Select-Object ControlName, ControlCategory, Description |
Format-Table -AutoSize3. List Global Admins
$ga = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"
Get-MgDirectoryRoleMember -DirectoryRoleId $ga.Id |
ForEach-Object { $_.AdditionalProperties } |
Select-Object displayName, userPrincipalName, accountEnabled4. Check MFA on each admin
$admins = Get-MgDirectoryRoleMember -DirectoryRoleId $ga.Id
foreach ($m in $admins) {
$upn = $m.AdditionalProperties.userPrincipalName
$methods = Get-MgUserAuthenticationMethod -UserId $upn
$types = $methods.AdditionalProperties.'@odata.type' -replace '#microsoft.graph.',''
[pscustomobject]@{ User = $upn; Methods = ($types -join ',') }
}5. Flag high-privilege apps
$graphId = '00000003-0000-0000-c000-000000000000'
Get-MgApplication -All |
Where-Object {
($_.RequiredResourceAccess | Where-Object { $_.ResourceAppId -eq $graphId }).ResourceAccess.Count -gt 5
} |
Select-Object DisplayName, AppId,
@{n='GraphRoles';e={ ($_.RequiredResourceAccess | ? ResourceAppId -eq $graphId).ResourceAccess.Count }}The downloadable script below wraps all of this into a single auditable run with a CSV export per section.
Next steps