diff --git a/scripts/aad-audit-directory-role-assignments/README.md b/scripts/aad-audit-directory-role-assignments/README.md new file mode 100644 index 000000000..5160be8fb --- /dev/null +++ b/scripts/aad-audit-directory-role-assignments/README.md @@ -0,0 +1,143 @@ +# Audit Azure AD (Entra ID) Role Assignments + +## Summary + +This script audits all privileged Entra ID directory role assignments across a Microsoft 365 tenant using Microsoft Graph. It enumerates role assignments (direct and group-based), resolves the assigned principals (users, groups, service principals), and exports a clean, audit-ready report suitable for large tenants. + +## Why It Matters + +Security reviews, compliance attestations, and least-privilege initiatives require a single source of truth for who holds privileged access. Over time, directory roles are often granted via groups, inherited, or left assigned to inactive identities. This audit provides the evidence needed to identify excessive privilege, remediate risk, and satisfy auditors. + +## Benefits +- **Complete visibility** into privileged role assignments (not limited to currently activated roles). +- **Detects risky patterns** (guest users, service principals, group-based elevation). +- **Audit-ready output** for compliance and governance reviews. +- **Scales to large tenants** using Microsoft Graph paging. + +# [PowerShell Script](#tab/pnpps) + +```powershell + +param( + [Parameter(Mandatory)] + [string]$OutputPath +) + +Connect-MgGraph -Scopes @( + "RoleManagement.Read.Directory", + "Directory.Read.All", + "User.Read.All", + "Group.Read.All" +) + +$roleDefinitions = Get-MgRoleManagementDirectoryRoleDefinition -All | + Select-Object Id, DisplayName + +$roleDefinitionLookup = @{} +foreach ($rd in $roleDefinitions) { + $roleDefinitionLookup[$rd.Id] = $rd.DisplayName +} + +$assignments = Get-MgRoleManagementDirectoryRoleAssignment -All + +$results = foreach ($assignment in $assignments) { + + $roleName = $roleDefinitionLookup[$assignment.RoleDefinitionId] + + $principalType = $assignment.PrincipalType + $principalId = $assignment.PrincipalId + + $principalName = $null + $principalUPN = $null + $principalAppId = $null + + switch ($principalType) { + "User" { + $user = Get-MgUser -UserId $principalId -ErrorAction SilentlyContinue + if ($user) { + $principalName = $user.DisplayName + $principalUPN = $user.UserPrincipalName + } + } + "Group" { + $group = Get-MgGroup -GroupId $principalId -ErrorAction SilentlyContinue + if ($group) { + $principalName = $group.DisplayName + } + } + "ServicePrincipal" { + $sp = Get-MgServicePrincipal -ServicePrincipalId $principalId -ErrorAction SilentlyContinue + if ($sp) { + $principalName = $sp.DisplayName + $principalAppId = $sp.AppId + } + } + } + + [PSCustomObject]@{ + RoleName = $roleName + RoleDefinitionId= $assignment.RoleDefinitionId + AssignmentId = $assignment.Id + AssignmentType = if ($assignment.PrincipalType -eq "Group") { "GroupBased" } else { "Direct" } + PrincipalType = $principalType + PrincipalName = $principalName + UserPrincipalName = $principalUPN + AppId = $principalAppId + Scope = $assignment.DirectoryScopeId + } +} + +$results | + Sort-Object RoleName, PrincipalType, PrincipalName | + Export-Csv -Path $OutputPath -NoTypeInformation + +``` + +# [Usage](#tab/usage) + +```powershell + +# Prerequisites (once) +Install-Module Microsoft.Graph -Scope CurrentUser + +# Permissions required (delegated or app): +# RoleManagement.Read.Directory, Directory.Read.All, User.Read.All, Group.Read.All + +# Run +.\Audit-EntraRoleAssignments.ps1 -OutputPath ".\EntraRoleAssignments.csv" + +``` + +[!INCLUDE [More about PnP PowerShell](../../docfx/includes/MORE-PNPPS.md)] +*** + +## Output +The CSV report includes the following fields: +- Role name and scope. +- Assignment type (Direct / Group). +- Principal type (User / Group / ServicePrincipal). +- Principal details (UPN/AppId/DisplayName). +- Assignment ID. + +## Notes +- Group-based assignments list the **group** as the principal (by design). Expand group membership separately if needed. +- PIM **eligibility vs active** can be added by querying PIM endpoints if required. + +## Contributors + +| Author(s) | +|-----------| +| [Josiah Opiyo](https://github.com/ojopiyo) | + +*Built with a focus on automation, governance, least privilege, and clean Microsoft 365 tenants—helping M365 admins gain visibility and reduce operational risk.* + + +## Version history + +Version|Date|Comments +-------|----|-------- +1.0|Jan 03, 2026|Initial release + + +[!INCLUDE [DISCLAIMER](../../docfx/includes/DISCLAIMER.md)] + \ No newline at end of file diff --git a/scripts/aad-audit-directory-role-assignments/assets/example.png b/scripts/aad-audit-directory-role-assignments/assets/example.png new file mode 100644 index 000000000..9feb18d96 Binary files /dev/null and b/scripts/aad-audit-directory-role-assignments/assets/example.png differ diff --git a/scripts/aad-audit-directory-role-assignments/assets/preview.png b/scripts/aad-audit-directory-role-assignments/assets/preview.png new file mode 100644 index 000000000..72a9255df Binary files /dev/null and b/scripts/aad-audit-directory-role-assignments/assets/preview.png differ diff --git a/scripts/aad-audit-directory-role-assignments/assets/sample.json b/scripts/aad-audit-directory-role-assignments/assets/sample.json new file mode 100644 index 000000000..8773e5d19 --- /dev/null +++ b/scripts/aad-audit-directory-role-assignments/assets/sample.json @@ -0,0 +1,55 @@ +[ + { + "name": "aad-audit-directory-role-assignments", + "source": "pnp", + "title": "Audit Azure AD (Entra ID) Role Assignments", + "shortDescription": "This script audits all privileged Entra ID directory role assignments across a Microsoft 365 tenant", + "url": "https://pnp.github.io/script-samples/aad-audit-directory-role-assignments/README.html", + "longDescription": [ + "" + ], + "creationDateTime": "2026-01-03", + "updateDateTime": "2026-01-03", + "products": [ + "Graph" + ], + "metadata": [ + { + "key": "GRAPH-POWERSHELL", + "value": "1.0.0" + } + ], + "categories": [ + "Report" + ], + "tags": [ + "Get-MgRoleManagementDirectoryRoleAssignment", + "Get-MgRoleManagementDirectoryRoleDefinition", + "Get-MgUser", + "Get-MgGroup" + ], + "thumbnails": [ + { + "type": "image", + "order": 100, + "url": "https://raw.githubusercontent.com/pnp/script-samples/main/scripts/aad-audit-directory-role-assignments/assets/preview.png", + "alt": "Preview of the sample Audit Azure AD (Entra ID) Role Assignments" + } + ], + "authors": [ + { + "gitHubAccount": "ojopiyo", + "company": "", + "pictureUrl": "https://github.com/ojopiyo.png", + "name": "Josiah Opiyo" + } + ], + "references": [ + { + "name": "Want to learn more about Microsoft Graph PowerShell SDK and the cmdlets", + "description": "Check out the Microsoft Graph PowerShell SDK documentation site to get started and for the reference to the cmdlets.", + "url": "https://learn.microsoft.com/graph/powershell/get-started" + } + ] + } +]