Graph-Driven Automation with Azure Functions
What you will build
A serverless automation that reacts to Microsoft Graph data changes, processes them, persists workflow state, and produces downstream artifacts. This is a general-purpose pattern for any scenario where Graph is the data source and short-lived compute is the right execution model.
Scenario
You have data in Microsoft Graph that changes over time: group memberships shift, application assignments update, user lifecycle events occur. You need code that periodically checks for changes, processes them, tracks what has been handled, and writes results somewhere a downstream system or operator can pick them up.
Examples:
- Sync group membership changes to an external system’s access list
- React to new application assignments by provisioning resources
- Export user lifecycle data for compliance reporting
Why Azure Functions fits
The work is event-driven and short-lived. Each execution follows the same shape:
- A timer fires or an HTTP request arrives
- The function calls Graph to read current state
- It compares against stored checkpoints
- It processes the delta
- It writes state and artifacts
- It finishes
That shape matches Azure Functions better than a permanently running service. You get managed triggers, automatic scaling on demand, and no idle cost between runs.
Architecture
sequenceDiagram
autonumber
participant Trigger as Timer or HTTP Trigger
participant Func as Azure Functions
participant Graph as Microsoft Graph
participant Cosmos as Cosmos DB
participant Blob as Azure Storage
participant Target as Downstream System
Trigger->>Func: Invoke function
Func->>Cosmos: Read last checkpoint
Cosmos-->>Func: Checkpoint state
Func->>Graph: Query changes since checkpoint
Graph-->>Func: Changed data
Func->>Func: Process and validate changes
Func->>Cosmos: Upsert workflow state + new checkpoint
Func->>Blob: Write export artifact (CSV, JSON, manifest)
Func->>Target: Notify or submit artifact reference
Func-->>Trigger: Return status
Each service has a clear role:
- Microsoft Graph is the data source. It tells you what changed but does not own the workflow around those changes.
- Azure Functions runs the glue logic, scoped to a bounded unit of work.
- Cosmos DB stores durable state: checkpoints, retry counts, correlation IDs, processing progress.
- Azure Storage stores file-shaped outputs: CSVs, JSON exports, manifests, raw snapshots.
Cosmos DB vs Azure Storage
Keep the storage boundary explicit. These are different tools for different access patterns:
| Use Cosmos DB for | Use Azure Storage for |
|---|---|
| Workflow checkpoints | Exported reports and manifests |
| Retry counts and failure metadata | Raw API response snapshots |
| Correlation IDs across runs | Large payloads handed to other systems |
| Processing status per entity | Files downloaded by operators or tools |
The split matters because Cosmos DB is optimized for frequent reads and updates on structured documents (and priced on request units), while Azure Storage is optimized for large, infrequently-accessed blobs (and priced on capacity). Mixing them up costs more and queries worse.
See State and Artifacts for a deeper treatment of this boundary.
Key implementation details
Checkpointing. Store the last-processed timestamp or delta token in Cosmos DB. On each invocation, read the checkpoint first, query Graph for changes since that point, then update the checkpoint only after successful processing. This makes the function resumable after failures.
Idempotency. If the function fails after processing some items but before updating the checkpoint, it will re-process those items on the next run. Design your downstream writes to be idempotent so duplicates are harmless.
Bounded execution. Azure Functions has a maximum execution duration (5 minutes on Consumption, 10 on Premium by default). If you have more work than fits in one execution, process a batch, update the checkpoint, and let the next trigger pick up the rest.
When not to use this pattern
This pattern is wrong when:
- The work is long-running or stateful across steps. If you need durable orchestration with human approval gates or multi-hour waits, consider Durable Functions or a Service Bus worker pattern instead.
- The workload is continuously busy. If the function would fire every few seconds with substantial work each time, a dedicated worker process is cheaper and simpler than constant cold starts.
- You need machine-level control. If the target system requires OS-level software, private network access, or host-bound connectors, see Hybrid Worker on VM.
- The data is a high-volume event stream. If you are processing thousands of events per second, Event Hubs with Data Explorer is a better fit than polling Graph.
- The downstream step needs reliable delivery. If the output is a work item that must be retried and dead-lettered on failure, route it through Service Bus rather than calling the target directly from the function.