Microsoft 365 integration (Teams + Outlook + Entra ID + SharePoint)
ActivityLog ingests Microsoft 365 activity through a single Azure Function App named ActivityLog-O365, which pulls from the Office 365 Management Activity API and Microsoft Graph CallRecords and routes events to ActivityLog systems by workload.
| Workload | ActivityLog system | Source |
|---|---|---|
| Teams (channel messages, calls, meetings) | teams (or whatever you name it) |
O365 Mgmt API Audit.General + Graph CallRecords |
| Outlook / Exchange (email, calendar) | outlook |
O365 Mgmt API Audit.Exchange |
| Entra ID (sign-ins, directory audit) | entra |
O365 Mgmt API Audit.AzureActiveDirectory |
| SharePoint + OneDrive (file activity) | sharepoint |
O365 Mgmt API Audit.SharePoint |
One Function App, four token routes. This is the architecture today — earlier drafts of the integration specs had separate Function Apps per workload; that's been consolidated.
Where the canonical deployment docs live
This guide is the user-facing overview. The actual deploy steps — Azure resource provisioning, Entra app reg, Key Vault secrets, app settings — are maintained in the ActivityLog-Integrations repo as operator runbooks. Use these as your source of truth:
| Doc | What it covers |
|---|---|
../../../ActivityLog-Integrations/Teams/DEPLOY.md |
The canonical deploy spec. Hosting layout, app permissions, KV secrets, app settings, deploy pipeline, adding new content types. |
../../../ActivityLog-Integrations/Azure/Teams-FunctionApp-Handoff.md |
Historical provisioning record (Teams-only era). Useful when standing up the stack in a new Azure tenant. |
../../../ActivityLog-Integrations/Azure/Outlook-FunctionApp-Handoff.md |
Historical Outlook-only handoff. Now superseded by the consolidated Function App. |
../../../ActivityLog-Integrations/Common-Architecture.md |
Auth contract, storage modes, retention defaults, idempotency schema across all integrations. |
Workload-specific message catalogues:
| Doc | What it covers |
|---|---|
../../../ActivityLog-Integrations/Teams/SPEC.md |
Teams message types, mapping rules, attribute schema |
../../../ActivityLog-Integrations/Outlook/SPEC.md |
Email + calendar event mapping |
../../../ActivityLog-Integrations/EntraID/SPEC.md |
Sign-in + directory-audit mapping |
../../../ActivityLog-Integrations/SharePoint/SPEC.md |
File-activity event mapping |
The summary below describes what you'll see in the ActivityLog portal once the Function App is running — read it first, then go to the deploy doc for the actual stand-up.
High-level architecture
┌──────────────────────────────────────────────┐
│ Microsoft 365 tenant (your org) │
│ ├── Teams │
│ ├── Exchange / Outlook │
│ ├── Entra ID │
│ └── SharePoint / OneDrive │
└────────────────────┬─────────────────────────┘
│ O365 Mgmt Activity API
│ (`Audit.General`, `Audit.Exchange`,
│ `Audit.AzureActiveDirectory`, `Audit.SharePoint`)
▼
┌──────────────────────────────┐
│ ActivityLog-O365 │
│ (Azure Function App, Y1) │
│ - System-assigned MI │
│ - One Entra app reg │
│ - Three Graph permissions │
│ + one O365 Mgmt perm │
└──────┬────────────┬──────────┘
│ │
│ │ Microsoft Graph
│ │ (CallRecords.Read.All)
│ ▼
│ Teams call detail
▼
Route by `Workload` field
├── MicrosoftTeams ──▶ POST /messages (teams token)
├── Exchange ──▶ POST /messages (outlook token)
├── AzureActiveDirectory ──▶ POST /messages (entra token)
└── SharePoint ──▶ POST /messages (sharepoint token)
│
▼
ActivityLog API
One client credential authenticates against two resources: the O365 Mgmt API (for audit content) and Graph (for call records). The Function App routes each event to the right ActivityLog system based on the audit event's Workload field.
What you need to set up
You provision four things in ActivityLog before the Function App can start ingesting:
- Four systems, one per workload, with whatever names you prefer (e.g.
teams,outlook,entra,sharepoint). - One token per system — five-year retention recommended (compliance-shaped events). See
20-setup-system-tokens.md. - One Entra app registration in your M365 tenant, with the three Graph permissions + one O365 Mgmt API permission listed below.
- One Function App (
ActivityLog-O365) wired up per the canonical deploy doc.
The Fyin-managed staging tenant has a pre-built version of this Function App that ingests Fyin's own M365 activity. If you're a customer evaluating, the easiest path is to mirror that setup in your own Azure subscription using the deploy doc as the template.
Required Entra app registration permissions
In your M365 tenant, create an Entra app registration with application permissions (not delegated):
| Permission | Resource | Why |
|---|---|---|
ActivityFeed.Read |
Office 365 Management APIs | Subscribe + pull from all four audit content types |
CallRecords.Read.All |
Microsoft Graph | Teams call detail (per-participant session start/end) — not in the audit feed |
User.Read.All |
Microsoft Graph | Resolve user IDs ↔ UPN/displayName for actor mapping |
Tenant admin consent is required for all three. Once consented, the same client credential works against both resources (the access token is requested per-resource at runtime).
Earlier provisioning runs granted
ChannelMessage.Read.All,Chat.Read.All,OnlineMeetingArtifact.Read.All,Reports.Read.All,Files.Read.All. These are redundant under the Mgmt API model and have been removed. If you're following an older handoff doc that lists them, ignore those entries — see../../../ActivityLog-Integrations/Azure/Teams-FunctionApp-Handoff.mdfor the historical note.
Workload coverage
Teams (Workload = MicrosoftTeams)
Routed to the teams token. Storage mode: lean metadata (the O365 audit feed scrubs body content, so we have nothing to ingest as body).
Event types you'll see:
| Type | Meaning |
|---|---|
teams.message.created |
A channel or chat message was posted (metadata only) |
teams.meeting.created / .joined / .left / .ended |
Meeting lifecycle |
teams.call.started / .ended |
Call lifecycle (from CallRecords, joined to audit) |
teams.user.added / .removed |
Team/channel membership changes |
Full catalogue: ../../../ActivityLog-Integrations/Teams/SPEC.md.
Outlook (Workload = Exchange)
Routed to the outlook token. Storage mode: lean metadata (body content scrubbed by audit).
Event types:
| Type | Meaning |
|---|---|
outlook.email.sent / .received |
Send and receive (with subject line in metadata, never body) |
outlook.calendar.event.created / .updated / .cancelled |
Calendar lifecycle |
outlook.meeting.accepted / .declined |
Meeting RSVP |
Full catalogue: ../../../ActivityLog-Integrations/Outlook/SPEC.md.
Entra ID (Workload = AzureActiveDirectory)
Routed to the entra token. Storage mode: lean metadata. Two downstream streams:
| Stream | Content |
|---|---|
signins |
Interactive user sign-ins, success and failure |
directory |
Directory-audit events: user added, group changed, role assigned |
Event types:
| Type | Meaning |
|---|---|
entra.signin.success / .failure |
Sign-in attempts (IP, app, MFA result in metadata) |
entra.user.added / .deleted / .modified |
User lifecycle |
entra.role.assigned / .removed |
Role changes |
entra.group.member.added / .removed |
Group membership |
Full catalogue: ../../../ActivityLog-Integrations/EntraID/SPEC.md.
SharePoint + OneDrive (Workload = SharePoint or OneDrive)
Routed to the sharepoint token. Storage mode: lean metadata (file names + paths in metadata, never file content).
Event types (whitelist — others are dropped by default):
| Type | Meaning |
|---|---|
sharepoint.file.viewed / onedrive.file.viewed |
File opened |
sharepoint.file.edited / onedrive.file.edited |
File edited |
sharepoint.file.uploaded / onedrive.file.uploaded |
File added |
sharepoint.file.downloaded / onedrive.file.downloaded |
File retrieved |
sharepoint.file.deleted / onedrive.file.deleted |
File removed |
sharepoint.file.shared / onedrive.file.shared |
File shared |
Weak-signal operations (FolderCreated, FileMoved, FileCopied, PageViewed, SearchQueryPerformed) are blocked by default to keep volume reasonable.
Full catalogue: ../../../ActivityLog-Integrations/SharePoint/SPEC.md.
Operational characteristics
| Concern | Behavior |
|---|---|
| Latency | 30–90 minutes — this is the O365 Mgmt API's publication lag, not anything ActivityLog adds. The Mgmt API publishes audit events in batches on a schedule. |
| Idempotency | Each event has an audit Id from Microsoft. The Function App uses it as sourceEventId — retries collapse safely. |
| Backfill on first run | On first start, the Function App pulls the last 7 days from the Mgmt API. Older history requires a separate REST-API backfill (not built in). |
| Watermark | Persisted in Azure Table Storage (activitylogfunctions storage account, o365_watermark partition). If the Function App restarts, it resumes where it left off. |
| Per-workload disable | App-setting toggle per workload. If you don't have a SharePoint use case, leave it off and save the pull. |
Retention recommendation
All four M365 workloads ship with 1825-day (5-year) retention on their tokens by default. This is the typical security-audit window and matches the Common Architecture default for activity-event class sources. Override per token if your compliance posture differs.
Cost (Azure side)
| Resource | Tier | Estimated monthly cost | ||
|---|---|---|---|---|
| Function App (Linux Consumption / Y1) | Y1 | $0 base, $0 from executions (well under the 1M/month free grant) | ||
| Storage account (shared across ActivityLog Function Apps) | Standard LRS | $1–3 | ||
| Entra app registration | n/a | $0 | ||
| Key Vault ops | Standard | <$0.01 | ||
| Total | **$1–3 per month** |
Application Insights is deliberately not provisioned on the Function App — the operational ceiling vs the value at this workload is poor. App-Insights-less debugging is via the deploy doc's logging guidance.
Status today
| Workload | Status |
|---|---|
| Teams | Live on Fyin's internal tenant; ready to provision for customers |
| Outlook | Ready to implement — same Function App; one more content-type subscription + one new token |
| Entra ID | Ready to implement — same as Outlook |
| SharePoint | Ready to implement — same as Outlook |
The Teams workload is the proven path; the other three plug into the same Function App via the "Adding a new O365 content type" playbook in the deploy spec.
What's next
| Goal | Doc |
|---|---|
| Stand up the Function App | ../../../ActivityLog-Integrations/Teams/DEPLOY.md |
| Provision the deploy pipeline | ../../../ActivityLog-Integrations/Azure/Pipeline-Setup.md |
| Understand event schema and idempotency | ../../../ActivityLog-Integrations/Common-Architecture.md |
| Mint the four tokens | 20-setup-system-tokens.md |
| Query the events back | 60-query-api.md |