Microsoft Secure Tech Accelerator
Apr 03 2024, 07:00 AM - 11:00 AM (PDT)
Microsoft Tech Community
O365 & AAD Multi-Tenant Custom Connector - Azure Sentinel
Published Nov 09 2020 02:57 AM 12.3K Views


Overview & Use Case


Thanks to @Ofer_Shezaf  and @Yaniv Shasha for the brainstorming, contributing and proof reading! 


A multi-national organization having multiple branches with different identities across the globe, each branch have it's own separate tenant (AAD & O365) logs and the global SOC team are looking for a way on how to ingest these logs coming from all branches to their main parent tenant via Azure Sentinel.


While Azure Sentinel can’t natively do that using the out-of-the box data connectors (Office 365 / Azure AD), we cover your back with O365 & AAD Multi-Tenancy custom connector via an Azure Sentinel Playbook (Logic App) that invoke the Office 365 Management API & Graph API to read from multiple tenants and writes to a custom table in Sentinel, same can be done using Azure Function.




With the help of Office 365 Management API & Microsoft Graph API we can invoke specific content types cross-tenants:


API Content-Type / Activity Log
Office 365 Management API Audit.AzureActiveDirectory
Office 365 Management API Audit.Exchange
Office 365 Management API Audit.SharePoint
Office 365 Management API Audit.General (includes all other workloads not included in the previous content types)
Office 365 Management API DLP.All (DLP events only for all workloads)
Graph API audit-Logs
Graph API Sign-in Logs


So, tell us more about the steps?


Assuming, we have a parent tenant (Pt) & a child tenant (Ct) and the requirements is to invoke the "Ct" tenant O365 Management API & Graph APIs to pull Office 365 & AAD logs and ingest at "Pt" ones.


Step(1): Prep & App Registration

  • Ensure that "Ct" Office 365 Security & Compliance audit logs is enabled 
  • Log in to "Ct" Azure tenant,
  • Search for App Registration > New Registration
  • Type Name, ensure of selecting the right "supported account type": Accounts in any organizational directory (Any Azure AD directory - Multitenant), then click Register button
  • Follow the below gif / step-by step guide on how to define and configure the API permissions & create the secret:


  • Click at “API permissions”
    • Add a permission > Office 365 Management API > Application permissions
    • Select and check “ActivityFeed.Read” , “ActivityFeed.ReadDlp” & “ServiceHealth.Read”
    • Add a permission > Microsoft Graph
    • Select and check “Directory.Read.All”, “AuditLog.Read.All”
    • Click at “grant admin consent” link
  • Click at “Certificates & secrets”
    • Under Client secrets > Add client secret
  • Get the following values:
    • Application / Client ID
    • Tenant ID
    • Secret
    • Azure Active Directory Domain
  • Register the API subscription via PowerShell, run the below PowerShell as administrator and connect to the "Ct" tenant directory, ensure of replacing $ClientID, $ClientSecret, $tenantdomain and $TenantGUID with the above copied values:



Connect-AzAccount # connect via child "Pt" admin account

# Populate with App ID and Secret from your Azure AD app registration 
$ClientID = "<Client_ID>"  
$ClientSecret = "<Secret>"  
$loginURL = ""  
$tenantdomain = "<tenant-domain>"  
# Get the tenant GUID from Properties | Directory ID under the Azure Active Directory section 
$TenantGUID = "<tenant_ID>"  
$resource = ""  
$body = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret} 
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body  
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}   
$publisher = New-Guid
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.AzureActiveDirectory&PublisherIdentifier=$Publisher" 
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "$tenantGuid/activity/feed/subscriptions/start?contentType=DLP.ALL&PublisherIdentifier=$Publisher" 
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.General&PublisherIdentifier=$Publisher" 
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.Exchange&PublisherIdentifier=$Publisher" 
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.SharePoint&PublisherIdentifier=$Publisher" 




Step(2): Cross-Tenants Custom Connector

  • Log in to "Pt" Azure tenant,
  • Go to Azure Sentinel > Playbooks
  • Create a new Playbook and follow the below gif / step-by-step guide, the code being uploaded to github repo as well:
    • Add a “Recurrence” step and set the following field, below is an example to trigger the Playbook every 5 minutes:
      • Interval: 5
      • Frequency: Minute
    • Initialize set of variables for the content-types values:
    • Set an HTTP endpoints to Get content-types data, ensure the authorization type is Active Directory OAuth:
      • HTTP – Get Logs – O365API:
        • Method: GET
        • URI: @{variables('AAD')}
        • Headers:
          • Accept: application/json
          • Content-Type: application/json
        • Authentication type: Active Directory OAuth
      • HTTP – GraphAPI:
        • Method: GET
        • URI: @{variables('Sign-Ins')}
        • Headers:
          • Accept: application/json
          • Content-Type: application/json
        • Authentication type: Active Directory OAuth
    • Parse content-types data via Json:
      • Parse JSON – O365API:
        • Content: @{body('HTTP__-_Get_Logs-O365API')}
        • Schema: uploaded to github
      • Parse JSON – GraphAPI:
        • Content: @{body('HTTP_-_GraphAPI')}
        • Schema: uploaded to github
    • Next step is to send logs to Azure Sentinel via a custom log table, so will show an example of iterating all returned values from O365 Management API and send data to Log analytics and another example of sending the raw-data from Graph API to log analytics without the iteration phase:
      • Iterate (For-each) on all returned values (Body) and Get contentUri from Office 365 API and value from Graph API via an embedded - looped HTTP Endpoints, ensuring that the authorization type is Active Directory OAuth:
        • Add  For Each control:
          • Select an output from previous steps: @body('Parse_JSON_-_O365API')
        • Add HTTP endpoint step:
          • Method: GET
          • URI: @{items('For_each_-O365API')['contentUri']}
          • Headers:
            • Accept: application/json
            • Content-Type: application/json
          • Authentication: Active Driectory OAuth
        • Add Send Data to Log Analytics Control:
          • JSON Request body: @{json(body('HTTP'))}
          • Custom Log Name: ChildO365AAD
    • Send the data (Office 365 Management API and Graph API) to Azure Sentinel Log analytics workspace via a custom log tables:
      • JSON Request body: @{body('HTTP_-_GraphAPI')}
      • Custom Log Name: ChildAADSignIn







Notes & Consideration


  • You can customize the parsers at the connector's flow with the required and needed attributed / fields based on your schema / payload before the ingestion process, also you can create custom Azure Functions once the data being ingested to Azure Sentinel
  • Azure Function can be used to create custom connector for multi-tenant access, here’s a great example created by Jon Nord invoking Office 365 Management API which you can extend and add Graph API as well.
  • Couple of points to be considered while using Logic Apps:


Get started today!


We encourage you to try it now!

You can also contribute new connectors, workbooks, analytics and more in Azure Sentinel. Get started now by joining the Azure Sentinel Threat Hunters GitHub community.


Version history
Last update:
‎Nov 02 2021 06:24 PM
Updated by: