Skip to main content

Microsoft Graph API

Microsoft Azure Pull

Synopsis​

Creates a poller that signs in to Microsoft Entra ID as an app registration and reads a Microsoft 365 tenant through Microsoft Graph, on a configurable interval, and forwards records to downstream pipelines. Director polls the source system's API at configured intervals to retrieve log and telemetry data rather than receiving pushed events. Supports time windows that stay behind the current time, pagination that resumes where it stopped, retry logic, and mutual TLS.

What a Device Reads​

In the console, a device reads one of three things:

  • One resource path of its own, set on the device (see API and Incremental Polling).
  • Its datasets: each dataset reads one Graph resource, on its own interval, through its own pipeline, with a saved position of its own. The dataset types, and what each needs, are on Microsoft Graph API Datasets.
  • Its profiles: the datasets of the profiles assigned to it.

A device made through the API or a deployment template can still read a resource path and datasets together.

Upgrading

Two things change for a device that reads a resource path of its own:

  • Records arrive later. With a timestamp_field, each read now stops delay seconds before the current time, 300 by default. Records reach the pipeline that much later than before.
  • max_pages means something else. It used to be a hard limit, and the records beyond it were lost. Now it is the number of pages read in one poll, and the next poll continues where this one stopped.

Schema​

- id: <numeric>
name: <string>
description: <string>
type: graphapi
tags: <string[]>
pipelines: <pipeline[]>
status: <boolean>
properties:
tenant_id: <string>
client_id: <string>
client_secret: <string>
version: <string>
resource: <string>
query_params: <string>
poll_interval: <numeric>
timeout: <numeric>
max_retries: <numeric>
retry_delay: <numeric>
enable_pagination: <boolean>
max_pages: <numeric>
enable_delta_query: <boolean>
timestamp_field: <string>
delay: <numeric>
initial_lookback: <numeric>
tls:
status: <boolean>
cert_name: <string>
key_name: <string>
passphrase: <string>
ca_name: <string>
min_tls_version: <string>
max_tls_version: <string>
insecure_skip_verify: <boolean>

Configuration​

The following fields are used to define the device:

Device​

FieldRequiredDefaultDescription
idYUnique identifier
nameYDevice name
descriptionN-Optional description
typeYMust be graphapi
tagsN-Optional tags
pipelinesN-Optional pre-processor pipelines
statusNtrueEnable/disable the device

Authentication​

FieldRequiredDefaultDescription
tenant_idYAzure AD tenant ID
client_idYApp registration client ID
client_secretYApp registration client secret

API​

FieldRequiredDefaultDescription
versionNv1.0Graph API version: v1.0 or beta. A custom dataset that names no version reads with this one
resourceNGraph API resource path that the device reads itself (see Allowed Resources). Optional when the device reads datasets. A device with no resource and no dataset reads nothing, and its connection state says so
query_paramsN""OData query parameters (e.g., $select=id,createdDateTime&$top=100)

Polling​

FieldRequiredDefaultDescription
poll_intervalN10Seconds between polls of the device's own resource (must be > 0). Each dataset has its own interval

HTTP​

FieldRequiredDefaultDescription
timeoutN30Request timeout in seconds
max_retriesN3Number of retry attempts on failure
retry_delayN5Seconds to wait between retries

Pagination​

FieldRequiredDefaultDescription
enable_paginationNtrueFollow @odata.nextLink pages automatically. Applies only to the device's own resource
max_pagesN10Maximum pages per poll of the device's own resource (0 = unlimited). A poll that reaches it saves the link to the next page, and the next poll continues from it. Each dataset has its own max_pages

Incremental Polling​

FieldRequiredDefaultDescription
timestamp_fieldN""JSON field name to read the device's own resource by, such as createdDateTime. Each poll then reads one time window (see Incremental Polling). Empty reads the whole resource at every poll
delayN300Seconds before the current time at which each window of the device's own resource ends
initial_lookbackN3600Seconds back that the first window starts. It is also the default of the device's datasets
enable_delta_queryNfalseNot supported: none of the allowed resources has a delta query, so Graph refuses the request. The device logs a warning when it is on. A timestamp_field takes priority over it

TLS​

FieldRequiredDefaultDescription
tls.statusNfalseEnable mutual TLS
tls.cert_nameY*cert.pemClient certificate for mutual TLS
tls.key_nameY*key.pemClient private key for mutual TLS
tls.passphraseN-Passphrase for an encrypted private key
tls.ca_nameN-CA bundle used to verify the server certificate. When unset, the host trust store is used.
tls.min_tls_versionNtls1.2Minimum accepted TLS version (tls1.0, tls1.1, tls1.2, tls1.3)
tls.max_tls_versionN-Maximum accepted TLS version. When unset, the highest mutually supported version is negotiated.
tls.insecure_skip_verifyNfalseSkip server certificate verification. Use only for testing.

* = Required when tls.status is true.

note

TLS material fields (cert_name, key_name, ca_name, client_ca_name) accept any of the following:

  • File name — resolved relative to the service root directory. Nested paths such as certs/prod/server.pem are supported.
  • Absolute path — honored only if it resolves inside the service root. Any path that escapes the root is refused.
  • Inline PEM content — used verbatim when the value contains -----BEGIN.
  • Environment variable — ${ENV_VAR}.
  • Vault reference — $secret{id=...} or $secret{store=...,ref=...}.

Details​

Authentication​

The device uses OAuth2 client credentials flow to obtain an access token from Azure AD. Tokens are cached per device/tenant/client combination and automatically refreshed on authentication failure (HTTP 401/403).

When client_id and client_secret are omitted, the credential provider automatically falls back to Azure Managed Identity, authenticating without explicit credentials when running on an Azure-hosted VM or container with a system-assigned or user-assigned managed identity.

Incremental Polling​

With a timestamp_field, each poll of the device's own resource reads one time window:

  • Each window covers the records whose time field falls between the end of the last window and delay seconds before now. Microsoft adds records to Graph some minutes after they happen, and a window that ended at the poll would miss the ones on their way.
  • When max_pages stops a poll, the next poll continues from the same place, so nothing is skipped.
  • A record at the window's edge is read once. It used to be read twice.
  • The first poll starts initial_lookback seconds back. The cursor merges with any existing $filter in query_params.

Without a timestamp_field, each poll fetches the whole resource.

Delta query. No allowed resource supports a delta query, so enable_delta_query cannot work: Graph refuses the request. Use a timestamp_field instead.

Allowed Resources​

The device enforces an allow list of Graph API resource paths. Requests to unlisted resources are rejected. Sub-paths of allowed resources (e.g., auditLogs/directoryAudits/someId) are also accepted. The same list applies to a custom dataset.

The permissions below are the least-privileged application permissions. Where a resource also needs a licence in the tenant, the licence is named: without it, Graph refuses the request whatever the app's permissions.

Audit Logs​

ResourceRequired PermissionLicence
auditLogs/signInsAuditLog.Read.AllMicrosoft Entra ID P1 or P2
auditLogs/directoryAuditsAuditLog.Read.All
auditLogs/provisioningAuditLog.Read.All and Directory.Read.All

Security​

ResourceRequired PermissionLicence
security/alerts_v2SecurityAlert.Read.All
security/incidentsSecurityIncident.Read.All
security/secureScoresSecurityEvents.Read.All
security/secureScoreControlProfilesSecurityEvents.Read.All
security/attackSimulationAttackSimulation.Read.AllMicrosoft Defender for Office 365 Plan 2
security/cases/ediscoveryCaseseDiscovery.Read.AllAn eDiscovery licence
security/threatIntelligence/articlesThreatIntelligence.Read.AllMicrosoft Defender XDR or Microsoft Sentinel

Identity Protection​

ResourceRequired PermissionLicence
identityProtection/riskDetectionsIdentityRiskEvent.Read.AllMicrosoft Entra ID P1 or P2
identityProtection/riskyUsersIdentityRiskyUser.Read.AllMicrosoft Entra ID P2
identityProtection/riskyServicePrincipalsIdentityRiskyServicePrincipal.Read.AllMicrosoft Entra Workload ID Premium
identityProtection/servicePrincipalRiskDetectionsIdentityRiskEvent.Read.AllMicrosoft Entra Workload ID Premium

Reports​

ResourceRequired PermissionLicence
reports/authenticationMethods/userRegistrationDetailsAuditLog.Read.AllMicrosoft Entra ID P1 or P2

The Microsoft 365 usage reports (reports/getEmailActivityUserDetail, reports/getEmailActivityCounts, reports/getEmailAppUsageUserDetail, reports/getMailboxUsageDetail, reports/getOffice365ActivationsUserDetail, reports/getOffice365ActiveUserDetail, reports/getOffice365GroupsActivityDetail, reports/getOneDriveActivityUserDetail, reports/getOneDriveUsageAccountDetail, reports/getSharePointActivityUserDetail, reports/getSharePointSiteUsageDetail, reports/getTeamsUserActivityUserDetail, reports/getTeamsDeviceUsageUserDetail and reports/getYammerActivityUserDetail) are on the allow list, but they cannot be read as a device's own resource. They need a period or date parameter, and on v1.0 they return a CSV file. Read them with the usage report datasets on Microsoft Graph API Datasets. They need Reports.Read.All.

Service Announcements​

ResourceRequired PermissionLicence
admin/serviceAnnouncement/issuesServiceHealth.Read.All
admin/serviceAnnouncement/messagesServiceMessage.Read.All
admin/serviceAnnouncement/healthOverviewsServiceHealth.Read.All

Exchange Message Trace​

Message trace is read with the message trace dataset, from admin/exchange/tracing/messageTraces. It needs ExchangeMessageTrace.Read.All and a one-time setup step in the tenant. See Microsoft Graph API Datasets.

Resources That Cannot Be Read​

These entries stay on the allow list, so that a configuration naming one still loads. They cannot be read, and a device whose own resource is one of them logs why:

ResourceWhy
security/alertsMicrosoft retired it on 2026-10-15. Use security/alerts_v2.
security/tiIndicatorsBeta only and deprecated. Microsoft scheduled its removal for April 2026.
security/cloudAppSecurityProfilesGraph documents no such API.
security/threatIntelligence/hostsGraph cannot list it. You can only fetch one host by name or IP address.
reports/credentialUserRegistrationDetailsIt stopped returning data on 2024-06-30. Use authentication method registration (reports/authenticationMethods/userRegistrationDetails).
reports/userCredentialUsageDetailsIt stopped returning data on 2025-08-01.
reports/applicationSignInDetailedSummaryGraph does not allow application permissions for it.
reports/messageTraceNo such Graph path exists. The message trace dataset reads admin/exchange/tracing/messageTraces.

Required Permissions​

To cover all readable resources and datasets, add the following Application permissions to the Azure App Registration and grant admin consent:

  • AuditLog.Read.All
  • Directory.Read.All
  • SecurityAlert.Read.All
  • SecurityIncident.Read.All
  • SecurityEvents.Read.All
  • AttackSimulation.Read.All
  • eDiscovery.Read.All
  • ThreatIntelligence.Read.All
  • IdentityRiskEvent.Read.All
  • IdentityRiskyUser.Read.All
  • IdentityRiskyServicePrincipal.Read.All
  • Reports.Read.All
  • ServiceHealth.Read.All
  • ServiceMessage.Read.All
  • ExchangeMessageTrace.Read.All

All permissions require admin consent. After adding them in the Azure portal, click Grant admin consent for [your tenant] to activate them. Only add permissions for the resources you intend to poll.

Console​

In the console, go to Fleet Management > Devices > Microsoft Graph API and click Add new device. The wizard's steps are:

  1. General settings, as on every device.
  2. API settings: under Authentication, Managed identity, or Tenant ID, Client ID and Client secret; under API, API version (v1.0). A custom dataset that sets no version reads with this one.
  3. Advanced settings: under HTTP, Timeout (Optional) (30), Maximum retries (Optional) (3) and Retry delay (Optional) (5); then TLS settings, with the Mutual TLS switch and, while it is on, Certificate, Private key and Skip certificate verification.
  4. Data collection: what the device reads, one of three tiles. A new device starts on Datasets. A clone of a device with a resource path starts on Resource path.
    • Resource path: Resource path (required here) and Query parameters (Optional); under Polling, Poll interval (Optional) (10); under Pagination, Automatic pagination and Maximum pages (Optional) (10); under Incremental polling, Timestamp field (Optional) and Delay (Optional) (300). Delta query shows only on a device saved with it on, with a warning to turn it off.
    • Datasets or Profiles: the datasets or profiles assigned to the device. The device then sends no resource, query_params, poll_interval, timestamp_field, delay, enable_delta_query, enable_pagination or max_pages. A Director older than 2.6.1 refuses such a device, and the step warns as soon as one of these tiles is chosen.
  5. Review and create.

The device's page has API settings, Advanced settings and Data collection tabs. Changing what the device reads on Data collection takes two saves: the console first adds what the device reads now, then removes what it read before. If the second save fails, the device reads both until you save again, and the message says which half failed. When the API or a deployment template set a resource path beside datasets or profiles, the tab shows This device also reads a resource path, and saving the tab removes that resource path. Saving the other tabs keeps it.

Examples​

Basic​

Polling sign-in logs with a timestamp cursor for incremental collection...

devices:
- id: 1
name: graphapi_signins
type: graphapi
properties:
tenant_id: "00000000-0000-0000-0000-000000000000"
client_id: "11111111-1111-1111-1111-111111111111"
client_secret: "your-client-secret"
resource: "auditLogs/signIns"
poll_interval: 30
timestamp_field: "createdDateTime"

Datasets​

Reading security alerts and directory audits as datasets, with no resource of the device's own. Each dataset reads on its own interval, and the alerts go through a pipeline of their own...

devices:
- id: 2
name: graphapi_datasets
type: graphapi
properties:
tenant_id: "00000000-0000-0000-0000-000000000000"
client_id: "11111111-1111-1111-1111-111111111111"
client_secret: "your-client-secret"
definitions:
- name: graphapi_alert_collector
properties:
pipeline_name: alert_enricher
- name: graphapi_directory_audit_collector
properties:
interval: 120

Security Alerts​

Collecting security alerts with OData filtering and preprocessing pipelines...

devices:
- id: 3
name: graphapi_alerts
type: graphapi
pipelines:
- alert_enricher
- severity_classifier
properties:
tenant_id: "00000000-0000-0000-0000-000000000000"
client_id: "11111111-1111-1111-1111-111111111111"
client_secret: "your-client-secret"
version: "v1.0"
resource: "security/incidents"
query_params: "$filter=severity eq 'high'"
poll_interval: 15
timestamp_field: "createdDateTime"

High-Volume​

Optimizing for high data volumes with a longer timeout and more pages per poll...

devices:
- id: 4
name: graphapi_audits
type: graphapi
properties:
tenant_id: "00000000-0000-0000-0000-000000000000"
client_id: "11111111-1111-1111-1111-111111111111"
client_secret: "your-client-secret"
resource: "auditLogs/directoryAudits"
timestamp_field: "activityDateTime"
poll_interval: 300
timeout: 60
max_retries: 5
max_pages: 50

Secure Connection​

Enabling mutual TLS for outbound Graph API requests...

devices:
- id: 5
name: graphapi_secure
type: graphapi
properties:
tenant_id: "00000000-0000-0000-0000-000000000000"
client_id: "11111111-1111-1111-1111-111111111111"
client_secret: "your-client-secret"
resource: "identityProtection/riskDetections"
poll_interval: 60
timestamp_field: "activityDateTime"
tls:
status: true
cert_name: "graphapi.crt"
key_name: "graphapi.key"