Teams Call Auswertung

Diese Seite beschreibt, wie sie mit Graph einen Einzelverbindungsnachweis für PSTN und Direct Routing-Anrufe generieren. Sie können über diesen Weg keine Federation-Anrufe, interne Verbindungen oder Besprechungen erfassen.

Problem

Die Lösung habe ich entwickelt, weil wieder einmal ein konkretes Problem bei einem Kunden mich genervt hat. Folgendes ist passiert.

  • Externer Teilnehmer ruft die Zentrale (Call Queue) an
  • Ein Mitarbeiter in der Call Queue schnappt sich den Anruf
  • Der Anruf wird sauber beendet

Nun kommt aber der Vertrieb und möchte ein paar Auswertungen, z.B.

  • Wer hat den Call angekommen
  • Wie lange musste der Anwender in der Call-Queue warten

Mit einer Auswertung der CDR-Einträge des SBCs komme ich hier nicht weiter, da ich nur den Start der Verbindung zur CallQueue-Rufnummer und das Ende des Anrufs sehe. Alle internen Vermittlungen und Verweilzeiten sieht der SBC nicht. Diese Daten hat aber Teams.

Grenzen des Teams Admin Portal

Ich bin also auf https://teams.admin.microsoft.com und wollte hier weiter analysieren. Das Problem dabei ist aber, dass es hier gar keine Liste der eingegangenen Anruf gibt. Ich kann nur auf einen Benutzer gehen und mir dessen Anrufe der letzen 30 Tage anzeigen lassen: In dem Fall wusste ich zufällig den Teilnehmer und den Zeitpunkt, so dass ich relativ schnell dann den Call dazu gefunden habe. Die Call-Übersicht erlaubt nämlich keine Suche nach Rufnummern sondern zeigt nur allgemeine Informationen an.

Diese Ansicht ist für die Suche anhand von Anrufen anhand der Rufnummer nicht geeignet. Wenn ich aber über die Zeit den Call finde, dann kann ich mir auch die Details anzeigen lassen:

Allerdings sehen Sie auch hier schon einige Einschränkungen, z.B. sehe ich nur "Bot" und nicht den Namen der Call Queue und auch die Rufnummer des Anrufers ist hier aus Datenschutzgründen teilweise unkenntlich gemacht.

Abfrage per Graph

Damit war klar, dass eine einfache Abfrage per GUI oder Browser mich hier nicht weiter bringt. Sehr schnell bin ich dann auf folgende beide API-Aufrufe in Graph gestoßen.

Ein Blick auf diese APIs zeigt, aber, dass ich als Anwender darauf gar nicht zugreifen kann.

Ich muss eine App in dem Tenant definieren, der ich die erforderlichen Rechte zuweise, z.B.:

Die App selbst muss ich auch authentifizieren. Dazu nutzt sie als "Benutzernamen" die AppID und als Geheimnis entweder ein Zertifikat oder für den Test reicht auch ein Kennwort. Zu den Details gibt es jede Menge anderen Seiten auf der MSFAQ und im Internet.

Token besorgen

Hinweis:
Ich beschreibe hier noch den manuellen Prozess. Mit der Graph PowerShell geht das natürlich auch:

Daher ist der erste Schritt einmal die Anmeldung, um ein Zugriffstoken zu erhalten. Den Prozess habe ich auf Graph Token und anderen Seiten beschrieben. Daher hier nur die Kurzfassung:

$LoginUrl="https://login.microsoftonline.com",
$AppID="12345678-1234-1234-1234-1234567890ab",
$AppSecret= "super geheimes secret der app",
$TenantName="tenantname.onmicrosoft.com"

$authresponse=Invoke-RestMethod `
                 -Uri "$($LoginUrl)/$($TenantName)/oauth2/v2.0/token" `
                 -Method POST `
                 -ContentType "application/x-www-form-urlencoded" `
                 -body "client_id=$($AppID)
                        &scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
                        &client_secret=$($AppSecret)
                        &grant_type=client_credentials
                        &api-version=1.0"
$accesstoken= $authresponse.access_token

Calls abrufen

Mit dem Access-Token kann ich dann direkt die APIs aufrufen. Ich muss allerdings als Parameter den Zeitraum angeben. Der Code nutzt den vorherigen Tag.

$datefilter = "(fromDateTime=$((get-date).adddays(-2).ToString("yyyy-MM-dd")),toDateTime=$((get-date).adddays(-1).ToString("yyyy-MM-dd")))"

$pstncalls = Invoke-RestMethod `
                 -Uri ('https://graph.microsoft.com/v1.0/communications/callRecords/getPstnCalls'+$datefilter) `
                 -Method GET `
                 -Header @{ 'Authorization' = "Bearer $($accesstoken)"}

Write-host "PSTNCall Count $($pstncalls.'@odata.count')"

$drcalls = Invoke-RestMethod `
                 -Uri ('https://graph.microsoft.com/v1.0/communications/callRecords/getDirectRoutingCalls'+$datefilter) `
                 -Method GET `
                 -Header @{ 'Authorization' = "Bearer $($accesstoken)"}
Write-host "PSTNCall Count $($drcalls.'@odata.count')"

Beide Ausgaben halten im Property "Value" eine Liste der Calls. Leider sind die Properties nicht identisch, so dass Sie hier noch etwas nacharbeiten müssen. Ein DirectRouting-Call sieht wie folgt aus:

PS C:\> $drcalls.value[0]

id                            : 12345678-1234-1234-1234-1234567890ab
correlationId                 : 12345678-1234-1234-1234-1234567890ab
userId                        : 12345678-1234-1234-1234567890ab
userPrincipalName             : Demouser1@msxfaq.de
userDisplayName               : DemoUser1
startDateTime                 :
inviteDateTime                : 09.05.2022 06:00:26
failureDateTime               : 09.05.2022 06:00:36
endDateTime                   :
duration                      : 0
callType                      : ByotIn
successfulCall                : False
callerNumber                  : +495251304***
calleeNumber                  : +4952579388
mediaPathLocation             : ukwe
signalingLocation             : EUNO
finalSipCode                  : 487
callEndSubReason              : 540200
finalSipCodePhrase            : Call completed elsewhere
trunkFullyQualifiedDomainName : sbc.msxfaq.de
mediaBypassEnabled            : True

Aber auch wenn Sie keine Microsoft Dialpläne nutzen, können Sie PSTN-Calls erhalten. Das sind z.B. Konferenzeinwahlen über die Microsoft-Rufnummern.

PS C:\group\Technik\Skripte\Get-TeamsCallIDs> $pstncalls.value[0]

id                 : 12345678-1234-1234-1234-1234567890ab
callId             : 123456789_123456789@80.169.27.117
userId             : 12345678-1234-1234-1234-1234567890ab
userPrincipalName  : Demouse1@msxfaq.de
userDisplayName    : DemoUser
startDateTime      : 09.05.2022 11:01:10
endDateTime        : 09.05.2022 11:37:48
duration           : 2198
charge             : 0
callType           : conf_in
currency           : EUR
calleeNumber       : +4969677765841
usageCountryCode   : DE
tenantCountryCode  : DE
connectionCharge   : 0
callerNumber       : +4952579388***
destinationContext :
destinationName    : Germany
conferenceId       : 12345678
licenseCapability  : MCOMEETADD
inventoryType      : Service
operator           :
callDurationSource : microsoft

Sie sehen aber auch hier dass die Daten auch hier beschnitten sind. Der Anrufer wird nur verkürzt angezeigt und Details über den Anruf bekommen wir so natürlich nicht

Details mit CallID

Wenn Sie dann die CallID zu einem Anruf haben, dann können Sie per Graph über die "/communications/callrecords"-API sehr einfach weitere Details abfragen

Meine App braucht dazu natürlich wieder die Berechtigungen "CallRecords.Read.All" und muss eine Call-ID in der URL mitgeben.

Es gibt leider hier keine Option, mit "*" o.ä. alle Calls zu bekommen. Diese API funktioniert immer nur genau mit einer gültigen CallID, die zum Tenant passen muss und nicht länger als 60 Tage in er Vergangenheit liegt

Die CallID kan ich aus der Liste entnehmen, die ich im vorigen Abschnitt ermittelt habe. Beim DirectRouting-Call ist es die "correlationID"

$calldetail = Invoke-RestMethod `
                 -Uri ('https://graph.microsoft.com/v1.0/communications/callRecords/'+$callid+'?$expand=sessions($expand=segments)') `
                 -Method GET `
                 -Header @{ 'Authorization' = "Bearer $($accesstoken)"}

Auch das ist auch per Graph PowerShell möglich:

Import-Module Microsoft.Graph.CloudCommunications
Connect-MgGraph 
Get-MgCommunicationCallRecord -CallRecordId $callRecordId

Die Rückgabe ist dann eine JSON-Struktur, die sich in Teilen wiederholt.

Auf die einzelnen Felder und deren Bedeutung gehe ich auf Graph Get-Callrecord genauer ein.

Einschränkung

Über den hier beschrieben Weg kann ich zumindest im Mai 2022 leider nur Calls ermitteln, die über Direct Routing, Operator Connect und Microsoft Dialpläne im Tenant angekommen sind. Ich sehe aber keine Verbindungen der Anwender in Meetings im eigenen oder fremden Tenant und auch keine 1:1 Teams-Gespräche zwischen Personen oder per Federation. Wenn Sie die CallID zu diesen Verbindungen haben, dann können Sie natürlich dann auch die Details abfragen aber solange es keine API zur Auflistung der Anrufe pro Anwender oder aller Anwender gibt, können Sie diese Daten nur asynchron über einen Graph CallReport Webhook stark verzögert oder inoffizielle APIs, die z.B. das Teams Admin Center nutzt erhalten.

Weitere Links