Blog Post

Microsoft Sentinel Blog
6 MIN READ

Secure your Calls- Monitoring Microsoft TEAMS CallRecords Activity Logs using Azure Sentinel

Hesham_Saad's avatar
Hesham_Saad
Icon for Microsoft rankMicrosoft
Aug 08, 2020

This blog is authored and technically implemented by Hesham_Saad with hearty thanks to our collaborator and use-cases executive mind brain yazanouf 

 

Before we dig deep on monitoring TEAMS CallRecords Activity Logs, please have a look at "Protecting your Teams with Azure Sentinel" blog post by Pete Bryan on how to ingest TEAMS management logs into Azure Sentinel via the O365 Management Activity API

 

Collecting TEAMS CallRecords Activity Data 

This section we will go into details on how to ingest TEAMS CallRecords activity logs into Azure Sentinel via the Microsoft Graph API and mainly leveraging CallRecords API which is a Graph webhook API that will give access to the Calls activity logs. SOC team can subscribe to changes to CallRecords via Azure Sentinel and using the Microsoft Graph webhook subscriptions capability, allowing them to build near-real-time reports from the data or to alert on specific scenarios , use cases which mentioned above.

 

Technically you can use the call records APIs to subscribe to call records and look up call records by IDs, the call records API is defined in the OData sub-namespace, microsoft.graph.callRecords.

 

So, what are the key resources types returned by the API ?

Resource Methods Description
CallRecord Get callRecord Represents a single peer-to-peer call or a group call between multiple participants
session Get callRecord
List sessions
A peer-to-peer call contains a single session between the two participants in the call. Group calls contain one or more session entities. In a group call, each session is between the participant and a service endpoint.
segment Get callRecord
List sessions
A segment represents a media link between two endpoints.

 

The callRecord entity represents a single peer-to-peer call or a group call between multiple participants, sometimes referred to as an online meeting.  A peer-to-peer call contains a single session between the two participants in the call. Group calls contain one or more session entities. In a group call, each session is between the participant and a service endpoint. Each session contains one or more segment entities. A segment represents a media link between two endpoints. For most calls, only one segment will be present for each session, however sometimes there may be one or more intermediate endpoints. For more details click here

 

Below is the main architecture diagram including the components to deploy Teams CallRecords Activity Logs Connector:

Deployment steps:

Register an App 

Create and register Azure AD APP to handle the authentication and authorization to collect data from the Graph API. Here are the steps - navigate to the Azure Active Directory blade of your Azure portal and follow the steps below: 

  1. Click on App Registrations 
  2. Select ‘New Registration 
  3. Give it a name and click Register. 
  4. Click API Permissions blade. 
  5. Click Add a Permission. 
  6. Click Microsoft Graph. 
  7. Click Application Permissions’. 
  8. Search for 'CallRecords', Check CallRecords.Read.All. Also, Search for 'Directory' and Check Directory.ReadWrite.All and 'Click Add permissions. 
  9. Click grant admin consent’. 
  10. Click Certificates and Secrets’. 
  11. Click New Client Secret 
  12. Enter a description, select never. Click Add. 
  13. NoteClick copy next to the new secret and store it somewhere temporarily. You cannot come back to get the secret once you leave the blade.  
  14. Copy the client Id from the application properties and store it. 
  15. Copy the tenant Id from the main Azure Active Directory blade and store it. 

 

 

 

Deploy a Logic App 

Last step is to collect the CallRecords activity data and ingest it into Azure Sentinel via a Logic App.

Navigate to Azure Sentinel workspace, click at Playbooks blade and follow the steps below: 

  1. Click on Add Playbook' 
  2. Select 'Resource Group', type a name to your logic app for example 'TeamsCalls-SecurityGraphAPI' and toggle on 'Log Analytics'
  3. Click 'Review + Create' then 'Create'
  4. Open your new logic app 'TeamsCalls-SecurityGraphAPI'
  5. Under 'Logic app designer', add the following steps:
    1. Add 'Recurrence' step and set the value to 10 minute for example
    2. Add 'HTTP' step to create CallRecords subscriptions, creating a subscriptions will subscribe a listener application to receive change notifications when the requested type of changes occur to the specified resource in Microsoft Graph, for more details on Create Subscriptions via Microsoft Graph API
      1. Method: POST
      2. URI: https://graph.microsoft.com/beta/subscriptions
      3. Body, note that you can edit 'changeType' value with 'created,updated' for example, 'notificationUrl' is the subscription notification endpoint, please refer to the note section at the end of the blog post to know how you can get the notificationURL hosted in Azure Function, for more details on notificationUrl,
        1. {
            "changeType": "created",
            "clientState": "secretClientValue",
            "expirationDateTime": "2022-11-20T18:23:45.9356913Z",
            "latestSupportedTlsVersion": "v1_2",
            "notificationUrl": "https://outlook.office.com/webhook/3ec886e9-86ef-4c86-bfff-2d0321f3313e@2006d214-5f91-4166-8d92-95f5e3ad9ec6/IncomingWebhook/9c6e121ed--x-x-x-x99939f71721fcbcc7/03c99422-50b0-x-x-x-ea-a00e-2b0b-x-x-x-12d5",
            "resource": "/communications/callRecords"
          }
      4. Authentication Type: Active Directory OAuth
      5. Tenant: with Tenant ID copied above
      6. Audience: https://graph.microsoft.com
      7. Client ID: with Client ID copied above
      8. Credential Type: Secret
      9. Secret: with Secret value copied above
    3. Add 'HTTP' step to list all subscriptions:
      1. Method: GET
      2. URI: https://graph.microsoft.com/v1.0/subscriptions
      3. Authentication Type: Active Directory OAuth
      4. Tenant: with Tenant ID copied above
      5. Audience: https://graph.microsoft.com
      6. Client ID: with Client ID copied above
      7. Credential Type: Secret
      8. Secret: with Secret value copied above
    4. If you want to get all sessions details per specific call record session ID follow the below steps, noting that the below example is for a single CallRecord Session ID for the sake of demonstration and hence we added a variable item, you can add a loop step to get all sessions IDs from the created CallRecords subscription step, noting that there is a change documented here by Oct 2020 so in order to get the callRecords IDs list you need to use the callChainId property of a call. The call record is available only after the associated call is completed, for more details click here:
      1. Method: GET
      2. URI: https://graph.microsoft.com/beta/communications/callRecords/@{variables('TEAMSCallRecordsID')}/sessions
      3. Authentication Type: Active Directory OAuth
      4. Tenant: with Tenant ID copied above
      5. Audience: https://graph.microsoft.com
      6. Client ID: with Client ID copied above
      7. Credential Type: Secret
      8. Secret: with Secret value copied above
    5. Add 'Send TEAMS CallRecords Data to Azure Sentinel LA-Workspace' step, after doing the connection successfully via your Azure Sentinel Workspace ID & Primary key:
      1. JSON Request Body: Body
      2. Custom Log Name: TEAMSGraphCallRecords

 

 

 

The complete Playbook code view have been uploaded to github repo as well, please click here for more details and check out the readme section.

 

NotificationURL code (Hosted in Azure Function) sample:

 

using System.Net;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log, TextWriter outputBlob)
{
  log.LogInformation($"Webhook was triggered!");

  // Grab the validationToken URL parameter

  If(reg.QueryString.HasValue)
  {
    string Token = req.Query["validationtoken"];
    if(!string.isNullorEmpty(Token))
    {
      log.LogInformation($"Validation token: {Token}");
      return (ActionResult)new OkObjectResult(validaTokentionToken);
    }
  }

  JObject jsonContent;
  string notifContent;
  using (var reader = new StreamReader(reg.Body))
  {
    notifContent = reader.ReadToEnd();
    Log.LogInformation(notifContent);
    jsonContent =JObject.Parse(notifContent);
  }
}

 

 

To get a list of call records without the need to hard-code / set CallRecord ID you need to do the following:

 

  • Set up the subscription for the CallRecord resource.
  • Receive notification (POST) when a new CallRecord is created or updated (based on the subscription). This notification contains the CallRecordId
  • For each notification, use the GET API to retrieve that CallRecord.

Monitoring TEAMS CallRecords Activity

When the Playbook run successfully, it will create a new custom log table 'TEAMSGraphCallRecords_CL' that will have the CallRecords activity logs, you might wait for a few minutes till the new CL table been created and the CallRecords activity logs been ingested.

 

Navigate to Azure Sentinel workspace, click at Logs blade and follow the steps below: 

  1. Tables > Group by: Solution > Custom Logs: TEAMSGraphCallRecords_CL
  2. Below are the list of main attributes that have been ingested:
    1. TimeGenerated
    2. Type_s: groupCall
    3. modalities_s: Audio, Video, ScreenSharing, VideoBasedScreenSharing
    4. LastModifiedDateTime
    5. StartDateTime, endDateTime
    6. joinWebUrl_s
    7. organizer_user_displayname_s
    8. participants_s
    9. sessions_odata_context_s
  3. As you can see from the results below we get the complete TEAMS CallRecords activity logs.

 

Parsing the Data 

Before building any detections or hunting queries on the ingested TEAMS CallRecords Activity data we can parse and normalize the data via a KQL Function to make it easier to use:

 

 

The parsing function have been uploaded as well to the github repo.

 

Part (2): we will share a couple of hunting queries and upload them to github, it's worth to explore Microsoft Graph API as there are other TEAMS related APIs logs that can be ingested based on the requirements and use cases:

  • TeamsActivity: 
    • Read all users' teamwork activity feed
  • TeamsAppInstallation:
    • Read installed Teams apps for all chats
    • Read installed Teams apps for all teams
    • Read installed Teams apps for all users
  • TeamsApp
    • Read all users' installed Teams apps

...etc

 

 

 

We will be continuing to develop detections and hunting queries for Microsoft 365 solutions data over time so make sure you keep an eye on GitHub As always if you have your own ideas for queries or detections please feel free to contribute to the Azure Sentinel community. 

Updated Nov 03, 2021
Version 4.0
  • Mike Crowley's avatar
    Mike Crowley
    Brass Contributor

    Reliability and scalability concerns aside (running a blanket query every 10 minutes) I am not liking this trend. Exchange mail flow and Teams call records should have first party integration. Customers do not want to pay for sentinel only to have to use additional Azure services (which also cost btw) to complete the product. This is causing organizations to look elsewhere for solutions…

  • MikeCrowley's avatar
    MikeCrowley
    Iron Contributor

    Thanks, yes I'm talking about 1st party connectors, thanks. In this thread, Ofer_Shezaf states that message tracking is on the roadmap, but his comment was from last year. The same request/concerns would apply to Teams and other Microsoft products. Customers should not have to engineer or pay for connectivity between Microsoft's own services, especially for basic use cases like these.

  • Paul_Mitchell's avatar
    Paul_Mitchell
    Copper Contributor

    We serve many customers with Call Reports from a whole boquet of PBX's, from a myriad of vendors.  I understand the purpose of using the notification typology for real-time circumstances.  We do have customers with real-time requirements where we bar usage of a device if an expense quota has been reached.  My concern is, what happens if a customer's server is down, and is not listening for notifications?  Will a stream of notifications be sent when communications are re-established?  Given the call record size, I understand why you don't allow a date range parameter query on the call record graph.  Is there any way a GetCallIDList allowing date range parameters can be made available, which info can be used in turn to request each call record?  This would allay fears that notifications have been missed, and wouldn't necessitate real-time notification for customers where there is no such requirement?

  • ph_ly's avatar
    ph_ly
    Steel Contributor

    What about individual meeting records?

     

    These arent even exposed in a GUI report.  Big limitation. 

  • Thanks Mike Crowley , we will share the feedback with Product group team on a ready / customized connectors, please keep them coming, quick point to mention that you can get all activities and logs details for Exchange and Teams via Office 365 Security & Compliance dashboard (http://protection.office.com) & TEAMS admin dashboard https://admin.teams.microsoft.com/dashboard without any ingestion flow - cost if the solutions licenses are already in place, the blog post here is a way for SOC people to export to their SIEM.

  • Good afternoon, I am extracting the call or meeting logs with the subscription notifications from /communications/callRecords, when a meeting is recorded it is uploaded to MStream, the url of the recording can be obtained by this subscription? Or should I make other requests to Stream? I want to also have the URL of the saved call available.
    Another question is the subscription, does it have to be updated or can it be made permanent?

    Thank you very much in advance!

  • Kyrouz's avatar
    Kyrouz
    Copper Contributor

    Hello - any chance of call logs being incorporated into an existing or new data connector soon?  I see this was written three years ago and don't want to go down this path if I'll soon be able to ingest this information by ticking a box.