Skip to main content

Entra ID (AzureAD) Audit via Graph API

The Azure Directory Audit template polls the Microsoft Entra ID (formerly Azure AD) directory audit and sign-in logs from Microsoft Graph: who changed users, groups, roles and application grants in the directory, and who signed in from where.

You never open the Microsoft portal: you enter an administrator's email address, and Microsoft asks that administrator to approve FluencyAzureADAudit, an application Fluency already operates, so there is no app registration and no client secret. The person who finishes the setup, a Global Administrator or Privileged Role Administrator, may not be you, and until they approve, the application reports Running and collects nothing.

The setup has two halves, and the Fluency half comes first:

  1. In Fluency: install the Azure Directory Audit template with an administrator's email address. This sends the approval request.
  2. In Microsoft: that administrator reviews the permissions and accepts.
Two templates collect the Entra ID audit and sign-in logs: this page covers the Graph API one
TemplateHow the logs leave Entra IDWhat your tenant grantsGuide
Azure Directory Audit (this page)Fluency polls Microsoft GraphTenant-wide AuditLog.Read.All and Directory.Read.All to a Fluency applicationThis page
Azure Directory Audit (Event Hub)A diagnostic setting pushes them into an event hub in your Azure subscriptionA Listen-only connection string to that one event hubEntra ID (AzureAD) Audit via Event Hubs

Both collect essentially the same records and file them in the same shape in the same index, so run one or the other; Graph API or Event Hubs sets out the differences.

What arrives​

StreamWhat it holdsLands in
Directory auditDirectory changes: users and groups created or modified, role assignments, application and consent grants, password resets, PIM activity.The MSEntra and AzureAuditLogs indexes
Sign-insInteractive sign-ins, with client IP, device, application, Conditional Access result and risk state.The MSEntra and AzureSigninLogs indexes

Sign-ins need Microsoft Entra ID P1 or P2; see Before you begin. Fluency reads only what Entra still holds, so collection that stops for longer than Entra's retention (seven days on Free, 30 on P1 or P2) loses data for good; see Retention decides how long an outage can last. Non-interactive, service principal and managed identity sign-ins are not collected; the Event Hub route collects them, as Graph API or Event Hubs shows.

Endpoints and permissions, the indexes, the record shape and the legacy AzureAudit index name are in Data reference.

Before you begin​

A Global Administrator or Privileged Role Administrator to approve the request. Microsoft's rule is:

Privileged Role Administrator, for granting consent for apps requesting any permission, for any API.

Cloud Application Administrator, AI Administrator, or Application Administrator, for granting consent for apps requesting any permission for any API, except Microsoft Graph app roles (application permissions).

This application requests AuditLog.Read.All as a Microsoft Graph application permission, so it falls in the exception: Cloud Application Administrator and Application Administrator are not sufficient.

Microsoft Entra ID P1 or P2, for sign-ins only. The directory audit works on every tier, including Free. Without P1 or P2, the sign-in endpoint returns

Authentication_RequestFromNonPremiumTenantOrB2CTenant:
Neither tenant is B2C or tenant doesn't have premium license

and the rest of the integration is unaffected.

An email address that will get the consent link to the approver. It does not have to be the administrator's own; see the note under Install the Application.

Access to the Fluency portal with permission to install applications.

Part 1: Fluency​

Install the Application​

In the navigation rail, open Applications and click Install Application, then choose Azure Directory Audit from the Office Software (SaaS) and IAM category of the Application Catalog.

ParameterNotes
Enable Admin Consent Mode?Checkbox, ticked by default. Leave it ticked for the consent route. Unticking it changes the form; see If your tenant will not allow the consent route.
admin email addressWhere the consent link is sent. Any valid address (see the note below).
datalakePre-filled managed.
datalake index namePre-filled MSEntra, the index of every record; older deployments use the legacy name AzureAudit. Keep it: the Entra ID search facets are set on MSEntra by name. Two applications writing to the same index name in the same lake will collide, and the second one aborts.
This template and Office365-Audit collect the same feed

Office365-Audit collects this directory audit and these sign-ins too, from the same Graph endpoints, and its install creates the same table indexes. Both also write the directory audit to an index that defaults to MSEntra, this template's datalake index name and Office365-Audit's datalake index name for Entra ID (AzureAudit) events, as does Entra ID (AzureAD) Audit via Event Hubs, and two applications writing to the same index name in the same lake will collide, and the second one aborts. Run one or the other, not both.

The email address only delivers the link

The admin email address can be any valid email address. It is used only to deliver the consent link and plays no part in authentication. Whoever opens the link signs in to Microsoft with the actual administrator account, which can be a different address entirely. The Global Administrator or Privileged Role Administrator requirement applies to the account that signs in, not to the address the link was sent to.

datalake and datalake index name sit in the collapsed Advanced configuration section below Configuration; click its heading to reach them.

Install Azure Directory Audit form with the Application section's Instance name and Display name fields, the Configuration section's Enable Admin Consent Mode? and admin email address fields, and Advanced configuration expanded to datalake and datalake index name, above Cancel, Dry run and Install Application

While Enable Admin Consent Mode? is ticked, Dry run is greyed out: there is no credential of yours for it to check. Click Install Application. The Applications page opens with the notice Default is launching. and lists the new application with its status badge.

Running does not mean working; it means waiting

The badge reflects the pipeline, not the consent. Until the administrator approves in Microsoft, this application has nothing to read, and it looks identical to one that is collecting normally. The check that answers the question is on the Microsoft side; see Confirm it is running.

If your organisation blocks consent to third-party applications, untick Enable Admin Consent Mode; the form replaces the email field with Azure Tenant ID, Azure ClientID and Azure ClientSecret, for an app registration you create and hold yourself. Build it as described in Part 1 of the Office 365 Audit page, granting AuditLog.Read.All and Directory.Read.All as Application permissions on Microsoft Graph.

A tenant that blocks third-party consent can also collect the same logs through Entra ID (AzureAD) Audit via Event Hubs, which needs no application in the tenant at all.

Part 2: Microsoft​

This half is performed by the administrator who opens the consent link.

Review the request and accept​

Opening the link takes the administrator to login.microsoftonline.com, where they sign in with their administrator account and are shown the permission request. If nothing arrives, check spam quarantine first; reinstalling does not change how the request is delivered.

Microsoft consent prompt headed Permissions requested, Review for your organization, for the application FluencyAzureADAudit published by Fluency Corp. with a verified publisher badge, listing three permissions

Consent screen wordingPermissionWhat it buys
Sign in and read user profileUser.Read (delegated)Identifies the signing-in administrator during the consent itself. Not used for collection.
Read all audit log dataAuditLog.Read.AllThe directory audit and sign-in endpoints.
Read directory dataDirectory.Read.AllResolves the object GUIDs in audit records to names, and lets the sign-in endpoint read the tenant's licensing.

Three checks before accepting:

  1. The application is FluencyAzureADAudit and the publisher is Fluency Corp. If either is different, stop.
  2. The list is exactly these three. A consent screen asking for more is not this integration.
  3. The screen says Review for your organization; that is the tenant-wide grant.

Click Accept. If the administrator clicks Cancel, Microsoft returns AADSTS65004: The resource owner or authorization server denied the request., no grant is created, and nothing on the Fluency side reports it.

Once accepted, the grant lives on the tenant rather than on the administrator who approved it. It does not expire, it survives the approver leaving, and it can be revoked by anyone with the role from Enterprise applications without Fluency being told.

Confirm it is running​

In the Microsoft Entra admin center, go to Entra ID → Enterprise apps → All applications and search for FluencyAzureADAudit. If consent was granted, the application is there; if it is not, the request was never accepted.

Enterprise application entry for FluencyAzureADAudit showing the Properties page, its Homepage URL of fluencysecurity.com, and a Delete button in the command bar

Security → Permissions → Admin consent shows exactly what was granted. Its Type column separates Delegated from Application, which matters because Read directory data is worded identically for both.

Then check that events are landing: search the MSEntra index (managed-MSEntra in the Data lake index drop-down) for @eventType:AuditLogs, or, on an older deployment, the AzureAudit index. See Confirm data is arriving. The directory audit only records changes, so on a quiet tenant make a harmless change in Entra (create a group, then delete it) and search for it a few minutes later.

Graph API or Event Hubs​

Both templates collect the Entra directory audit and interactive sign-ins and file them identically: the same shape in MSEntra, and the same five table indexes (AzureAuditLogs, AzureSigninLogs and the three AAD…SignInLogs). The same search facets and Entra ID detections work on either. Running both stores every record twice and raises every Entra ID detection twice, so run one or the other. They differ in what each asks of your tenant and in a few categories at the edges:

Azure Directory Audit (Graph API)Azure Directory Audit (Event Hub)
Who sets it upA Global Administrator or Privileged Role Administrator, approving a consent requestContributor on an Azure subscription, then a Security Administrator for the diagnostic setting
What Fluency holdsA tenant-wide Graph grantA Listen-only connection string to one event hub
Entra categoriesDirectory audit and interactive sign-insThe same, plus non-interactive, service principal and managed identity sign-ins and provisioning; not the risk categories
@source on each recordAzureAuditAzureEventHub
How long collection can stop without lossEntra's retention: seven days on Free, 30 on P1 or P2The hub's retention: one day on Basic, up to seven on Standard
Azure costNoneThe Event Hubs namespace, billed hourly

The Graph API route has nothing to build or pay for in Azure and rides out a longer outage on a P1 or P2 tenant. The Event Hub route puts no application in your directory and adds the sign-in categories this template does not collect; with non-interactive sign-ins, the stolen-session detections also see the token refreshes a replayed session makes.

Maintenance​

There is no credential to rotate​

No secret expires on the consent route. What can break the integration is someone revoking the consent, so the maintenance task is review rather than rotation.

Reviewing and revoking the grant​

From Entra ID → Enterprise apps → All applications → FluencyAzureADAudit → Permissions → Admin consent, use the ... control on a permission row and select Revoke permission.

The Admin consent tab of an enterprise application's Permissions page, with the Revoke Permission control open on a permission row

  • Nothing tells Fluency. Collection stops and the application keeps reporting Running. On a Free tenant that silence costs a week of data before anyone is likely to notice.
  • Revoking does not stop the application being re-consented. Microsoft notes that "Revoking the current granted permission doesn't stop users from re-consenting to the application's requested permissions." To block it permanently, delete the enterprise application or restrict consent at the tenant level.

Removing the integration​

Uninstall the application in Fluency first, with the Uninstall button at the top right of its details page (v1: the red Uninstall button at the foot of its details panel on Platform → Applications), then delete the enterprise application in Entra ID (Properties → Delete) or revoke its permissions as above.

Troubleshooting​

SymptomLikely causeFix
The application shows Error (v1: Aborted) immediately after installAn index name is already taken: usually MSEntra from Office365-Audit or Entra ID (AzureAD) Audit via Event Hubs (AzureAudit on an older deployment)The error message in the application's details names the index. Either application already collects this feed; keep one of the two.
The administrator sees Need admin approval rather than a consent screenAADSTS90094: the account cannot consent to Microsoft Graph application permissionsHave a Global Administrator or Privileged Role Administrator open the same link.
Consent fails with AADSTS65004The administrator selected CancelSend the request again. No grant was created.
The administrator never received anythingWrong address, or the message was quarantinedCheck the address on the install form, then spam quarantine.
Running, but the index is empty and has always been emptyConsent was never grantedSearch Enterprise applications for FluencyAzureADAudit. If it is absent, the request was not accepted; resend it.
Running, consent is granted, index still emptyThe tenant may simply be quietMake a harmless change in Entra and search for it.
Directory audit records arrive, sign-in records never doThe tenant has no Entra ID P1 or P2 licenceExpected on Free. Neither tenant is B2C or tenant doesn't have premium license confirms it.
Sign-in records arrive but appliedConditionalAccessPolicies is emptyPolicy.Read.All is not grantedThe consent route does not request it. On the client-secret route, add it to your registration.
The premium-licence error appears intermittently rather than alwaysDirectory.Read.All missing or uncachedMicrosoft notes that permission is what retrieves tenant licensing information. Confirm both permissions are on the Admin consent tab.
Collection worked, then stopped, with no change on the Fluency sideThe grant was revoked, or the application was deletedCheck Enterprise applications → Permissions → Admin consent. Re-consent if it is gone.
A gap in the data that will not backfillThe events aged out of EntraUnrecoverable. Seven days on Free, 30 on P1 or P2.

Data reference​

What the two streams look like once they land, for searches and detection rules written against them.

Streams, endpoints and permissions​

StreamEndpointPermissionContents
Directory auditGET /v1.0/auditLogs/directoryAuditsAuditLog.Read.AllDirectory changes: users and groups created or modified, role assignments, application and consent grants, password resets, PIM activity.
Sign-insGET /v1.0/auditLogs/signInsAuditLog.Read.AllInteractive sign-ins with client IP, device, application, Conditional Access result and risk state. Requires Entra ID P1 or P2; see Before you begin.

Where the records land​

Both streams land in three indexes:

IndexContentsNamed by
MSEntraEvery record, both streams, in the shape described below.The form's datalake index name
AzureAuditLogsThe directory audit, in the AzureAuditLogs schema.The template
AzureSigninLogsThe sign-ins, in the AzureSigninLogs schema.The template

The install also creates AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs and AADManagedIdentitySignInLogs, the tables for the other sign-in types, which stay empty: this template collects interactive sign-ins only. The five table indexes are created by the install and are not exposed as parameters, so they cannot be renamed to dodge a collision.

AzureAudit is the legacy name of the form's index: older deployments write every record to AzureAudit rather than MSEntra, and keep doing so.

The record shape​

Each record in MSEntra has the shape a Microsoft Entra diagnostic setting exports: Graph's sign-in or audit object, unchanged, under @azure.properties, inside the Azure Monitor envelope (time, category, operationName, resultType and the rest). @eventType holds the category (AuditLogs for the directory audit, SignInLogs for an interactive sign-in) and @source is AzureAudit. A few envelope values that Graph does not return are absent.

That is the shape the Event Hub template writes to the same index, so the Default-EntraID_AuditLogs and Default-EntraID_SignIn search facets and the Entra ID detection rules work the same on records from either route.

Retention decides how long an outage can last​

ReportEntra ID FreeEntra ID P1Entra ID P2
Audit logsSeven days30 days30 days
Sign-insSeven days30 days30 days

This template reads what Entra still holds, so Entra's retention is how long collection can stop before data is lost for good. Seven days on a Free tenant is the number that catches people: an outage over a long weekend, unnoticed until Tuesday, is close to the limit. Microsoft is explicit that there is no recovery path: "Log retention changes aren't retroactive", and "Data that has already expired can't be recovered unless it was previously archived." Upgrading the licence afterwards does not help either: "When you switch from a free to a premium version, you can only see up to 7 days of data."

Under the hood​

Engineering reference: the consent flow, the Graph endpoints and the install

The application Fluency operates is registered as multi-tenant in Fluency's own directory. Approval goes through Microsoft's admin consent endpoint:

https://login.microsoftonline.com/{tenant}/v2.0/adminconsent
?client_id={fluency-client-id}
&scope=https://graph.microsoft.com/.default
&redirect_uri={fluency-callback}
&state={opaque}

Approval produces a redirect carrying admin_consent=True and the tenant GUID, which is why the form never asks for a tenant ID.

Collection then uses the client credentials grant against https://graph.microsoft.com/.default. Application permissions produce an appRoleAssignment on the service principal, and delegated permissions consented tenant-wide produce an oauth2PermissionGrant with consentType: AllPrincipals. Neither references the approving user's account, which is why the grant outlives them.

The Graph endpoints​

Both streams are ordinary Graph collections, polled with a timestamp filter and paginated through @odata.nextLink:

GET https://graph.microsoft.com/v1.0/auditLogs/directoryAudits
GET https://graph.microsoft.com/v1.0/auditLogs/signIns

directoryAudits filters on activityDateTime; signIns filters on createdDateTime. appliedConditionalAccessPolicies is populated on sign-in records only when the caller also holds Policy.Read.All, which this application does not request.

What the install creates​

Two pipes run on the application's router. The first, AzureAudit_Transform, rewrites each sign-in into the schema of the table for its sign-in type (AzureSigninLogs for an interactive one) and each directory audit record into the AzureAuditLogs schema, writes it to the index of the same name, and passes the original record on. The second, AzureAudit_EntraAdjustments, wraps a copy of each record in the Azure Monitor envelope, sets @eventType to its category (a sign-in's from its signInEventTypes) and writes it to the index named on the form, then hands it to the Entra ID detection rules for that category. The application's Actions list records one line per object created; addDatalakeIndex entries create the indexes in the managed lake as a side effect of the install, which is why a name that already exists aborts the whole application.

References​

Microsoft

Fluency