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.6K Views
Microsoft

 

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.

 

Implementation

 

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, http://portal.azure.com
  • 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:

O365MultiConnector1.gif

  • 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 = "https://login.microsoftonline.com/"  
$tenantdomain = "<tenant-domain>.onmicrosoft.com"  
# Get the tenant GUID from Properties | Directory ID under the Azure Active Directory section 
$TenantGUID = "<tenant_ID>"  
$resource = "https://manage.office.com"  
$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 "https://manage.office.com/api/v1.0/$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.AzureActiveDirectory&PublisherIdentifier=$Publisher" 
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "https://manage.office.com/api/v1.0/$tenantGuid/activity/feed/subscriptions/start?contentType=DLP.ALL&PublisherIdentifier=$Publisher" 
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "https://manage.office.com/api/v1.0/$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.General&PublisherIdentifier=$Publisher" 
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "https://manage.office.com/api/v1.0/$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.Exchange&PublisherIdentifier=$Publisher" 
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "https://manage.office.com/api/v1.0/$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.SharePoint&PublisherIdentifier=$Publisher" 

 

 

 

Step(2): Cross-Tenants Custom Connector

  • Log in to "Pt" Azure tenant, http://portal.azure.com
  • 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

O365MultiConnectorPlaybookPart1.gif

 

O365MultiConnectorPlaybookPart3.gif

 

O365MultiConnectorPlaybookPart4.gif

 

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.

 

18 Comments
Bronze Contributor

I'm guessing this ingestion would not be free anymore since it is not using the OOTB connector?

Microsoft

Yes, correct @Gary Bushey , cost point mentioned under "notes & consideration" Section.

Brass Contributor

@Hesham Saad Thanks for the post. I was actually checking on this a couple of days back. 

Since this feature was available in the Office 365 connector and was then depreciated due to some security concerns, do you know what concerns were there exactly. 

Also isn't it more appropriate to allow the customer to accept the risk and allow the same functionality.

Because it was really easy and straightforward to use the the OOTB connector to connect external O365 tenants and we would get the logs under OfficeActivity table and not as Custom logs. Also the burden of cost per run of log ingestion would have been avoided.

I really think we should make Logic Apps free, since most workarounds and custom features end up with Logic Apps.

Copper Contributor

Many thanks for the post. Can you please be more specific about the concepts Parent Tenant (Pt) and Child Tenant (Ct)? I do not understand the difference between them.

Microsoft

No problem @TH-Zenon75  -  please refer to the post top section "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."

 

The use case here is to invoke and pull logs from specific tenant (called child Ct tenant) to ingest at the parent (Pt) tenant.

Microsoft

Thanks @Joseph-Abraham  for the feedback, we will share it with the product group team.

Copper Contributor

Thank you for sharing. This is a wonderful workaround. But I have some questions about the afterward. When we send this kind of logs to PT, the logs will be kept on custom tables so I guess the built-in analytics rules for O365 can not work because built-in rule queries work with the default tables not for the custom tables am I right? 

Copper Contributor

Hi Folks,

Great article it helps a lot when starting with logic apps ;)

 

One comment : Microsoft provides workbooks, queries, ... based on Microsoft log analytics table "OfficeActivity" . How can we translate from our custom log parsing to Microsoft standard attributes names and values ?

Brass Contributor

FYI for anyone trying this, you got to start the subscriptions first as mentioned here, else you get an error AF20023

https://docs.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api...

 

You need to start this for all content-types mentioned below, i.e. if you need those. Read this page, it will give you a better idea of what we are doing here.

https://docs.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api...

Brass Contributor

@Hesham Saad  I have been working on this playbook and wanted to highlight an issue.

I believe we are using the below endpoint to list contenets.

https://docs.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api...

 

We need to use the start time and end time here, else every 5 minutes it is going to pull all the data available in the last 24 hours as mentioned in the doc.

 

For anyone trying this, you could use the utcnow() for start time and subtractfromtime() and calculate the end time by subtarcting from utc time. these functions are explained in doc below.

https://docs.microsoft.com/en-us/azure/logic-apps/workflow-definition-language-functions-reference#d...

Copper Contributor

I am trying to get this to work and getting the error  "Calling principal does not have required MSGraph permissions AuditLog.Read.All"

I have checked the API Permissions, even completely rebuilt the account and still not working.

this is at the HTTP-GraphAPI step.

any thoughts?

 

Microsoft

Hi @AlexReid - did you mean you added AudiLog API permission and still giving you this principal error ?

Copper Contributor

HI @Hesham Saad 

yes that is correct. here is the permissions setup in the child tenant. 

AlexReid_0-1623072719285.png

and here is error i get with the Logic app

AlexReid_1-1623072907617.png

AlexReid_2-1623072943509.png

 

Thoughts on what i might have wrong?

 

 

 

Microsoft

@AlexReid - please share your email address

Brass Contributor

@AlexReid am not sure but the API permission type seems to be different. You have used Delegated in the screenshot while as seen in below link it shows Application permission.

https://techcommunity.microsoft.com/t5/image/serverpage/image-id/231185iC568B89AE6D69EF2/image-size/...

Copper Contributor

@Hesham Saad @Joseph-Abraham 

Thank you 

i think this found the problem. I had Delegated when i needed the Application permissions. I get a different error now which i suspect has to to do with my SKU.

"

Neither tenant is B2C or tenant doesn't have premium license"
I only have E3 in my test tenant 
thank you
 
Copper Contributor

Does this solution actually work for the Graph API? I've tried to set it up and it doesn't work as expected at all. It fails on two things:

1. There is no tracking of what events have already been downloaded/ingested, so every time the LogicApp runs it pulls down the entire list of events available in the tenant, causing the same events to be duplicated over and over.

2. It takes all available events from the API and puts them into a single truncated event in Sentinel. 

So the end result is in Sentinel we just have tons of duplicated events that are unusable because they all contain the same truncated list of every available event in the tenant. In order for this to work it would need some way to track what events it has already ingested and it would need to iterate over individual items to store them as separate events in Sentinel.

Brass Contributor

@Trevor Smith - have you able to resolve this issue.

@Hesham Saad  - do we have any updated version of this, I doubt on O365 logs. unable to see SharePoint, exchange longs by this playbook. 

 

Can we have similar playbook to fetch other tenant's Azure activity logs 

 

Thanks.

 

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