Process long-running operations by using Azure Functions
The previous unit established that Contoso's equipment-provisioning solution can't leave a Power Automate flow or Dataverse caller waiting on the external device-availability check. This unit builds on that idea directly: instead of a function that assumes a quick reply, you explore how to handle work that's too slow for a single request/response cycle.
Recognize when a direct request/response call breaks down
Picture the most direct approach to Contoso's availability check. A Power Automate flow calls an HTTP-triggered Azure Function, the function calls the external device-availability service, waits for the result, and returns it in the same response. For a lookup that finishes in a second or two, that approach works fine. The device-availability service can also take several minutes during a stock reconciliation window, and that's where the direct call breaks down.
An HTTP-triggered function that doesn't complete within 230 seconds gets cut off by the platform's load balancer, and the caller's request fails even though the function keeps running in the background. The caller sees a failure and may retry, potentially triggering the external service again while the original attempt is still in progress.
Lengthening the function's own timeout setting doesn't solve this, because the 230-second limit sits in front of the function host and applies no matter how the function itself is configured. Separately from the 230-second limit, a function's own execution timeout depends on the hosting plan: Consumption defaults to 5 minutes (10 minutes maximum), while Flex Consumption, Premium, and Dedicated plans default to 30 minutes with no maximum. Flex Consumption is the recommended plan for new serverless apps.
The fix isn't a longer wait. It's a different contract between caller and function, one where the caller never blocks on a single long-running request in the first place.
Acknowledge immediately, resolve in the background
A more durable contract looks like this: the function accepts the request, responds right away to confirm receipt, and lets the caller check back later for the result. This request, acknowledge, and poll pattern separates "did you receive my request" from "is the work done," so a slow external dependency no longer holds a connection open.
Power Automate already expects this kind of contract from flows and functions it calls. When a flow configures an asynchronous response, it returns an HTTP 202 status code together with a Location header that points to a status-check URL. The caller then follows that URL until it gets back a completed result, instead of waiting on an open connection.
Because Contoso's provisioning flow calls an Azure Function the same way it calls a child flow, the function can expose that identical 202-and-Location contract. Whichever side starts the long-running work, the shape of the handoff stays the same: acknowledge first, resolve later.
Let Durable Functions coordinate the workflow
Building that acknowledge-and-poll contract by hand means designing a status table, a polling endpoint, and recovery logic for a restart partway through. Durable Functions, an extension of Azure Functions, takes on that plumbing. The workflow lives in an orchestrator function that describes each step in code: call the availability check, wait for the result, update the request record.
The Durable Functions runtime coordinates execution and checkpoints progress every time the orchestrator reaches an await or yield. For .NET, use the isolated worker model rather than the in-process model: in-process support ends on November 10, 2026.
That checkpointing matters for a workflow like Contoso's. If the host restarts while the availability check is still running, the orchestration doesn't start over from the beginning. The runtime replays its history and resumes where it left off, whether that process takes seconds or spans the several minutes a stock check can require.
An HTTP-triggered starter function kicks off this kind of orchestration. It accepts the original request details, such as a request ID, device type, and quantity, starts the orchestration, and immediately returns HTTP 202 Accepted along with a set of management URLs: a status-query URL, an event URL for signaling the running instance, and URLs for terminating or purging it.
The Power Automate flow (or whatever Dataverse-connected component made the call) follows the status-query URL the same way it would follow a Location header from an asynchronous flow response. That means nobody has to build a custom status store.
Check multiple sources at once, then combine the results
A single availability check against one external service is a reasonable starting point, but Contoso's catalog spans more than one fulfillment source: a primary warehouse, a regional backup site, and a pool of refurbished devices that get inspected before reissue. Querying each source one after another adds up. Three sequential two-minute checks become six minutes of waiting even though none of the checks depend on each other.
This sequential wait is where an orchestration pattern beyond plain sequencing earns its place. A fan-out/fan-in orchestration calls each source's availability check in parallel as separate activity functions. The orchestrator then waits for every call to finish before it fans back in. It picks the best option, such as the nearest warehouse with stock or the lowest handling cost among sources that report availability.
The orchestrator checkpoints this parallel work the same way it checkpoints a sequential call, so a restart during the fan-out doesn't force every source to be queried again.
Guiding question: Think about a workflow in your own solution that checks several sources one after another even though those checks don't depend on each other. Where would fanning them out in parallel, then fanning in on the best result, reduce the total wait?
Match the pattern to the problem
Not every long-running call justifies an orchestrator. A single asynchronous HTTP call acknowledges with 202 and lets you poll a status URL you maintain yourself. That pattern is enough when there's exactly one external call to wait on and no need to coordinate multiple steps or recover complex state after a crash.
Durable Functions earns its complexity when:
- The workflow has more than one step that depends on the last, and you want checkpointing and retries built in instead of hand-rolled.
- Fan-out/fan-in or sub-orchestrations express the workflow more clearly than custom coordination code.
- The wait is long enough that a durable timer, rather than your own polling loop, should manage the delay.
| Situation | Simple async HTTP pattern | Durable Functions |
|---|---|---|
| Dependent steps | One external call | Multiple steps, sequential or parallel |
| State after a restart | Rebuilt by your own code | Checkpointed and resumed automatically |
| Combining parallel results | Not built in | Fan-out/fan-in pattern available |
| Typical wait | Seconds to a couple of minutes | Minutes to days or longer |
For Contoso's equipment-provisioning solution, a single allowance check can stay a simple asynchronous call. The multi-source availability check is different: with its parallel lookups and its need to survive a restart mid-check, it's a better fit for an orchestrator.
Avoid two traps that undermine the pattern
The management URLs that the starter function returns exist so nobody has to guess at a polling strategy. A common mistake is ignoring the retry guidance that comes with the status response and polling every few hundred milliseconds instead. That adds load without benefit, because the orchestration finishes on its own schedule and tighter polling doesn't speed it up.
The second trap is more subtle: treating orchestrator code as though it runs exactly once. Because the runtime replays an orchestrator's history to rebuild its state, the orchestrator function itself must be deterministic and produce the same result on every replay. That means the orchestrator shouldn't call an API like the current date and time or a random-number generator directly.
It also shouldn't make an HTTP call inline, since each replay would produce a different answer and corrupt the workflow's state. Non-deterministic work, including the call to the external device-availability service, belongs in a separate activity function that the orchestrator calls and awaits. The orchestrator coordinates the steps. Activity functions handle the I/O.
With the availability check now running as a durable, non-blocking workflow, the next concern is reliability elsewhere. Contoso's scheduled reconciliation and event-driven updates need to be just as reliable, without anyone having to kick them off by hand.