Entra/Get-AdminRoleReport.ps1
|
<#
.SYNOPSIS Lists all active Entra ID directory role assignments and their members. .DESCRIPTION Queries Microsoft Graph for all activated directory roles and enumerates their members. Produces a flat report showing each role-member combination, which is critical for reviewing privileged access during security assessments. Requires Microsoft.Graph.Identity.DirectoryManagement module and RoleManagement.Read.Directory permission. .PARAMETER OutputPath Optional path to export results as CSV. If not specified, results are returned to the pipeline. .EXAMPLE PS> . .\Common\Connect-Service.ps1 PS> Connect-Service -Service Graph -Scopes 'RoleManagement.Read.Directory' PS> .\Entra\Get-AdminRoleReport.ps1 Displays all directory role assignments in the tenant. .EXAMPLE PS> .\Entra\Get-AdminRoleReport.ps1 -OutputPath '.\admin-roles.csv' Exports admin role membership to CSV for review. #> [CmdletBinding()] param( [Parameter()] [ValidateNotNullOrEmpty()] [string]$OutputPath ) $ErrorActionPreference = 'Stop' # Verify Graph connection if (-not (Assert-GraphConnection)) { return } # Ensure required Graph submodule is loaded (PS 7.x does not auto-import) Import-Module -Name Microsoft.Graph.Identity.DirectoryManagement -ErrorAction Stop # Retrieve all activated directory roles try { Write-Verbose "Retrieving activated directory roles..." $directoryRoles = Get-MgDirectoryRole -All } catch { Write-Error "Failed to retrieve directory roles: $_" return } $allRoles = @($directoryRoles) Write-Verbose "Found $($allRoles.Count) activated directory roles. Enumerating members..." $report = foreach ($role in $allRoles) { try { $members = Get-MgDirectoryRoleMember -DirectoryRoleId $role.Id -All } catch { Write-Warning "Failed to retrieve members for role '$($role.DisplayName)': $_" continue } if ($members.Count -eq 0) { Write-Verbose "Role '$($role.DisplayName)' has no members, skipping." continue } foreach ($member in $members) { $additionalProperties = $member.AdditionalProperties $memberDisplayName = $additionalProperties['displayName'] $memberUpn = $additionalProperties['userPrincipalName'] $memberType = $additionalProperties['@odata.type'] # Clean up the OData type to a friendly name $friendlyType = switch ($memberType) { '#microsoft.graph.user' { 'User' } '#microsoft.graph.servicePrincipal' { 'ServicePrincipal' } '#microsoft.graph.group' { 'Group' } default { $memberType } } [PSCustomObject]@{ RoleName = $role.DisplayName RoleId = $role.Id MemberDisplayName = $memberDisplayName MemberUPN = $memberUpn MemberType = $friendlyType MemberId = $member.Id } } } $report = @($report) | Sort-Object -Property RoleName, MemberDisplayName Write-Verbose "Found $($report.Count) total role assignments" if ($OutputPath) { $report | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 Write-Output "Exported admin role report ($($report.Count) assignments) to $OutputPath" } else { Write-Output $report } |