<# .SYNOPSIS Reports what a Microsoft Entra ID group is and what it's wired to: members, owners, group-based licenses, app assignments and Intune policy assignments. .DESCRIPTION Read-only. Looks the group up by name or object ID, then returns one row per finding with Section, Name and Detail columns, so the output sorts, filters and exports to CSV cleanly. Intune assignments cover device configuration profiles, compliance policies and (unless -SkipSettingsCatalog) Settings catalog policies. .PARAMETER Identity The group's display name or object ID. .PARAMETER SkipIntune Don't look at Intune assignments. Handy if you don't have Intune read rights. .PARAMETER SkipSettingsCatalog Skip Settings catalog policies, which are only exposed on the Graph beta endpoint. .EXAMPLE .\Get-EntraGroupReport.ps1 -Identity 'Sales Team' .EXAMPLE .\Get-EntraGroupReport.ps1 -Identity 'Sales Team' | Export-Csv .\sales-team.csv -NoTypeInformation #> [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [string]$Identity, [switch]$SkipIntune, [switch]$SkipSettingsCatalog ) $ErrorActionPreference = 'Stop' $scopes = 'Directory.Read.All', 'DeviceManagementConfiguration.Read.All' if (-not (Get-MgContext)) { Connect-MgGraph -Scopes $scopes -NoWelcome } function New-Row([string]$Section, [string]$Name, [string]$Detail = '') { [pscustomobject]@{ Section = $Section; Name = $Name; Detail = $Detail } } # --- Find the group (by ID if it looks like a GUID, otherwise by exact display name) $props = 'Id,DisplayName,Description,GroupTypes,MailEnabled,SecurityEnabled,Mail,MembershipRule,OnPremisesSyncEnabled,AssignedLicenses,CreatedDateTime' if ($Identity -as [guid]) { $group = Get-MgGroup -GroupId $Identity -Property $props } else { $found = @(Get-MgGroup -Filter "displayName eq '$($Identity -replace "'", "''")'" -Property $props -All) if ($found.Count -eq 0) { throw "No group named '$Identity'." } if ($found.Count -gt 1) { throw "$($found.Count) groups are named '$Identity'. Use the object ID instead: $($found.Id -join ', ')" } $group = $found[0] } $kind = if ($group.GroupTypes -contains 'Unified') { 'Microsoft 365' } elseif ($group.MailEnabled) { 'Mail-enabled security / distribution' } else { 'Security' } New-Row 'Group' $group.DisplayName "$kind group, ID $($group.Id), created $($group.CreatedDateTime)" if ($group.MembershipRule) { New-Row 'Group' 'Dynamic rule' $group.MembershipRule } if ($group.OnPremisesSyncEnabled) { New-Row 'Group' 'Source' 'Synced from on-premises AD' } # --- Owners and members foreach ($o in Get-MgGroupOwner -GroupId $group.Id -All) { New-Row 'Owner' $o.AdditionalProperties['displayName'] $o.AdditionalProperties['userPrincipalName'] } foreach ($m in Get-MgGroupMember -GroupId $group.Id -All) { $type = ($m.AdditionalProperties['@odata.type'] -replace '#microsoft.graph.', '') $upn = $m.AdditionalProperties['userPrincipalName'] $detail = if ($upn) { "$type, $upn" } else { $type } New-Row 'Member' $m.AdditionalProperties['displayName'] $detail } # --- Licenses assigned through this group if ($group.AssignedLicenses) { $skuNames = @{} Get-MgSubscribedSku -All | ForEach-Object { $skuNames[[string]$_.SkuId] = $_.SkuPartNumber } foreach ($lic in $group.AssignedLicenses) { $disabled = if ($lic.DisabledPlans) { "$($lic.DisabledPlans.Count) service plan(s) disabled" } else { 'All service plans' } $name = if ($skuNames[[string]$lic.SkuId]) { $skuNames[[string]$lic.SkuId] } else { [string]$lic.SkuId } New-Row 'License' $name $disabled } } # --- Enterprise apps the group is assigned to foreach ($a in Get-MgGroupAppRoleAssignment -GroupId $group.Id -All) { $role = if ($a.AppRoleId -eq [guid]::Empty) { 'Default access' } else { "App role $($a.AppRoleId)" } New-Row 'App' $a.ResourceDisplayName $role } # --- Intune policies that target the group if (-not $SkipIntune) { $sources = [ordered]@{ 'Configuration profile' = 'v1.0/deviceManagement/deviceConfigurations?$expand=assignments&$select=id,displayName' 'Compliance policy' = 'v1.0/deviceManagement/deviceCompliancePolicies?$expand=assignments&$select=id,displayName' } if (-not $SkipSettingsCatalog) { $sources['Settings catalog'] = 'beta/deviceManagement/configurationPolicies?$expand=assignments&$select=id,name' } foreach ($label in $sources.Keys) { $uri = $sources[$label] try { while ($uri) { $page = Invoke-MgGraphRequest -Method GET -Uri $uri foreach ($policy in $page.value) { foreach ($assignment in $policy.assignments) { if ($assignment.target.groupId -ne $group.Id) { continue } $mode = if ($assignment.target.'@odata.type' -like '*exclusion*') { 'Excluded' } else { 'Included' } $policyName = if ($policy.displayName) { $policy.displayName } else { $policy.name } New-Row $label $policyName $mode } } $uri = $page.'@odata.nextLink' } } catch { Write-Warning "Couldn't read $label assignments: $($_.Exception.Message)" } } }