Entra / Microsoft 365 · Groups
Report AU roles and members
Use the Microsoft Graph PowerShell SDK to report administrative units, role assignments for AU management, and AU membership.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -Scopes Directory.Read.All -NoWelcome
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-MgGraph -Scopes Directory.Read.All -NoWelcome$OutputFile = "c:\temp\AdminUnitAssignments.csv"$Version = "V1.1"$HtmlReportFile = "c:\temp\AdminUnitAssignments.html"Write-Host "Looking for administrative units and role assignments..."[array]$AdminUnits = Get-MgDirectoryAdministrativeUnit | Sort-Object DisplayName[array]$Assignments = Get-MgRoleManagementDirectoryRoleAssignmentWrite-Host ("Now examining details of {0} administrative units and {1} role assignments..." -f $AdminUnits.count, $Assignments.count)[int]$i=0$Report = [System.Collections.Generic.List[Object]]::new()ForEach ($AU in $AdminUnits) {$i++Write-Output ("Processing admin unit: {0} ({1}/{2})..." -f $AU.DisplayName, $i, $AdminUnits.count)$AUAssignments = $Null; $AUAssignments = $Null$AUId = "/administrativeUnits/" + $AU.IdIf ($AU.IsMemberManagementRestricted -eq $True) {$Scope = "Restricted"} Else {$Scope = "Directory"}$AUAssignments = $Assignments | Where-Object {$_.directoryscopeid -eq $AuId}$RoleData = $Null; [array]$Roles = $NullForEach ($AUAssignment in $AUAssignments) {# Check if assignment is for a user account$Result = Get-MgUser -UserId $AUAssignment.PrincipalId -ErrorAction SilentlyContinue$Type = "User"# Check if it's a groupIf (!($Result)) {$Result = Get-MgGroup -GroupId $AUAssignment.PrincipalId -ErrorAction SilentlyContinue$Type = "Group"}If (!($Result)) { # No user, so try service principal$Result = Get-MgServicePrincipal -ServicePrincipalId $AuAssignment.PrincipalId -ErrorAction SilentlyContinue$Type = "Service Principal"}$Role = Get-MgDirectoryRoleTemplate -DirectoryRoleTemplateId $AuAssignment.RoleDefinitionIdIf ($Result -and $Type -ne "Group") {$RoleData = ("{0}/{1} ({2})" -f $Result.DisplayName, $Type, $Role.DisplayName)$Roles += $RoleData} ElseIf ($Result -and $Type -eq "Group") {$AuGroupMembers = Get-MgGroupMember -GroupId $AuAssignment.PrincipalId -AllForEach ($Member in $AuGroupMembers.additionalProperties.displayName) {$RoleData = ("{0}/{1} ({2})" -f $Member, $Type, $Role.DisplayName)$Roles += $RoleData }}}# Check if the membership is dynamic or staticIf ($AU.additionalProperties.membershipType) {$AuMembershipType = "Dynamic"$AuMembershipRule = $AU.additionalProperties.membershipRule} Else {$AuMembershipType = "Static"$AuMembershipRule = $Null}# Get membership[array]$AuMembers = Get-MgBetaAdministrativeUnitMember -AdministrativeUnitId $Au.Id -All[array]$MemberUsers = $AuMembers.additionalProperties | Where-Object {$_.'@odata.type' -eq "#microsoft.graph.user"}[array]$MemberGroups = $AuMembers.additionalProperties | Where-Object {$_.'@odata.type' -eq "#microsoft.graph.group"}$AuMembersNames = $AuMembers.additionalproperties.displayName -Join ", "If ($Roles) {$RoleDisplayNames = $Roles -join ", "} Else {$RoleDisplayNames = "Administrator Roles"}$ReportLine = [PSCustomObject]@{DisplayName = $AU.DisplayNameDescription = $AU.DescriptionId = $AU.IdScope = $ScopeAssignments = $RoleDisplayNamesMembers = $AuMembersNames'User members' = $MemberUsers.count'Group members' = $MemberGroups.countMembershipType = $AuMembershipTypeMembershipRule = $AuMembershipRule}$Report.Add($ReportLine)} #End ForEach AU[string]$Organization = (Get-MgOrganization).DisplayName# Generate the report files$HtmlHeading ="<html><style>BODY{font-family: Arial; font-size: 8pt;}H1{font-size: 22px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}H2{font-size: 18px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}H3{font-size: 16px; font-family: 'Segoe UI Light','Segoe UI','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;}TABLE{border: 1px solid black; border-collapse: collapse; font-size: 8pt;}TH{border: 1px solid #969595; background: #dddddd; padding: 5px; color: #000000;}TD{border: 1px solid #969595; padding: 5px; }td.pass{background: #B7EB83;}td.warn{background: #FFF275;}td.fail{background: #FF2626; color: #ffffff;}td.info{background: #85D4FF;}</style><body><div align=center><p><h1>Microsoft Entra ID Administrative Units Report</h1></p><p><h2>for $Organization</h2></p><p><h3>Generated: " + (Get-Date -format 'dd-MMM-yyyy hh:mm tt') + "</h3></p></div>"$HtmlReport = $HtmlHeading$HtmlData = $Report | ConvertTo-html -Fragment$Htmltail = "<p><p>Report created for: " + ($Organization.DisplayName) + "</p><p>" +"<p>Number of Entra ID Administrative Units: " + $AdminUnits.count + "</p>" +"<p>-----------------------------------------------------------------------------------------------------------------------------" +"<p>Microsoft Entra ID Administrative Units<b>" + $Version + "</b>"$HtmlReport = $HtmlHeading + $HtmlData + $HtmlTail$HtmlReport | Out-File $HtmlReportFile -Encoding UTF8$Report | Export-CSV -NoTypeInformation $OutputFileWrite-Host ("All done. Reports available in {0} and {1}" -f $OutputFile, $HtmlReportFile)
Attribution
Author
Office365itpros