For Microsoft 365 Groups:
# 1. Connect (ensure you have the right scopes)
Connect-MgGraph -Scopes "Group.Read.All", "User.Read.All"
# 2. Build the User Cache (The "Secret Sauce" for speed)
# We fetch ID, Name, and Email once and store them in a Hashtable (Key = ID)
Write-Host "Building user cache..." -ForegroundColor Cyan
$UserCache = @{}
Get-MgUser -All -Property Id, DisplayName, Mail | ForEach-Object {
$UserCache[$_.Id] = $_
}
# 3. Retrieve all groups
Write-Host "Fetching groups and mapping owners..." -ForegroundColor Cyan
$AllGroups = Get-MgGroup -All -Property Id, DisplayName, GroupTypes
$GroupReport = foreach ($Group in $AllGroups) {
Write-Host "Starting group: $Group" -ForegroundColor Cyan
# Get just the IDs of the owners for this group
$OwnerLinks = Get-MgGroupOwner -GroupId $Group.Id
if ($OwnerLinks) {
foreach ($Link in $OwnerLinks) {
# Lookup the owner in our local cache instead of calling the API
$Owner = $UserCache[$Link.Id]
[PSCustomObject]@{
GroupName = $Group.DisplayName
GroupType = ($Group.GroupTypes -join ", ") -replace "Unified", "M365 Group"
OwnerName = if ($Owner) { $Owner.DisplayName } else { "Unknown (Service Principal or Deleted)" }
OwnerEmail = if ($Owner) { $Owner.Mail } else { "N/A" }
OwnerId = $Link.Id
}
}
} else {
# Optional: Include groups that have no owners
[PSCustomObject]@{
GroupName = $Group.DisplayName
GroupType = ($Group.GroupTypes -join ", ")
OwnerName = "--- NO OWNER ---"
OwnerEmail = "N/A"
OwnerId = "N/A"
}
}
}
# 4. Output the results
$GroupReport | Out-GridViewFor Distribution Lists:
Connect-ExchangeOnline
# 2. Setup a local cache to resolve GUIDs to Names efficiently
$RecipientCache = @{}
Write-Host "Fetching Distribution Lists..." -ForegroundColor Cyan
$AllDLs = Get-DistributionGroup -ResultSize Unlimited | Where-Object { $_.RecipientTypeDetails -eq "MailUniversalDistributionGroup" }
$DLReport = foreach ($DL in $AllDLs) {
Write-Host "Processing: $($DL.DisplayName)" -ForegroundColor Gray
# Resolve each manager in the list
$ResolvedManagers = foreach ($ManagerIdentity in $DL.ManagedBy) {
# If we've already looked up this GUID/Identity, use the cached name
if ($RecipientCache.ContainsKey($ManagerIdentity)) {
$RecipientCache[$ManagerIdentity]
}
else {
# Look up the name from Exchange
$Found = Get-Recipient -Identity $ManagerIdentity -ErrorAction SilentlyContinue
$Name = if ($Found) { $Found.DisplayName } else { $ManagerIdentity }
# Save to cache for the next time we see this person
$RecipientCache[$ManagerIdentity] = $Name
$Name
}
}
[PSCustomObject]@{
DisplayName = $DL.DisplayName
PrimaryEmail = $DL.PrimarySmtpAddress
Managers = $ResolvedManagers -join "; "
Alias = $DL.Alias
}
}
# 3. Output
$DLReport | Out-GridViewReplace an owner with another owner:
(Get-DistributionGroup -Identity "**NAME_OR_ADDRESS**").ManagedBy
$AllDLs = Get-DistributionGroup -ResultSize Unlimited | Where-Object { $_.RecipientTypeDetails -eq "MailUniversalDistributionGroup" }
$DLstoChange = $AllDLs | Where-Object {$_.managedby -eq "**OLD_OWNER_FROM_ABOVE**"}
$DLstoChange | Set-DistributionGroup -ManagedBy "**NEW_OWNER**"Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article