Export Microsoft Teams PSTN Call Logs
Summary
This script exports the log of PSTN calls for a given duration.
Permissions
The PowerShell version of the script requires Microsoft Entra ID App Registration with the Application Permission as follows:
- CallRecord-PstnCalls.Read.All
- CallRecords.Read.All
Note: You can skip this step, if you are planning to use CLI for Microsoft 365.
Implementation
- Open Windows PowerShell ISE
- Create a new file
- Copy the code below
- Save the file and run it
[CmdletBinding()]
param (
[parameter(Mandatory)][string] $TenantID,
[parameter(Mandatory)][string] $ClientID,
[parameter(Mandatory)][string] $ClientSecret,
[parameter(Mandatory)][DateTime] $StartDate,
[parameter(Mandatory = $false)][DateTime] $EndDate = (Get-Date)
)
function Get-AccessToken {
# Construct URI
$uri = "https://login.microsoftonline.com/$TenantID/oauth2/v2.0/token"
# Construct Body
$body = @{
client_id = $ClientID
client_secret = $ClientSecret
scope = 'https://graph.microsoft.com/.default'
grant_type = 'client_credentials'
}
# Fix for issue: The underlying connection was closed
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Get OAuth 2.0 Token
$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType 'application/x-www-form-urlencoded' -Body $body -UseBasicParsing
# Access Token
return ($tokenRequest.Content | ConvertFrom-Json).access_token
}
function Get-GraphQueryOutput {
param(
[parameter(Mandatory = $true)][String] $Uri
)
$retryCount = 0
$maxRetries = 3
$pauseDuration = 2
$token = Get-AccessToken
$allRecords = @()
while ($Uri) {
try {
Write-Host "Invoking Graph query: $($Uri)"
$query = Invoke-RestMethod -Method Get -Uri $Uri -ContentType 'application/json;odata.metadata=none' -Headers @{Authorization = "Bearer $token" }
$recordToAdd = ($query | Select-Object Value).Value
if (-not $recordToAdd) {
$recordToAdd = $query
}
$allRecords += $recordToAdd
if ($query.'@odata.nextLink') {
# set the url to get the next page of records
$Uri = $query.'@odata.nextLink'
}
else {
$Uri = $null
}
}
catch {
Write-Host "StatusCode: " $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
if ($_.Exception.Response.StatusCode.value__ -eq 401) {
$token = Get-AccessToken
}
if ($_.ErrorDetails.Message) {
Write-Host "Inner Error: $_.ErrorDetails.Message" -ForegroundColor Red
}
if ($_.Exception.Response.StatusCode.value__ -eq 404) {
# If the error is "Not Found", let us not retry
$retryCount = $maxRetries + 1
}
if ($retryCount -ge $maxRetries) {
# Not going to retry again
$Uri = $null
Write-Host "Not going to retry..." -ForegroundColor Red
}
else {
$retryCount += 1
$pauseDuration = $pauseDuration * 5
Write-Host "Retry attempt $retryCount after a $pauseDuration second pause..." -ForegroundColor Yellow
Start-Sleep -Seconds $pauseDuration
}
}
}
return $allRecords
}
Start-Transcript -Path ".\PSTN-Logs-$((Get-Date).toString("dd-MM-yyyy")).log"
$timeSpan = New-TimeSpan -Start (Get-Date -Date $StartDate) -End (Get-Date -Date $EndDate)
if ($timeSpan.Days -gt 90) {
Write-Host "The maximum number of days between StartDate and EndDate cannot exceed 90" -ForegroundColor Red
Write-Host "Please try again with a smaller time span." -ForegroundColor Red
Exit
}
$fromDateTime = Get-Date -Date $StartDate -Format "yyyy-MM-dd"
$toDateTime = Get-Date -Date $EndDate -Format "yyyy-MM-dd"
$callRecords = Get-GraphQueryOutput -Uri "https://graph.microsoft.com/v1.0/communications/callRecords/getPstnCalls(fromDateTime=$fromDateTime,toDateTime=$toDateTime)"
ForEach ($record in $callRecords) {
$reportLine = [PSCustomObject][Ordered]@{
"id" = $record.id
"callId" = $record.callId
"userId" = $record.userId
"userPrincipalName" = $record.userPrincipalName
"userDisplayName" = $record.userDisplayName
"startDateTime" = $record.startDateTime
"endDateTime" = $record.endDateTime
"duration" = $record.duration
"charge" = $record.charge
"callType" = $record.callType
"currency" = $record.currency
"calleeNumber" = $record.calleeNumber
"usageCountryCode" = $record.usageCountryCode
"tenantCountryCode" = $record.tenantCountryCode
"connectionCharge" = $record.connectionCharge
"callerNumber" = $record.callerNumber
"destinationContext" = $record.destinationContext
"destinationName" = $record.destinationName
"conferenceId" = $record.conferenceId
"licenseCapability" = $record.licenseCapability
"inventoryType" = $record.inventoryType
}
$reportLine | Export-Csv -NoTypeInformation -Path ".\PSTN-Data-$((Get-Date).toString("dd-MM-yyyy")).csv" -Encoding UTF8 -Append
}
Write-Host "Finished!" -ForegroundColor Green
Stop-Transcript
Check out the PowerShell to learn more at: PowerShell Documentation | Microsoft Learn
Contributors
Author(s) |
---|
Nanddeep Nachan |
Smita Nachan |
Credits
The retry invoke REST method requests in PowerShell, when an error occurs is inspired from this blog.
Disclaimer
THESE SAMPLES ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.