All scripts
Microsoft 365 90
Teams Calling Policy Report
Reports which Teams calling policy is assigned to every enabled user, and whether that assignment came from a direct assignment, a group policy assignment, or the global default. Exports to CSV so you can hand it to whoever owns the phone system without giving them admin access. Useful right before a Teams Phone rollout when you need to confirm nobody's on the wrong policy.
Get-TeamsCallingPolicyReport.ps1
<#
.SYNOPSIS
Reports which Teams calling policy is assigned to each enabled user.
.DESCRIPTION
Connects to the Microsoft Teams PowerShell module and pulls every
enabled, licensed user along with the calling policy assigned to them,
whether that assignment came from the global policy, a direct
assignment, or a group-based policy assignment. Useful before a
Teams Phone rollout, or when you need to confirm nobody's stuck on
a restricted calling policy by accident. Exports the results to CSV
so you can hand it to whoever owns the phone system without giving
them admin access.
.AUTHOR
Shehryar Hassan
.EXAMPLE
.\Get-TeamsCallingPolicyReport.ps1 -OutputPath "C:\Reports\CallingPolicy.csv"
Connects to Teams, pulls the policy assignment for every enabled user,
and writes the report to the given CSV path.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$OutputPath = ".\TeamsCallingPolicyReport.csv"
)
if (-not (Get-Module -ListAvailable -Name MicrosoftTeams)) {
Write-Error "The MicrosoftTeams module isn't installed. Run: Install-Module MicrosoftTeams -Scope CurrentUser"
return
}
Import-Module MicrosoftTeams -ErrorAction Stop
try {
Get-CsTenant -ErrorAction Stop | Out-Null
}
catch {
Write-Host "Not connected to Teams. Connecting now..." -ForegroundColor Yellow
Connect-MicrosoftTeams | Out-Null
}
Write-Host "Pulling enabled users from Entra ID..." -ForegroundColor Cyan
$users = Get-CsOnlineUser -Filter { AccountEnabled -eq $true } -ErrorAction Stop |
Where-Object { $_.InterpretedUserType -match "Member" }
Write-Host "Found $($users.Count) enabled users. Checking calling policy assignments..." -ForegroundColor Cyan
$report = foreach ($user in $users) {
$assignment = $null
try {
$assignment = Get-CsUserPolicyAssignment -Identity $user.UserPrincipalName -PolicyType TeamsCallingPolicy -ErrorAction Stop
}
catch {
Write-Verbose "Could not read policy assignment for $($user.UserPrincipalName): $_"
}
$policySource = "Global (Org Wide Default)"
$policyName = "Global"
if ($assignment) {
if ($assignment.PolicySource -eq "Direct") {
$policySource = "Direct"
$policyName = $assignment.PolicyName
}
elseif ($assignment.PolicySource -eq "Group") {
$policySource = "Group ($($assignment.GroupId))"
$policyName = $assignment.PolicyName
}
}
[PSCustomObject]@{
DisplayName = $user.DisplayName
UserPrincipalName = $user.UserPrincipalName
EnterpriseVoice = $user.EnterpriseVoiceEnabled
CallingPolicy = $policyName
AssignmentSource = $policySource
}
}
$report | Sort-Object DisplayName | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Done. $($report.Count) users written to $OutputPath" -ForegroundColor Green
$summary = $report | Group-Object CallingPolicy | Sort-Object Count -Descending
Write-Host ""
Write-Host "Calling policy breakdown:" -ForegroundColor Cyan
$summary | ForEach-Object {
Write-Host (" {0,-30} {1} users" -f $_.Name, $_.Count)
}
Read it before you run it, and test in a safe tenant first.