Teams Anrufliste mit Graph
Diese Seite beschreibt den Zugang zu den Teams Call Logs über Graph. Anhand eines WebService und etwas Powershell bekomme ich die Daten in "neartime" eigene Auswertungen.
Beachten Sie dazu auch die Seite Teams Verbindungsdaten, Graph Get-Callrecord und Graph CallReport Webhook
Muss es Graph sein?
Auf der Seite Teams Verbindungsdaten habe ich die verschiedenen Zugänge zu den Teams Anrufdatensätzen beschrieben. Früher konnte ich mit Get-CSUserSessoin die Aktivitäten eines Anwender über einen Zeitraum erfragen. Dieser Weg ist aber abgekündigt.
Quelle:
https://docs.microsoft.com/en-us/powershell/module/skype/get-csusersession?view=skype-ps
Auch gibt es keinen direkten Weg per Teams PowerShell diese Daten zu erhalten, obwohl diese im Teams Admin Center pro Benutzer weiter erreichbar sind.
Die offizielle Lösung ist aktuell der Zugriff per Microsoft Graph.
- Working with the call records API in
Microsoft Graph
https://docs.microsoft.com/en-us/graph/api/resources/callrecords-api-overview - callRecord resource type
https://docs.microsoft.com/en-us/graph/api/resources/callrecords-callrecord - Use the Microsoft Graph API to get
change notifications
https://docs.microsoft.com/en-us/graph/api/resources/webhooks
Über den den "callRecord resource type" kann eine berechtigte App aktuell die PSTN-Anrufe und DirectRouting-Anrufe ermitteln um dann auch die Details dazu abzufragen.
Die passenden Graph-URLs sind dazu
GET https://graph.microsoft.com/v1.0/communications/callRecords/getPstnCalls(fromDateTime=2019-11-01,toDateTime=2019-12-01) GET https://graph.microsoft.com/v1.0/communications/callRecords/getDirectRoutingCalls(fromDateTime=2019-11-01,toDateTime=2019-12-01)
Es kommen aber nur immer die ersten 1000 Elemente zurück und in der Antwort steht ein "@odata.NextLink"-Attribute, um die nächsten Elemente zu laden.
Achtung:
Allerdings sehen Sie über den Weg keine
Meetings und keine Federation-Calls. Hierfür gibt es
anscheinend noch keinen Endpunkt um diese Verbindungen in
Erfahrung zu bringen
Der Zugriff auf diese Endpunkte kann nur eine Application bekommen.
Quelle:
https://docs.microsoft.com/en-us/graph/api/callrecords-callrecord-get
Ihre App muss ich gegenüber Graph mit einer AppID und Credentials (Kennwort / Zertifikat) ausweisen und der Administrator die Berechtigungen gewährt haben.
Authentifizierung
Also habe ich mir meine PowerShell geschnappt, eine AzureApp-Registration mit Permissions und Client-Secret angelegt und hinterlegt:
Mit dem Modul MSAL.PS können Sie die Anmeldung auf Basis von MSAL noch einfacher gestalten, insbesondere mit MFA o.ä.
$LoginUrl="https://login.microsoftonline.com" # Graph API URLs $ResourceUrl="https://graph.microsoft.com" # Ressource API URLs $TenantName="msxfaq.onmicrosoft.com" $AppID="hier muss die GUID ihrer AppRegistration rein" $Appkey="hier muss dann das Kennwort der AppRegistration rein"
Mit den Daten habe ich mir dann ein Authentication Token beschafft:
$Request = @{ "api-version" ="1.0" client_id = $AppID client_secret = $appkey scope = "https://graph.microsoft.com/.default" grant_type = "client_credentials" } $authresponse=Invoke-RestMethod ` -Method POST ` -Uri "$($LoginUrl)/$($tenantName)/oauth2/v2.0/token" ` -ContentType "application/x-www-form-urlencoded" ` -Body $RequestBody ` -UseBasicParsing ` -$body $body $accesstoken= $authresponse.access_token
Wer mag, kann das "accesstoken" ja auf https://jwt.io decodieren lassen.
Details zu einer CallID
Ich habe mir dann aus dem Admin-Portal eine CallID geschnappt und diese direkt abgerufen.
$call=Invoke-RestMethod ` -Method GET ` -Headers @{"Authorization" = "Bearer $($accesstoken)"} ` -URI "https://graph.microsoft.com/v1.0/communications/<callRecords/hier-die-call-id-einsetzen>" $call @odata.context : https://graph.microsoft.com/v1.0/$metadata#communications/callRecords/$entity id : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx version : 2 type : peerToPeer modalities : {audio} lastModifiedDateTime : 21.09.2021 08:52:19 startDateTime : 21.09.2021 08:35:33 endDateTime : 21.09.2021 08:40:41 joinWebUrl : organizer : @{acsUser=; spoolUser=; phone=; guest=; encrypted=; On-Premises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=} participants : {@{acsUser=; spoolUser=; phone=; guest=; encrypted=; On-Premises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=}, @{acsUser=; spoolUser=; phone=; guest=; encrypted=; On-Premises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=}} $call.participants acsUser : spoolUser : phone : guest : encrypted : On-Premises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : user : @{id=<userid1>; displayName=User1; tenantId=<tenantid>} acsUser : spoolUser : phone : guest : encrypted : On-Premises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : user : @{id=<userid1>; displayName=User2; tenantId=<tenantid>}
Wenn ich eine CallID habe, kann ich so sehr einfach zumindest grundlegende Informationen abfragen. Über den gleichen Weg kann ich auch eine Konferenz abfragen.
Liste der Calls
Nun wird es aber kniffliger. Wie komme ich an die Liste der Anrufe? Im Teams Admin-Portal kann ich nur pro Benutzer die Calls der letzten 30 Tage ermitteln. Einen Weg die Daten per Teams Powershell gibt es wohl nicht und das frühere "Get-CSUserSession" ist abgekündigt. Es gibt aber zwei Aufrufe, die zumindest für PSTN-Calls und Direct Routing die Daten ermitteln.
- callRecord: getPstnCalls
https://docs.microsoft.com/en-us/graph/api/callrecords-callrecord-getpstncalls - callRecord: getDirectRoutingCalls
https://docs.microsoft.com/en-us/graph/api/callrecords-callrecord-getdirectroutingcalls - Call records overview
https://docs.microsoft.com/en-us/graph/cloud-communications-callrecords
Das App-Token habe ich ja schon, so dass der nächste Request schnell und einfach angefordert wurde.
$drcalls=Invoke-RestMethod ` -Method GET ` -Headers @{"Authorization" = "Bearer $($accesstoken)"} ` -URI "https://graph.microsoft.com/v1.0/communications/callRecords/getDirectRoutingCalls(fromDateTime=2021-09-15,toDateTime=2021-09-18)" $drcalls| fl @odata.context : https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.callRecords.directRoutingLogRow) @odata.count : 641 value : {@{xxxxxxxxxxxx}…}
Sie sehen hier eine Rückgabe von 641 Datensätze, die sich unter "values" verstecken. Hier mal die Details eines Calls.
$drcalls.value[1] id : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx correlationId : yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy userId : zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz userPrincipalName : user1@msxfaq.de userDisplayName : User1 startDateTime : 15.09.2021 05:37:13 inviteDateTime : 15.09.2021 05:37:09 failureDateTime : 01.01.0001 00:00:00 endDateTime : 15.09.2021 05:55:38 duration : 1105 callType : ByotIn successfulCall : True callerNumber : +4952513046 calleeNumber : +49525793880 mediaPathLocation : EUNO signalingLocation : EUNO finalSipCode : 0 callEndSubReason : 540000 finalSipCodePhrase : BYE trunkFullyQualifiedDomainName : nawsbc.msxfaq.de mediaBypassEnabled : False
Dies ist ein Direct Routing Call. Sie können gut den Start und Endezeitpunkt sehen (UTC), die Rufnummern. Ich habe mir mal die Felder genauer anschaut, indem ich per "Group-Object" aus diesen 641 Anrufen zusammengefasst habe:
Feld | Gefundene Werte | Beschreibung |
---|---|---|
CallType |
ByotIn ByotInUcap ByotOut ByotOutUcap ByotOutUserForwarding |
Byot kürze ich als "Bring your own trunk" ab und zeigt die verschiedenen Status einer Verbindung. |
mediaPathLocation |
EUNO EUWE |
Ich hatte nur Anrufe aus "Europa und damit wurden nur die beiden Zugänge in Europa NO und WE als Mediarelay genutzt. |
signalingLocation |
EUNO EUWE JAEA |
Die gleiche Information gibt es auch noch mal für die Signalisierung per SIP bei DirectRouting. Alle JAEA-Calls zeichneten sich durch einen SIP-Error 504 und folgender Meldung aus: finalSipCodePhrase: Unable to deliver INVITE: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. |
finalSipCode |
0 403 4008 480 486 487 500 504 603 |
Hier habe ich jede Menge unterschiedliche SIP-Statuscodes gefunden. |
Auf diesen Daten kann man schon eine erste Auswertung für Telefonie-Anrufe starten, die per Microsoft Rufnummern (PSTN-Call) oder Direct Routing bzw. Operator Connect ankommen. Allerdings sehe ich damit natürlich keine internen oder per Federation geführten Teams-Anrufe oder Meetings.
Realtime?
Allerdings ist ein "Polling" immer mit einer gewissen Last und Verzögerung verbunden. Es ist natürlich die einfachste Art der Programmierung aber wenn Sie schnell informiert sein müssen, dann brauchen Sie einen Webhook, d.h. ihr Software bittet Graph um einen Rückruf. Dazu müssen Sie selbst erst einmal einen per HTTPS erreichbaren Webservice im Internet bereitstellen und dann die URL an Graph übergeben. Dann wird Microsoft Graph jeden neuen Datensatz als HTTP-Post mit der Call-ID an ihren Webservice melden, so dass Sie dann die Details dazu abholen können.
Organizations can subscribe to changes
to call records using the Microsoft Graph webhook
subscriptions capability, allowing them to build near-real-time
reports from the data or to alert on certain scenarios like
emergency calls. ...
... This push-model enables organizations and partners to
build their own real-time reporting solutions.
Quelle:
https://docs.microsoft.com/en-us/graph/cloud-communications-callrecords
Dazu habe ich aber eine eigene Seite auf Graph CallReport Webhook beschrieben.
- Teams Verbindungsdaten
- Graph CallReport Webhook
- Azure Functions
- Create subscription
https://docs.microsoft.com/en-us/graph/api/subscription-post-subscriptions - Incoming call notifications
https://docs.microsoft.com/en-us/microsoftteams/platform/bots/calls-and-meetings/call-notifications - Quickstart: Create a PowerShell function in Azure using
Visual Studio Code
https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-vs-code-powershell
Weitere Links
- Graph Berechtigungen
- Graph PowerShell
- Teams CQD/CDR/QoE
- Graph Get-Callrecord
- Graph CallReport Webhook
- Dimensions and measurements available in
Call Quality Dashboard (CQD)
https://docs.microsoft.com/en-us/microsoftteams/dimensions-and-measures-available-in-call-quality-dashboard - Use CQD to manage call and meeting
quality in Microsoft Teams
https://docs.microsoft.com/en-us/microsoftteams/quality-of-experience-review-guide - QoE Monitoring for Microsoft Teams and
SIP Trunk Calls Made Simple
https://blog.audiocodes.com/qoe-monitoring-for-microsoft-teams-and-sip-trunk-calls-made-simple - Analytics and Reporting for Teams, Office365, Skype for
Business Online
https://www.mafinfo.com/article/analytics-and-reporting-for-teams-office365-skype-for-business-online/
"CDR’s are collected from Skype for Business online using Skype online PowerShell scripts" - Atradis< Balance : Call Accounting
https://www.tcc.de/callmanagement/callaccounting
https://www.tcc.de/atradis/balance