Public/Entra/Group/Get-MgGroupOwnerInfo.ps1
|
<#
.SYNOPSIS Retrieves the owners of all or a specific Entra ID group. .DESCRIPTION The Get-MgGroupOwnerInfo function queries Microsoft Entra ID via the Microsoft Graph API to return the owners of each group. Results can be filtered by a specific group ID or display name, and are exported to Excel by default. Groups with no owners are included in the output with an empty owner list, which is useful for identifying groups that lack an accountable owner. Note: This function is named Get-MgGroupOwnerInfo to avoid conflict with the built-in Microsoft Graph PowerShell cmdlet Get-MgGroupOwner. .PARAMETER GroupId Optional. The unique object ID of a specific Entra ID group to retrieve owners for. If omitted, all groups are processed. .PARAMETER DisplayName Optional. Filter groups by display name (exact match). Use with GroupId to narrow the scope. If omitted, all groups are processed. .PARAMETER ExportToExcel When specified, exports the results to an Excel file in the user's profile directory. Requires the ImportExcel module. .EXAMPLE Get-MgGroupOwnerInfo Retrieves the owners for all Entra ID groups and outputs them to the console. .EXAMPLE Get-MgGroupOwnerInfo -ExportToExcel Retrieves the owners of all groups and exports the results to an Excel file. .EXAMPLE Get-MgGroupOwnerInfo -GroupId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' Retrieves owners for a specific group by its object ID and exports to Excel. .EXAMPLE Get-MgGroupOwnerInfo -DisplayName 'Marketing Team' Retrieves owners for the group named 'Marketing Team' and outputs to the console only. .NOTES Requires Connect-MgGraph with scopes: 'Group.Read.All', 'User.Read.All' .LINK https://ps365.clidsys.com/docs/commands/Get-MgGroupOwnerInfo #> function Get-MgGroupOwnerInfo { [CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$GroupId, [Parameter(Mandatory = $false)] [string]$DisplayName, [Parameter(Mandatory = $false)] [switch]$ExportToExcel ) [System.Collections.Generic.List[PSCustomObject]]$results = @() $headers = @{ ConsistencyLevel = 'eventual' } # Fetch groups: all, by ID, or by display name [System.Collections.Generic.List[Object]]$groups = @() if ($GroupId) { Write-Host -ForegroundColor Cyan "Fetching group with ID: $GroupId" try { $group = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/groups/$GroupId`?`$select=id,displayName,groupTypes,securityEnabled,mailEnabled,mail" -ErrorAction Stop $groups.Add($group) } catch { Write-Error "Group with ID '$GroupId' not found." return } } else { Write-Host -ForegroundColor Cyan 'Fetching all groups...' $selectFields = 'id,displayName,groupTypes,securityEnabled,mailEnabled,mail' $filter = '' if ($DisplayName) { $filter = "&`$filter=displayName eq '$DisplayName'" } $uri = "https://graph.microsoft.com/v1.0/groups?`$select=$selectFields&`$top=999&`$count=true$filter" do { $response = Invoke-MgGraphRequest -Method GET -Uri $uri -Headers $headers foreach ($g in $response.value) { $groups.Add($g) } $uri = $response.'@odata.nextLink' } while ($uri) } $totalCount = $groups.Count Write-Host -ForegroundColor Cyan "Found $totalCount group(s). Retrieving owners..." $processed = 0 foreach ($group in $groups) { $processed++ Write-Progress -Activity 'Retrieving group owners' -Status "$processed / $totalCount" -PercentComplete (($processed / $totalCount) * 100) # Determine group type $groupType = switch ($true) { { $group.groupTypes -contains 'Unified' -and $group.groupTypes -contains 'DynamicMembership' } { 'M365 Dynamic Group' } { $group.groupTypes -contains 'Unified' } { 'Microsoft 365 Group' } { $group.groupTypes -contains 'DynamicMembership' } { 'Dynamic Security Group' } { $group.mailEnabled -and -not $group.securityEnabled } { 'Distribution Group' } { $group.mailEnabled -and $group.securityEnabled } { 'Mail-enabled Security Group' } { $group.securityEnabled } { 'Security Group' } default { 'Other' } } # Retrieve owners for the group try { $ownersUri = "https://graph.microsoft.com/v1.0/groups/$($group.id)/owners?`$select=id,displayName,userPrincipalName,mail" $ownerResponse = Invoke-MgGraphRequest -Method GET -Uri $ownersUri -ErrorAction Stop $owners = $ownerResponse.value if ($owners.Count -eq 0) { $object = [PSCustomObject][ordered]@{ GroupDisplayName = $group.displayName GroupId = $group.id GroupType = $groupType GroupMail = $group.mail OwnerDisplayName = $null OwnerUserPrincipalName = $null OwnerId = $null OwnerMail = $null TotalOwners = 0 } $results.Add($object) } else { foreach ($owner in $owners) { $object = [PSCustomObject][ordered]@{ GroupDisplayName = $group.displayName GroupId = $group.id GroupType = $groupType GroupMail = $group.mail OwnerDisplayName = $owner.displayName OwnerUserPrincipalName = $owner.userPrincipalName OwnerId = $owner.id OwnerMail = $owner.mail TotalOwners = $owners.Count } $results.Add($object) } } } catch { Write-Warning "Failed to retrieve owners for group '$($group.displayName)' ($($group.id)): $_" } } Write-Progress -Activity 'Retrieving group owners' -Completed Write-Host -ForegroundColor Green "Done. $($results.Count) row(s) returned for $totalCount group(s)." # Export to Excel if requested if ($ExportToExcel.IsPresent) { $now = Get-Date -Format 'yyyy-MM-dd_HHmmss' $excelFilePath = "$($env:userprofile)\$now-MgGroupOwnerInfo_Report.xlsx" Write-Host -ForegroundColor Cyan "Exporting to Excel file: $excelFilePath" $results | Export-Excel -Path $excelFilePath -AutoSize -AutoFilter -WorksheetName 'Entra-GroupOwners' } return $results } |