Shared channels in Microsoft Teams allow users to collaborate without adding everyone to the parent team. A shared channel can include individuals from the host organization, external participants through Microsoft Entra B2B direct connect, or members of another team with which the channel has been shared.
This flexibility makes shared channels useful for cross-team and cross-organization collaboration. However, it also makes membership more difficult to track. Reviewing only the parent team does not provide a complete picture because users can access a shared channel without being members of that team. This guide explains how to review and export shared channel membership details in Microsoft Teams.
How Shared Channel Membership Works in Microsoft Teams
Unlike standard channels, shared channels maintain their own membership. A shared channel can provide access through:
- Direct membership: A user is added directly to the shared channel.
- Team-based membership: The channel is shared with another team, allowing eligible members of that team to access it.
- External membership: A user from another Microsoft 365 organization is added through B2B direct connect.
External participants in shared channels do not appear as guest accounts in the host tenant. This is one reason shared-channel access can be difficult to review through regular team or guest-user reports.
How to Export Private Channel Membership in Microsoft Teams
Administrators can review private channel membership through the Teams admin center or use PowerShell to generate a consolidated report across the tenant.
Get a List of Shared Channel Members Using Teams Admin Center
The Microsoft Teams admin center allows administrators to identify shared channels within a team and review their membership without using PowerShell. It is useful for checking members, owners, and outdated access.
- Sign in to the Microsoft Teams admin center.
- Go to Teams > Manage teams.
- Select the team you want to review.
- Open the Channels tab.
- Filter the Type column by Shared.
- Select a shared channel and open its membership details to review its owners and directly added members.
Export Shared Channel Membership Report Using PowerShell
The following script scans every team, identifies its shared channels, retrieves their membership, and exports the results to a CSV file.
# 1. Connect to Microsoft Graph
Import-Module MicrosoftTeams
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Groups)) {
Install-Module Microsoft.Graph -Scope CurrentUser -Force
}
Connect-MicrosoftTeams
Connect-MgGraph -Scopes "GroupMember.Read.All", "Directory.Read.All", "User.Read.All"
# Setup Report Path
$OutputPath = "C:\temp\TeamsSharedChannelReport_FixedFinal.csv"
if (-not (Test-Path "C:\temp")) { New-Item -Path "C:\" -Name "temp" -ItemType Directory -Force }
$Report = [System.Collections.Generic.List[PSCustomObject]]::new()
Write-Host "Fetching all Teams..." -ForegroundColor Cyan
$AllTeams = Get-Team
foreach ($Team in $AllTeams) {
Write-Host "Processing Team: $($Team.DisplayName)..." -ForegroundColor Yellow
# Get only Shared Channels
$SharedChannels = Get-TeamChannel -GroupId $Team.GroupId | Where-Object { $_.MembershipType -eq "Shared" }
foreach ($Channel in $SharedChannels) {
Write-Host " -> Found Shared Channel: $($Channel.DisplayName)" -ForegroundColor Green
try {
$Members = Get-MgTeamChannelMember -TeamId $Team.GroupId -ChannelId $Channel.Id -ErrorAction Stop
foreach ($Member in $Members) {
$UserUPN = "Unknown/External"
$AccessType = "Direct Member"
# Check for OData mapping properties safely
if ($null -ne $Member.AdditionalProperties) {
if ($Member.AdditionalProperties.containsKey("@odata.type")) {
$OdataType = $Member.AdditionalProperties["@odata.type"]
if ($OdataType -like "*sharedWithChannelTeam*") {
$AccessType = "Inherited via Team"
}
}
}
# In Graph ConversationMembers, the object's top-level ID can often map directly
# to the user's underlying Azure/Entra ID object if it's a direct user object.
# If that fails, we fallback to finding the distinct ID inside the property bags.
$SearchId = $Member.Id
if ($null -ne $Member.AdditionalProperties -and $Member.AdditionalProperties.containsKey("userId")) {
$SearchId = $Member.AdditionalProperties["userId"]
}
# Clean up prefix punctuation Graph occasionally inserts into conversation member IDs
if ($SearchId -match "([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})") {
$SearchId = $Matches[1]
}
if (-not [string]::IsNullOrEmpty($SearchId)) {
try {
# Resolve the actual UPN identity against the tenant directory
$EntraUser = Get-MgUser -UserId $SearchId -Property "UserPrincipalName,Mail" -ErrorAction Stop
$UserUPN = if ($null -ne $EntraUser.UserPrincipalName) { $EntraUser.UserPrincipalName } else { $EntraUser.Mail }
} catch {
# Fallback for external direct-connect profiles
if ($null -ne $Member.AdditionalProperties -and $Member.AdditionalProperties.containsKey("email")) {
$UserUPN = $Member.AdditionalProperties["email"]
}
}
}
$Report.Add([PSCustomObject]@{
"HostTeamName" = $Team.DisplayName
"ChannelName" = $Channel.DisplayName
"UserDisplayName" = $Member.DisplayName
"UserUPN" = $UserUPN
"AccessType" = $AccessType
"ChannelRole" = ($Member.Roles -join ", ")
})
}
} catch {
Write-Host " [!] Graph API retrieval failed for channel: $($Channel.DisplayName)." -ForegroundColor Red
Write-Host " Error Info: $($_.Exception.Message)" -ForegroundColor DarkRed
}
}
}
# Export Results
if ($Report.Count -gt 0) {
$Report | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "`nSuccess! Cleaned up report exported to $OutputPath" -ForegroundColor Green
} else {
Write-Host "`nNo shared channels membership could be enumerated." -ForegroundColor Red
}
Make sure the destination folder already exists before running the script.
The output shows the host team and shared channel name, along with each user’s display name, UPN, access type, and channel role. This helps administrators see which users have direct access to each shared channel, distinguish owners from members, identify users who belong to multiple shared channels, and find channels that may not have an assigned owner.
By exporting the host team, channel, user, and role information, administrators can quickly review shared channel access, identify users with membership in several channels, distinguish owners from members, and locate channels that may no longer have an assigned owner.