Graph Get-Callrecord
Wenn Sie eine CallID zu einem Teams Call oder Konferenz haben, dann können Sie per Graph die weiteren Details dazu auslesen. Diese Seite komplettiert die Vorarbeiten von Azure Functions und Graph CallReport Webhook, um an die CallIDs zu kommen.
Beachten Sie dazu auch die Seite Teams Verbindungsdaten, Graph CallReport Webhook und Teams Graph und CallIDs
App-Berechtigungen
Alleine eine CallID reicht nicht. Sie müssen schon die Berechtigungen zum Auslesen der Callrecords haben
Get-CallRecord
Mit der CallID und einer berechtigten App habe ich die Bausteine zusammen, damit ich die Details zu einem Call ermitteln kann.
PS C:\> $calldetails = Invoke-RestMethod ` -Uri "https://graph.microsoft.com/v1.0/communications/callRecords/1234568-1234-1234-1234-1234567890ab" ` -Method GET ` -Header @{ 'Authorization' = "Bearer $($accesstoken)"}
- Graph: Get callRecord
https://docs.microsoft.com/en-us/graph/api/callrecords-callrecord-get
Die geschweiften Klammern aus der API-Beschreibung werden nicht mit angegeben. Die Rückmeldungen liefern aktuell (Stand Nov 2021) nur grundlegende Verbindungsdaten aber keine Details zu den RTP-Streams.
CallTyp | Daten |
---|---|
1:1 Direct Routing |
PS C:\> $calldetails odata.context : https://graph.microsoft.com/v1.0/$metadata#communications/callRecords/$entity id : 1234568-1234-1234-1234-1234567890ab version : 2 type : peerToPeer modalities : {audio} lastModifiedDateTime : 13.11.2021 19:42:51 startDateTime : 13.11.2021 19:24:40 endDateTime : 13.11.2021 19:24:44 joinWebUrl : organizer : @{user=; acsUser=; spoolUser=; phone=; guest=; encrypted=; onPremises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=} participants : {@{user=; acsUser=; spoolUser=; phone=; guest=; encrypted=; onPremises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=}, @{acsUser=; spoolUser=; phone=; guest=; encrypted=; onPremises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=}} PS C:\> $calldetails.organizer user : acsUser : spoolUser : phone : @{id=+49160xxxxxxxx; displayName=; tenantId=} guest : encrypted : onPremises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : PS C:\> $calldetails.participants[0] user : acsUser : spoolUser : phone : @{id=+49160xxxxxxx; displayName=; tenantId=} guest : encrypted : onPremises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : PS C:\> $calldetails.participants[1] acsUser : spoolUser : phone : guest : encrypted : onPremises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : user : @{id=1234568-1234-1234-1234-1234567890ab; displayName=Carius, Frank (NAW); tenantId=1234568-1234-1234-1234-1234567890ab} |
MeetNow |
PS C:\> $calldetails.organizer @odata.context : https://graph.microsoft.com/v1.0/$metadata#communications/callRecords/$entity id : 1234568-1234-1234-1234-1234567890ab version : 2 type : groupCall modalities : {audio, video, screenSharing} lastModifiedDateTime : 13.11.2021 21:01:11 startDateTime : 13.11.2021 20:24:43 endDateTime : 13.11.2021 20:39:42 joinWebUrl : https://teams.microsoft.com/l/meetup-join/19%3ameeting_ZTg0ZDI0NTEtMWYyOS00xxxxxxxxxYzFhY 2JiM2I2%40thread.v2/0?context=%7b%22Tid%22%3a%22d<guid>%22%2c%22Oid %22%3a%22<guid>%22%7d organizer : @{acsUser=; spoolUser=; phone=; guest=; encrypted=; onPremises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=} participants : {@{acsUser=; spoolUser=; phone=; guest=; encrypted=; onPremises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=}} PS C:\> $calldetails.participants[0] acsUser : spoolUser : phone : guest : encrypted : onPremises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : user : @{id=1234568-1234-1234-1234-1234567890ab; displayName=Carius, Frank (NAW); tenantId=1234568-1234-1234-1234-1234567890ab} PS C:\> $calldetails.participants[1] user : acsUser : spoolUser : phone : guest : @{id=ec96fafc5e744cc0951fb5e5efcb3aa8; displayName=Guest user; tenantId=} encrypted : onPremises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : |
Federation call (Voicemail) |
PS C:\> $calldetails.participants[1] acsUser : spoolUser : phone : guest : encrypted : onPremises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : user : @{id=b13fed33-b3ab-42b5-83a9-feb2c7665f52; displayName=External user; tenantId=eef62a09-7718-4063-82db-d7582dc8916f} |
Sie sehen aber, dass da noch nicht so viel "Details" zu finden sind.
- Graph: Get callRecord
https://docs.microsoft.com/en-us/graph/api/callrecords-callrecord-get
Get-Callrecord Details
Die Anzeige, wann ein Call gestartet und beendet wurde, ist zwar nett, aber im Teams Admin Portal kann ich viel mehr Details zu einem Anruf sehen. Teams zeigt mir z.B. das Endgeräte, das Headset und viele andere Daten an, die ich auch gerne wüsste aber in dem einfachen CallReport nicht ersichtlich sind. Aber Microsoft beschreibt selbst, wie man die "Full Details" bekommt, indem ich ein "?$expand=sessions($expand=segments)" an die URL anhänge.
Hinweis:
Das "$"-Zeichen ist normal das Kennzeichen für eine
Variable. Hier muss aber ein $-Zeichen in der URL übergeben
werden, damit die Sessiondaten erhalten werden können. Es
macht einen Unterschied, ob sie die URL mit einfachen oder
doppelten Anführungsstrichen einfassen
- Graph: Get callRecord
https://docs.microsoft.com/en-us/graph/api/callrecords-callrecord-get?view=graph-rest-1.0&tabs=http#example-2-get-full-details
$calldetails = Invoke-RestMethod ` -Uri 'https://graph.microsoft.com/v1.0/communications/callRecords/xxxxxguidxxxxxx?$expand=sessions($expand=segments)' ` -Method GET ` -Header @{ 'Authorization' = "Bearer $($accesstoken)"}
Bei einen Meeting wird dann auch das Property "sessions" gefüllt.
PS C:\Users\fcarius> $calldetails7 @odata.context : https://graph.microsoft.com/v1.0/$metadata#communications/callRecords(sessions(segments()))/$entity id : f32197cb-e848-45ab-9c26-54bbff5ed77a version : 1 type : groupCall modalities : {audio, video, videoBasedScreenSharing} lastModifiedDateTime : 12.11.2021 15:04:32 startDateTime : 12.11.2021 12:29:29 endDateTime : 12.11.2021 14:52:55 joinWebUrl : https://teams.microsoft.com/l/meetup-join/19%3ameeting_xxxxxxxxxczMDk 4YjZlOWFk%40thread.v2/0?context=%7b%22Tid%22%3a%22xxxxxxxxxx6%22%2c%2 2Oid%22%3a%22xxxxxxxxxxxf7%22%7d organizer : @{acsUser=; spoolUser=; phone=; guest=; encrypted=; onPremises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=} participants : {@{acsUser=; spoolUser=; phone=; guest=; encrypted=; onPremises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=}, @{acsUser=; spoolUser=; phone=; guest=; encrypted=; onPremises=; acsApplicationInstance=; spoolApplicationInstance=; applicationInstance=; application=; device=; user=}…} sessions@odata.context : https://graph.microsoft.com/v1.0/$metadata#communications/callRecords('xxxxx -xxxxx')/sessions(segments()) sessions : {@{id=xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxx; modalities=System.Object[]; startDateTime=12.11.2021 13:59:12; endDateTime=12.11.2021 14:47:39; failureInfo=; caller=; callee=; segments@odata.context=https://graph.microsoft.com/v1.0/$metadata#communications/call Records('xxxxxxxxxx')/sessions('xxxxxx')/segments; segments=System.Object[]}, @{id=xxxx; modalities=System.Object[]; startDateTime=12.11.2021 12:29:41; endDateTime=12.11.2021 13:33:17; failureInfo=; caller=; callee=; segments@odata.context=https://graph.microsoft.com/v 1.0/$metadata#communications/callRecords('xxxxxxx')/sessions('xxxxxxx')/segments; segments=System.Object[]}, @{id=xxxxx; modalities=System.Object[]; startDateTime=12.11.2021 12:29:34; endDateTime=12.11.2021 14:52:52; failureInfo=; caller=; callee=; segments@odata.context=https://graph.microsoft.com/v 1.0/$metadata#communications/callRecords('xxxxx')/sessions('xxxxxxxx')/segments; segments=System.Object[]}…}
Und jede Session enthält umfangreiche Daten über den Teilnehmer (Useragent, sein Feedback etc.
$calldetails.sessions.caller.useragent.headervalue $calldetails.sessions.caller.feedback $calldetails8.sessions.Segments.media.streams.maxjitter $calldetails8.sessions.Segments.media.streams.maxPacketLossRate $calldetails8.sessions.Segments.media.calleenetwork $calldetails8.sessions.Segments.media.callernetwork
Die JSON-Struktur ist sehr verschachtelt und Microsoft hat sie recht gut dokumentiert.
Die Blattknoten haben folgende Properties. Nicht immer sind alle Felder gefüllt:
Blatt | Properties |
---|---|
Verzeigungsknoten![]() |
Unterschiedliche Zusatzproperties. Siehe Informationen im vorigen Bild. |
Organizer, Participants
Identity![]() |
user :@{id=userGUID; displayName=Carius, Frank; tenantId=<tenantguid>} acsUser : spoolUser : phone : @{id=+49160xxxxxxxx; displayName=; tenantId=} guest : encrypted : onPremises : acsApplicationInstance : spoolApplicationInstance : applicationInstance : application : device : |
userAgent![]() |
@odata.type : #microsoft.graph.callRecords.clientUserAgent headerValue :CallSignalingAgent (27/1.4.00.30068//;release_petrusp/2558553_mac_nr_multi_compositor_ui_api.2021.34.01.22;releases/CL2021.R34) applicationVersion : platform : windows productFamily : teams |
callerNetwork,
calleeNetwork![]() |
ipAddress : 192.168.178.91 subnet : 192.168.178.0 linkSpeed : 1000000000 connectionType : wired port : 50006 reflexiveIPAddress : 94.31.83.223 relayIPAddress : 52.112.172.67 relayPort : 50488 macAddress : f2:20:7a:12:34:45 wifiMicrosoftDriver : Virtueller Microsoft Wi-Fi Direct-Adapter wifiMicrosoftDriverVersion : Microsoft:10.0.19041.1 wifiVendorDriver : Intel(R) Dual Band Wireless-AC 8265 wifiVendorDriverVersion : Intel:20.70.24.1 wifiChannel : wifiBand : unknown basicServiceSetIdentifier : wifiRadioType : unknown wifiSignalStrength : wifiBatteryCharge : dnsSuffix : sentQualityEventRatio : 0 receivedQualityEventRatio : 0 delayEventRatio : 0 bandwidthLowEventRatio : |
callerDevice,
calleeDevice![]() |
captureDeviceName : 2- Jabra Evolve 65 captureDeviceDriver : Microsoft: 10.0.19041.1202 renderDeviceName : 2- Jabra Evolve 65 renderDeviceDriver : Microsoft: 10.0.19041.1202 sentSignalLevel : receivedSignalLevel : -35 sentNoiseLevel : receivedNoiseLevel : -68 initialSignalLevelRootMeanSquare : cpuInsufficentEventRatio : 0 renderNotFunctioningEventRatio : 0 captureNotFunctioningEventRatio : 0 deviceGlitchEventRatio : 0 lowSpeechToNoiseEventRatio : 0 lowSpeechLevelEventRatio : 0 deviceClippingEventRatio : 0 howlingEventCount : 0 renderZeroVolumeEventRatio : 0 renderMuteEventRatio : 0 micGlitchRate : speakerGlitchRate : |
Streams![]() |
streamId : 2874020951 startDateTime : endDateTime : streamDirection : callerToCallee averageAudioDegradation : averageJitter : PT0S maxJitter : PT0.001S averagePacketLossRate : 0 maxPacketLossRate : 0 averageRatioOfConcealedSamples : 0 maxRatioOfConcealedSamples : 0 averageRoundTripTime : maxRoundTripTime : packetUtilization : 3725 averageBandwidthEstimate : wasMediaBypassed : postForwardErrorCorrectionPacketLossRate : averageVideoFrameLossPercentage : averageReceivedFrameRate : lowFrameRateRatio : averageVideoPacketLossRate : averageVideoFrameRate : lowVideoProcessingCapabilityRatio : averageAudioNetworkJitter : PT0.002S maxAudioNetworkJitter : PT0.004S |
Die Daten sind schon sehr umfangreich, auch wenn es durchaus Lücken gibt. Insbesondere bei Gegenstellen, die z.B. ein Telefon sind oder per Federation in einem anderen Tenant unterwegs sind. Allerdings kann ich auch sehen, wann welche Sessions gestartet und geendet haben. Hier müssen Sie auch damit rechnen, dass ein Client das Meeting verlässt und wiederkehrt oder Video Ein/Abschaltet. All das sehen Sie z.B. auch im Teams Admin Center:
Entsprechend sind in der JSON-Antwort durchaus mehrere Streams pro Benutzer verzeichnet. Der "Attendance Report" könnte ich so auch generieren. Die CallID zu Meetings habe ich nun ja auch.
- Microsoft Teams – Anwesenheitsbericht zu
einer Besprechung
https://docs.microsoft.com/de-de/microsoftteams/teams-analytics-and-reports/meeting-attendance-report - Working with the call records API in
Microsoft Graph
https://docs.microsoft.com/en-us/graph/api/resources/callrecords-api-overview - Graph: Get callRecord - Full Details
https://docs.microsoft.com/en-us/graph/api/callrecords-callrecord-get?view=graph-rest-1.0&tabs=http#example-2-get-full-details
Weitere Call Events sind noch zu beschreiben
Weitere Links