Blog Post

Microsoft Mission Critical Blog
6 MIN READ

🚀 Export D365 CE Dataverse Org Data to Cosmos DB via the Office365 Management Activity API

PravinT's avatar
PravinT
Icon for Microsoft rankMicrosoft
Sep 11, 2025

📘 Preface 

This post demonstrates one method to export Dynamics 365 Customer Engagement (CE) Dataverse organization data using the Office 365 Management Activity API and Azure Functions. It is feasible for customers to build a custom lake-house architecture with this feed, enabling advanced analytics, archiving, or ML/AI scenarios. 

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

 

🧭 When to Use This Custom Integration 

While Microsoft offers powerful native integrations like Dataverse Synapse Link and Microsoft Fabric, this custom solution is observed implemented and relevant in the following scenarios: 

  • Third-party observability and security tools already use this approach 

Solutions such as Splunk and other enterprise-grade platforms commonly implement integrations based on the Office 365 Management Activity API to ingest tenant-wide audit data. This makes it easier for customers to align with existing observability pipelines or security frameworks. 

  • Customers opt out of Synapse Link or Fabric 

Whether due to architectural preferences, licensing constraints, or specific compliance requirements, some customers choose not to adopt Microsoft’s native integrations. The Office Management API offers a viable alternative for building custom data export and monitoring solutions tailored to their needs. 

 

🎯 Why Use the Office 365 Management Activity API? 

  • Tenant-wide Data Capture: Captures audit logs and activity data across all Dataverse orgs in a tenant. 
  • Integration Flexibility: Enables export to Cosmos DB, cold storage, or other platforms for analytics, compliance, or ML/AI. 
  • Third-party Compatibility: Many enterprise tools use similar mechanisms to ingest and archive activity data. 

 

🏗️ Architecture Overview 

  • Azure Function App (.NET Isolated): Built as webhook, processes notifications, fetches audit content, and stores filtered events in Cosmos DB. 
  • Cosmos DB: Stores audit events for further analysis or archiving. 
  • Application Insights: Captures logs and diagnostics for troubleshooting. 

 

🛠️ Step-by-Step Implementation 

https://learn.microsoft.com/en-us/office/office-365-management-api/get-started-with-office-365-management-apis#build-your-app

1. Prerequisites
  • Azure subscription 
  • Dynamics 365 CE environment (Dataverse) 
  • Azure Cosmos DB account (SQL API) 
  • Office 365 tenant admin rights 
  • Enable Auditing in Dataverse org 
2. Register an Azure AD App 
  • Go to Azure Portal > Azure Active Directory > App registrations > New registration 
  • Note: 
    • Application (client) ID 
    • Directory (tenant) ID
    • Create a client secret 
  • Grant API permissions: 
    • ActivityFeed.Read 
    • ActivityFeed.ReadDlp 
    • ServiceHealth.Read 
  • Grant admin consent 
3. Set Up Cosmos DB
  • Create a Cosmos DB account (SQL API) 
  • Create: 
    • Database: officewebhook 
    • Container: dynamicsevents
    • Partition key: /tenantId 
  • Note endpoint URI and primary key
4. Create the Azure Function App
  • Use Visual Studio or VS Code
  • Create a new Azure Functions project (.NET 8 Isolated Worker) 
  • Add NuGet packages: 
    • Microsoft.Azure.Functions.Worker 
    • Microsoft.Azure.Cosmos 
    • Newtonsoft.Json 
  •  

Function Logic: 

  • Webhook validation 
  • Notification processing
  • Audit content fetching
  • Event filtering 
  • Storage in Cosmos DB
5. Configure Environment Variables
    { 
      "OfficeApiTenantId": "<your-tenant-id>", 
       "OfficeApiClientId": "<your-client-id>", 
       "OfficeApiClientSecret": "<your-client-secret>", 
       "CrmOrganizationUniqueName": "<your-org-name>", 
       "CosmosDbEndpoint": "<your-cosmos-endpoint>", 
       "CosmosDbKey": "<your-cosmos-key>", 
       "CosmosDbDatabaseId": "officewebhook", 
       "CosmosDbContainerId": "dynamicsevents", 
       "EntityOperationsFilter": { 
         "incident": ["create", "update"], 
         "account": ["create"] 
       } 
     } 

 

6. Deploy the Function App
  • Build and publish using Azure Functions Core Tools or Visual Studio 
  • Restart the Function App from Azure Portal 
  • Monitor logs via Application Insights 

 

🔔 How to Subscribe to the Office 365 Management Activity API for Audit Notifications 

To receive audit notifications, you must first subscribe to the Office 365 Management Activity API. This is a two-step process: 

https://learn.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-reference#start-a-subscription

1. Fetch an OAuth2 Token

Authenticate using your Azure AD app credentials to get a bearer token: 

https://learn.microsoft.com/en-us/office/office-365-management-api/get-started-with-office-365-management-apis 

# Define your Azure AD app credentials 

     $tenantId = "<your-tenant-id>"
     $clientId = "<your-client-id>" 
     $clientSecret = "<your-client-secret>" 
      
# Prepare the request body for token fetch 

     $body = @{ 
         grant_type    = "client_credentials"
         client_id     = $clientId 
         client_secret = $clientSecret 
         scope         = "https://manage.office.com/.default" 
     } 

      
# Fetch the OAuth2 token 
$tokenResponse = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Body $body 
$token = $tokenResponse.access_token 

  

    

 2. Subscribe to the Content Type

Use the token to subscribe to the desired content type (e.g., Audit.General): 

https://learn.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-reference#working-with-the-office-365-management-activity-api 

$contentType = "Audit.General"
$headers = @{ 
 Authorization = "Bearer $token" 
 "Content-Type" = "application/json"
  } 

$uri = "https://manage.office.com/api/v1.0/$tenantId/activity/feed/subscriptions/start?contentType=$contentType" 
$response = Invoke-RestMethod -Method Post -Uri $uri -Headers $headers 
$response 

 

⚙️ How the Azure Function Works 

🔸 Trigger 

The Azure Function is triggered by notifications from the Office 365 Management Activity API. These notifications include audit events across your entire Azure tenant—not just Dynamics 365. 

🔸 Filtering Logic 

Each notification is evaluated against your business rules: 

  • Organization match 
  • Entity type (e.g., incident, account) 
  • Operation type (e.g., create, update) 

These filters are defined in the EntityOperationsFilter environment variable: 

  { 
      "incident": ["create", "update"],
       "account": ["create"] 
  } 

    

🔸 Processing 
  • If the event matches your filters, the function fetches the full audit data and stores it in Cosmos DB. 
  • If not, the event is ignored. 

 

🔍 Code Explanation: The Run Method

1. Webhook Validation

https://learn.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-reference#webhook-validation 

string validationToken = query["validationToken"]; 
 if (!string.IsNullOrEmpty(validationToken)) { 
          await response.WriteStringAsync(validationToken); 
          response.StatusCode = HttpStatusCode.OK; 
          return response;
 } 
2. Notification Handling

https://learn.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-reference#receiving-notifications 

 var notifications = JsonConvert.DeserializeObject<dynamic[]>(requestBody); 
foreach (var notification in notifications) 
{ 
      if (notification.contentType == "Audit.General" && notification.contentUri != null) 
       { 
          // Process each notification 
        } 
 } 

   

3. Bearer Token Fetch
string bearerToken = await GetBearerTokenAsync(log); 
if (string.IsNullOrEmpty(bearerToken)) continue; 

 

4. Fetch Audit Content

https://learn.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-reference#retrieve-content 

 var requestMsg = new HttpRequestMessage(HttpMethod.Get, contentUri); 
       requestMsg.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); 
 var result = await httpClient.SendAsync(requestMsg); 

 if (!result.IsSuccessStatusCode) continue; 
 var auditContentJson = await result.Content.ReadAsStringAsync(); 

 

5. Deserialize and Filter Audit Records

https://learn.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-schema#dynamics-365-schema 

 var auditRecords = JsonConvert.DeserializeObject<dynamic[]>(auditContentJson);
 foreach (var eventData in auditRecords) { 
          string orgName = eventData.CrmOrganizationUniqueName ?? ""; 
          string workload = eventData.Workload ?? ""; 
          string entityName = eventData.EntityName ?? ""; 
          string operation = eventData.Message ?? ""; 
     
 if (workload != "Dynamics 365" && workload != "CRM" && workload != "Power Platform") continue; 
 if (!entityOpsFilter.ContainsKey(entityName)) continue; 
 if (!entityOpsFilter[entityName].Contains(operation)) continue; 
   // Store in Cosmos DB 
  } 

  

 6. Store in Cosmos DB
var cosmosDoc = new { 
id = Guid.NewGuid().ToString(),
tenantId = notification.tenantId, 
 raw = eventData 
 }; 

var partitionKey = (string)notification.tenantId; 
var resp = await cosmosContainer.CreateItemAsync(cosmosDoc, new PartitionKey(partitionKey)); 

 

 7. Logging and Error Handling

https://learn.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-reference#errors 

 log.LogInformation($"Stored notification in Cosmos DB for contentUri: {notification.contentUri}, DocumentId: {cosmosDoc.id}"); 
 catch (Exception dbEx) {
 log.LogError($"Error storing notification in Cosmos DB: {dbEx.Message}"); 
  } 
 

  

 🧠 Conclusion 

This solution provides a robust, extensible pattern for exporting Dynamics 365 CE Dataverse org data to Cosmos DB using the Office 365 Management Activity API. Solution architects can use this as a reference for building or evaluating similar integrations, especially when working with third-party archiving or analytics solutions. 

Updated Sep 11, 2025
Version 1.0
No CommentsBe the first to comment