function CheckFor-MicrosoftTeamsVersion { if (!(((Get-Module -Name "MicrosoftTeams") -ne $null) -or ((Get-InstalledModule -Name "MicrosoftTeams" -ErrorAction SilentlyContinue) -ne $null))) { Write-Host $msg = "INFO: MicrosoftTeams PowerShell module not installed." Write-Host $msg $msg = "INFO: Installing MicrosoftTeams PowerShell module." Write-Host $msg try{ Install-Module -Name MicrosoftTeams -RequiredVersion 1.1.3-preview -AllowPrerelease } catch{ $msg = "ERROR: Failed to install MicrosoftTeams module. Script will abort." Write-Host -ForegroundColor Red $msg Write-Host $msg = "ACTION: Run this script 'As administrator' to intall the MicrosoftTeams module." Write-Host -ForegroundColor Yellow $msg Exit } } else{ $MicrosoftTeamsVersion = if ((Get-Module -Name "MicrosoftTeams") -ne $null) { (Get-Module -Name "MicrosoftTeams").Version } else { (Get-InstalledModule -Name "MicrosoftTeams").Version } if(($MicrosoftTeamsVersion -ne '1.1.3') -and ($MicrosoftTeamsVersion -ne '1.1.3-preview')) { Write-Host $msg = "INFO: MicrosoftTeams PowerShell module not using the right version. Current version $MicrosoftTeamsVersion" Write-Host $msg Write-Host "MicrosoftTeams PowerShell module needs to be updated to run operations related to Private Channels since the current version is incompatible." $confirm = (Read-Host -prompt "Do you want to update MicrosoftTeams PowerShell module? [Y]es or [N]o") if($confirm.ToLower() -eq "y") { $msg = "INFO: Updating MicrosoftTeams PowerShell module." Write-Host $msg try { Uninstall-Module -Name MicrosoftTeams -Force -ErrorAction Stop Install-Module -Name MicrosoftTeams -RequiredVersion 1.1.3-preview -AllowPrerelease } catch{ $msg = "ERROR: Failed to update MicrosoftTeams module." Write-Host -ForegroundColor Red $msg Write-Host $msg = "ACTION: Run this script 'As administrator' to intall the MicrosoftTeams module." Write-Host -ForegroundColor Yellow $msg Exit } } } } } function Show-Menu { param ( [string]$Title = 'Options' ) Write-Host "================ $Title ================" Write-Host "1: Press '1' to get list of teams with private channels at source tenant." Write-Host "2: Press '2' to get owners and members of private channels in selected teams from source tenant." Write-Host "3: Press '3' to add owners and members into private channels of the corresponding teams in the destination tenant." } function GetTeamsWithPrivatechannel-InSourceTenant { if(!(Test-Path Variable::global:SourceGlobalCred)-or($global:SourceGlobalCred-isnot[pscredential])){ $global:SourceGlobalCred=Get-Credential -Message 'Please enter the GA credentials for your Source tenant.' } $SourceCred=$global:SourceGlobalCred Connect-MicrosoftTeams -Credential $SourceCred $teamsWithPrivateChannels = @() $counter = 0 $teams = Get-Team Write-Output "Teams count" $teams.count ForEach ($team in $teams) { $teamWithPrivateChannels = Get-TeamChannel -GroupId $team.GroupId -MembershipType Private if ($teamWithPrivateChannels) { Write-Host $team.DisplayName "has private channels" $teamsWithPrivateChannels += New-Object -TypeName PSObject -Property @{ mailNickName = $team.MailNickName } } $counter += 1 if ($counter % 10 -eq 0) { Write-Host "Scanned" $counter "teams for private channels" } } if ($teamsWithPrivateChannels) { $teamsWithPrivateChannels | Export-Csv -NoTypeInformation -Path .\ListOfTeamsWithPrivateChannels.csv -Encoding UTF8 Write-Host "Exported all teams with private channels into ListOfTeamsWithPrivateChannels.csv" Write-Host "`nPlease remove teams' mailNickName from ListOfTeamsWithPrivateChannels.csv for those teams whose private channel membership you do not wish to migrate over to desitnation tenant" -ForegroundColor Yellow Write-Host "`nThen run option 2 in the script to retreive all owners and members of private channels for the respective teams in ListOfTeamsWithPrivateChannels.csv" -ForegroundColor Yellow } } function GetOwnerMember-OfPrivateChannelsSouceTenant { if(!(Test-Path Variable::global:SourceGlobalCred)-or($global:SourceGlobalCred-isnot[pscredential])){ $global:SourceGlobalCred=Get-Credential -Message 'Please enter the GA credentials for your source tenant.' } $SourceCred=$global:SourceGlobalCred Connect-MicrosoftTeams -Credential $SourceCred $OwnersAndMembersInPrivateChannels = @() $mailNickNames = Import-Csv -Path .\ListOfTeamsWithPrivateChannels.csv $teamsWithPrivateChannels = $mailNickNames | Get-Team ForEach ($teamWithPrivateChannels in $teamsWithPrivateChannels) { Write-Host "Scanning owners and members of private channels in team " $teamWithPrivateChannels.DisplayName $privateChannelsInTeam = Get-TeamChannel -GroupId $teamWithPrivateChannels.GroupId -MembershipType Private ForEach ($privateChannelInTeam in $privateChannelsInTeam) { $ownerMemberOfPrivateChannel = $privateChannelInTeam | Get-TeamChannelUser -GroupId $teamWithPrivateChannels.GroupId | Select-Object User, Role, @{n='PrivateChannelDisplayName';e={$privateChannelInTeam.DisplayName}}, @{n='TeamMailNickName';e={$teamWithPrivateChannels.MailNickName}} $ownersAndMembersInPrivateChannels += $ownerMemberOfPrivateChannel } } if ($ownersAndMembersInPrivateChannels) { $ownersAndMembersInPrivateChannels | Export-Csv -NoTypeInformation -Path .\OwnersAndMembersOfPrivateChannels.csv -Encoding UTF8 Write-Host "Exported all owners and members of private channels into OwnersAndMembersOfPrivateChannels.csv" Write-Host "`nChange the user's domain, apply user mapping (if any) and apply teams mapping (if any) in OwnersAndMembersOfPrivateChannels.csv before running option 3 in the script." -ForegroundColor Yellow } } function AddOwnerMember-ToPrivateChannelDestinationTenant { if(!(Test-Path Variable::global:DestinationGlobalCred)-or($global:DestinationGlobalCred-isnot[pscredential])){ $global:DestinationGlobalCred=Get-Credential -Message 'Please enter the GA credentials for your destination tenant.' } $DestCred=$global:DestinationGlobalCred Connect-MicrosoftTeams -Credential $DestCred $mapOfTeamNickNameToGroupId = @{} $ownersAndMembersInPrivateChannels = Import-Csv -Path .\OwnersAndMembersOfPrivateChannels.csv ForEach ($item in $ownersAndMembersInPrivateChannels) { try { if ($mapOfTeamNickNameToGroupId[$item.TeamMailNickName] -eq $null) { $team = Get-Team -MailNickName $item.TeamMailNickName $mapOfTeamNickNameToGroupId[$item.TeamMailNickName] = $team.GroupId } Add-TeamChannelUser -GroupId $mapOfTeamNickNameToGroupId[$item.TeamMailNickName] -DisplayName $item.PrivateChannelDisplayName -User $item.User if ($item.Role -eq "Owner") { Add-TeamChannelUser -GroupId $mapOfTeamNickNameToGroupId[$item.TeamMailNickName] -DisplayName $item.PrivateChannelDisplayName -User $item.User -Role Owner } Write-Host "Added user" $item.User "to private channel" $item.PrivateChannelDisplayName "in team" $item.TeamMailNickName "as" $item.Role } catch { # Convert common exception messages into human understandable form if ($_.Exception.Message.contains("Cannot bind argument to parameter 'GroupId' because it is null")) { $errMessage = "Team is not found in the tenant." } elseif ($_.Exception.Message.contains("does not exist or one of its queried reference-property objects")) { $errMessage = "User is not found in the tenant." } elseif ($_.Exception.Message.contains("Failed to execute backend request")) { $errMessage = "User is already in the private channel." } elseif ($_.Exception.Message.contains("User is not found in the team")) { $errMessage = "User is not found in the team." } else { $errMessage = $_.Exception.Message } Write-Host "Could not add user" $item.User "to private channel" $item.PrivateChannelDisplayName "in team" $item.TeamMailNickName "because" $errMessage -ForegroundColor red } } } $ErrorActionPreference="Stop" CheckFor-MicrosoftTeamsVersion Import-Module MicrosoftTeams Show-Menu $input = Read-Host "Please make a selection" switch ($input) { '1' { GetTeamsWithPrivatechannel-InSourceTenant } '2' { GetOwnerMember-OfPrivateChannelsSouceTenant } '3' { AddOwnerMember-ToPrivateChannelDestinationTenant } }