Identity operations
Graph + Event Grid for lifecycle events
Receiving tenant-wide identity change notifications at scale with Microsoft Graph and Event Grid partner topics, including the renewal events that people forget.
Problem
Microsoft Graph change notifications tell your system when something changes, for example a user is created, updated or deleted. The default delivery method is a webhook, and webhooks are brittle at scale. Endpoints time out, retries pile up, cold starts add latency, and a missed lifecycle event means a subscription quietly expires and you stop hearing about changes. Event Grid partner topics replace the webhook with managed delivery, retries, dead-lettering and fan-out to serverless handlers.
Options and trade-offs
- Direct webhook to a Function or web app: the simplest to build. You own scaling, retry behavior and response-time limits, and a slow validation response can lose the subscription.
- Event Grid partner topic: Graph delivers to a topic in your Azure subscription, and you attach event subscriptions to route the events. There is more setup, but delivery, retry and dead-lettering are handled for you.
- Event Hubs: the right choice for very high-volume streams where you want partitioned, replayable consumption rather than per-event routing.
My recommendation
Use an Event Grid partner topic for anything that must not lose events: identity lifecycle changes that drive access removal or deprovisioning. Subscribe to lifecycle notifications as well as change notifications, and treat renewal as part of the design, not an afterthought.
Minimal working example
Step 1: create the Graph subscription. The notification URL points at Event Grid instead of a web address:
POST https://graph.microsoft.com/v1.0/subscriptions
Content-Type: application/json
Authorization: Bearer <token>
{
"changeType": "created,updated,deleted",
"notificationUrl": "EventGrid:?azuresubscriptionid=<subscription-id>&resourcegroup=<resource-group>&partnertopic=<topic-name>&location=<region>",
"lifecycleNotificationUrl": "EventGrid:?azuresubscriptionid=<subscription-id>&resourcegroup=<resource-group>&partnertopic=<topic-name>&location=<region>",
"resource": "users",
"expirationDateTime": "2026-12-01T00:00:00Z",
"clientState": "<random-secret-value>"
}
Step 2: activate the partner topic that Graph created in your resource group, then route it:
az eventgrid partner topic activate \
--name <topic-name> \
--resource-group <resource-group>
Then add an event subscription on the topic that sends events to an Azure Function (portal or CLI).
Step 3: handle events asynchronously. The function should do as little as possible before returning:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using System.Text.Json;
public class OnGraphEvent
{
private readonly ILogger<OnGraphEvent> _log;
public OnGraphEvent(ILogger<OnGraphEvent> log) => _log = log;
[Function("OnGraphEvent")]
public void Run([EventGridTrigger] string eventJson)
{
using var doc = JsonDocument.Parse(eventJson);
var type = doc.RootElement.GetProperty("type").GetString();
if (type != null && type.Contains("lifecycleNotification", StringComparison.OrdinalIgnoreCase))
{
_log.LogInformation("Lifecycle event received, renew the subscription: {Type}", type);
// enqueue a renewal job
return;
}
_log.LogInformation("Change event: {Type}", type);
// enqueue the change for processing, do not do slow work here
}
}
Check the exact event schema and type names against your own partner topic before you rely on them, since they differ by resource and Graph version.
Test it: create a test user, confirm an event reaches the function, then delete the user and confirm the delete event arrives. Let a short-lived subscription run to expiry in a test tenant and confirm the lifecycle event fires.
Checklist
- Subscribe to lifecycle notifications as well as change notifications, and renew subscriptions when the lifecycle events say so.
- Check the maximum subscription lifetime for the resource type, and set renewal well before it.
- Put a
clientStatesecret in the subscription and validate it on receipt where your handler can see it. - Keep handlers fast and idempotent. Queue work and return, since events can be delivered more than once.
- Configure dead-lettering on the event subscription and alert when messages land there.
- Use encrypted rich notifications where the resource supports them, and manage the certificates as documented.
Where this doesn't apply
Extremely high-volume streams, where Event Hubs is a better fit. Also, resources that Graph does not support through Event Grid partner topics still need a webhook.
Sources
- Change notifications overview — https://learn.microsoft.com/en-us/graph/change-notifications-overview
- Microsoft Graph Event Grid sample (.NET) — https://github.com/microsoftgraph/msgraph-sample-eventgrid-notifications-dotnet
- Microsoft Graph Event Grid sample (Java) — https://github.com/microsoftgraph/msgraph-sample-eventgrid-notifications-java
---