Azure Durable Functions is an extension of Azure Functions that enables you to build stateful, long-running workflows using serverless computing. Unlike traditional Azure Functions, which typically execute individual tasks in response to triggers, Durable Functions allows you to coordinate multiple functions, maintain workflow state, and manage execution automatically.
With Azure Durable Functions, you can build solutions for scenarios such as order processing, approval workflows, scheduled tasks, data processing pipelines, and other business processes that require multiple steps.
In this article, we will learn how to create your first Azure Durable Function using C#. We will explore the basic architecture of Durable Functions, understand the roles of client, orchestrator, and activity functions, and implement a simple example step by step. By the end of this tutorial, you will have a basic understanding of how Durable Functions work and how to build your own serverless workflows in Azure.
Basic Components of Azure Durable Functions
Before learning how to create Azure Durable Functions in C#, let’s first explore their basic components and understand the role each component plays in executing a workflow.
Azure Durable Functions consists of three primary components that are Client Functions, Orchestrator Functions, and Activity Functions. Each component has a specific responsibility in managing and executing a workflow.
1. Client FunctionA Client Function is responsible for starting a new durable workflow or interacting with an existing orchestration instance. It is typically triggered by an HTTP request, a queue message, or another supported event.
The client function uses a Durable Functions client to schedule an orchestration and can return the orchestration instance ID or a response containing status-check URLs.
Example: An HTTP-triggered function receives an order request and starts an order-processing workflow.
2. Orchestrator FunctionAn Orchestrator Function controls the workflow execution. It defines the sequence of steps, calls activity functions, and manages workflow coordination, including waiting for events, timers, or the completion of parallel tasks.
Durable Functions automatically saves the orchestration state and restores execution when necessary, allowing long-running workflows to continue without keeping a process running continuously.
Example: An orchestrator coordinates order validation, payment processing, and order confirmation in the correct sequence.
Note: Orchestrator functions must follow deterministic execution rules so that their state can be reliably replayed.
3. Activity FunctionAn Activity Function performs the actual business logic within a workflow. It is called by an orchestrator to execute individual tasks, such as accessing a database, calling an external API, processing a file, or sending an email.
Activity functions can perform I/O operations and other business logic that should not run directly inside an orchestrator.
Example: An activity function validates an order, processes a payment, or sends an order confirmation email.
How These Components Work Together
These three components work together to execute a durable workflow:- The Client Function receives a request and starts the orchestration.
- The Orchestrator Function determines the workflow steps and coordinates their execution.
- The Activity Functions perform the individual business operations requested by the orchestrator.
- The orchestrator tracks the workflow progress and completes the process when all required activities finish.
For example, in an order-processing application, the client starts the workflow, the orchestrator coordinates the steps, and activity functions validate the order, process payment, and send a confirmation.
Together, these components make it easier to build reliable, stateful, and scalable serverless applications using Azure Durable Functions.
Create Azure Durable Functions in C#
In this section, we will create an Azure Functions project using C# and the .NET isolated worker model. You can create the project using either Visual Studio or the .NET CLI.
Before getting started, ensure you have the following installed:- Visual Studio 2022 or a suitable .NET development environment
- Supported .NET SDK
- Azure Functions Core Tools
- Azure subscription for deployment
- Local development storage or a supported Durable Task backend Mention the required NuGet packages.
Create the project using Visual Studio
Follow these steps to create an Azure Functions project in Visual Studio.- Create a new project
Open Visual Studio and select File → New → Project. Search for Azure Functions, select the Azure Functions template, and click Next.
- Configure the project
Enter a project name, such as
DurableFunctionsDemo, choose the project location, and click Next. - Select the worker model
In the additional configuration screen, select a supported .NET version and choose the .NET isolated worker model. If the template offers a Durable Functions trigger, you can select it; otherwise, create the project and add the Durable Functions package in the next step.
- Click Create to generate the project.
Visual Studio generates the basic project structure, including the C# project file, Program.cs, host.json, and local.settings.json.
Implement Your First Durable Function in C#
Now that we have created the Azure Functions project and explored the basic components of Azure Durable Functions, let's implement our first Durable Function using C#.
In this example, we will create a simple order-processing workflow. The workflow consists of three functions:
- Client Function: Starts the order-processing orchestration when an HTTP request is received.
- Orchestrator Function: Coordinates the execution of the workflow.
- Activity Function: Processes the order and returns the result.
Step 1: Install the Required NuGet Packages
Before implementing the functions, make sure your project includes the required NuGet packages for Azure Functions and Durable Functions.
For a .NET isolated worker project, install the following packages:Microsoft.Azure.Functions.WorkerMicrosoft.Azure.Functions.Worker.Extensions.HttpMicrosoft.Azure.Functions.Worker.Extensions.DurableTask
By default the Durable Task extensionYou installed in the project, you can install the extension using the .NET CLI:
</> Bash
dotnet add package Microsoft.Azure.Functions.Worker.Extensions.DurableTask
Step 2: Implement the Activity Function
The activity function contains the actual business logic. In our example, it processes an order and returns a confirmation message.
Create a file namedOrderActivity.cs and add the following code:
</> C#
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
public class OrderActivity
{
private readonly ILogger<OrderActivity> _logger;
public OrderActivity(ILogger<OrderActivity> logger)
{
_logger = logger;
}
[Function(nameof(ProcessOrder))]
public string ProcessOrder(
[ActivityTrigger] string orderId)
{
_logger.LogInformation(
"Processing order: {OrderId}", orderId);
return $"Order {orderId} processed successfully.";
}
}
Here, the [Function] attribute registers the function with the Azure Functions runtime, while [ActivityTrigger] identifies it as an activity function.
The ProcessOrder method receives an order ID, performs the processing logic, and returns a confirmation message. In a real-world application, this method could validate an order, update a database, or call an external service.
The orchestrator function coordinates the workflow by calling one or more activity functions. It determines the execution sequence and waits for the activities to complete.
Create a file namedOrderOrchestrator.cs and add the following code:
</> C#
using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
public static class OrderOrchestrator
{
[Function(nameof(ProcessOrderOrchestrator))]
public static async Task<string>
ProcessOrderOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
string orderId = context.GetInput<string>();
string result = await context.CallActivityAsync<string>(
nameof(OrderActivity.ProcessOrder),
orderId);
return result;
}
}
Let's understand the important parts of this implementation:
[OrchestrationTrigger]identifies the function as an orchestrator.TaskOrchestrationContextprovides methods to coordinate the workflow and call activity functions.GetInput<string>()retrieves the order ID provided when the orchestration starts.CallActivityAsync<string>()invokes the activity function and waits for its result.
Important: Orchestrator functions must be deterministic because Azure Durable Functions can replay them to rebuild their execution state. Avoid performing direct I/O operations, such as database queries or HTTP requests, inside the orchestrator. Put such operations in activity functions instead.
Step 4: Implement the Client FunctionThe client function starts the orchestration when a client sends an HTTP request. In this example, the client provides an order ID through a query-string parameter.
Create a file namedOrderClient.cs and add the following code:
</> C#
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.DurableTask.Client;
using System.Net;
public static class OrderClient
{
[Function(nameof(StartOrderProcessing))]
public static async Task<HttpResponseData>
StartOrderProcessing(
[HttpTrigger(AuthorizationLevel.Function, "get")]
HttpRequestData req,
[DurableClient] DurableTaskClient client)
{
var query = System.Web.HttpUtility.ParseQueryString(
req.Url.Query);
string orderId = query["orderId"];
if (string.IsNullOrWhiteSpace(orderId))
{
var badRequest = req.CreateResponse(
HttpStatusCode.BadRequest);
await badRequest.WriteStringAsync(
"Please provide an orderId.");
return badRequest;
}
string instanceId =
await client.ScheduleNewOrchestrationInstanceAsync(
nameof(OrderOrchestrator.ProcessOrderOrchestrator),
orderId);
return await client.CreateCheckStatusResponseAsync(
req, instanceId);
}
}
The client function uses the [DurableClient] attribute to obtain a DurableTaskClient instance. It then calls ScheduleNewOrchestrationInstanceAsync() to start the orchestration.
The function returns a status response containing the orchestration instance ID and URLs that can be used to check its progress.
In this example, we use an HTTP GET request for simplicity. For a production order-processing API that creates or modifies an order, an HTTP POST request with a request body is generally more appropriate.
Make sure the required local development dependencies are configured, including the Azure Functions Core Tools and the Durable Functions storage provider. For the common local setup, this means configuring Azure Storage and starting the Azurite emulator or using an appropriate Azure Storage account.
After the Functions host starts, locate the URL for the StartOrderProcessing HTTP-triggered function in the console output.
Send a request similar to the following:</> html
GET http://localhost:7071/api/StartOrderProcessing?orderId=ORD1001
Replace the URL with the actual URL printed by your local Functions host if it differs.
The client function starts the orchestration and returns a response containing the orchestration status URLs. Use the returned status URL to check whether the workflow has completed.
When the workflow finishes successfully, the result will be similar to:</> JSON
{
"runtimeStatus": "Completed",
"output": "Order ORD1001 processed successfully."
}
The response may include additional properties, such as the instance ID, creation time, and last updated time.
How Does the Workflow Work?When you send an HTTP request to the client function, the following sequence takes place:
- The client function receives the order ID.
- The client function starts a new orchestration instance.
- The orchestrator invokes the
ProcessOrderactivity function. - The activity function processes the order and returns a confirmation message.
- The orchestrator returns the activity result.
- You can retrieve the completed workflow result using the status URL returned by the client function.
This example demonstrates how Azure Durable Functions coordinates multiple functions to execute a workflow reliably. You can extend the same approach to more complex scenarios, such as processing payments, sending notifications, or coordinating multiple external services.
A technical note before publishing: This example uses the current .NET isolated worker programming model and the Durable Task extension. Ensure your project targets a supported .NET version and includes the relevant package versions. If you use System.Web.HttpUtility, confirm it is available for your target framework; otherwise, use an alternative query-string parser.
Deploy the Durable Function to Azure
After implementing and testing the Durable Function locally, the next step is to deploy it to Azure. Azure Functions supports several deployment options, including Visual Studio, Visual Studio Code, and the Azure Functions Core Tools.
In this section, we will deploy our Durable Function to Azure using Visual Studio and verify that the workflow executes successfully in the cloud.
Step 1: Create an Azure Function AppBefore deploying the project, you need to create a Function App in the Azure portal. A Function App provides the hosting environment for your Azure Functions.
Follow these steps:- Sign in to the Azure portal.
- Search for Function App and select Create.
- Choose your Azure subscription and create a new resource group or select an existing one.
- Enter a unique name for the Function App.
- Select the appropriate runtime stack and version for your .NET isolated worker project.
- Choose a supported hosting plan and select a region.
- Configure the required storage account.
- Review the configuration and select Create.
Note: Choose a hosting plan that supports your workload and required Durable Functions capabilities. Review the current Azure Functions hosting documentation before selecting a plan.
Step 2: Configure Storage for Durable FunctionsAzure Durable Functions requires storage to maintain orchestration state, coordinate execution, and manage workflow history.
For a typical Azure Storage-based configuration, open your Function App in the Azure portal and navigate to Settings → Environment variables (the exact navigation may vary).
Ensure that the required application settings are configured:AzureWebJobsStorage: The storage connection used by the Azure Functions runtime and the standard Azure Storage-backed Durable Task provider.FUNCTIONS_WORKER_RUNTIME: Set todotnet-isolatedfor the .NET isolated worker model.
The Function App also needs the appropriate Durable Functions storage-provider configuration. For the standard Azure Storage provider, the AzureWebJobsStorage setting is commonly used.
Save any changes and restart the Function App if required.Security tip: For production applications, consider using managed identity and supported identity-based storage configuration instead of storing account keys in application settings.
Step 3: Publish the Project from Visual Studio- Open your Azure Functions project in Visual Studio.
- Right-click the project in Solution Explorer and select Publish.
- Choose Azure as the target.
- Select Azure Function App (Windows or Linux), depending on your hosting configuration.
- Sign in to your Azure account if prompted.
- Select the Function App you created in the previous step.
- Review the publishing settings and select Publish.
Visual Studio builds the project and deploys the application files to the selected Function App. After publishing completes, open the Function App in the Azure portal and check its deployment status.
Step 4: Verify the Deployed Function- Open your Function App in the Azure portal.
- Navigate to Functions and select
StartOrderProcessing. - Locate the function URL or use the option to obtain its function URL, if available.
- Send an HTTP request containing an order ID.
</> html
GET https://<your-function-app>.azurewebsites.net/api/StartOrderProcessing?orderId=ORD1001
Replace <your-function-app> with your actual Function App name. Depending on the HTTP trigger's authorization level, you may also need to provide a function key.
If the request succeeds, the client function starts a new orchestration instance and returns a response containing the instance ID and status-check URLs.
Use the status URL returned in the response to monitor the orchestration. When execution finishes, the response should contain a completed status and an output similar to the following:
</> JSON
{
"runtimeStatus": "Completed",
"output": "Order ORD1001 processed successfully."
}
Step 5: Monitor Execution in Azure
You can monitor the deployed Durable Function through the Azure portal and Application Insights, when configured.
Use these resources to troubleshoot problems:- Function execution logs: Review function invocations and application log messages.
- Application Insights: Investigate failures, exceptions, and execution traces when telemetry is enabled and configured.
- Durable orchestration status: Use the status URL returned by the client function to check whether an orchestration is running, completed, or failed.
If the orchestration does not complete, verify that the storage configuration is correct, the Function App has started successfully, and all required dependencies have been deployed.
By following these steps, you can deploy your first Azure Durable Function to the cloud and execute the order-processing workflow using a publicly reachable HTTP endpoint, subject to the function's configured authorization requirements.
Summary
Azure Durable Functions provides a powerful way to build stateful workflows in Azure Functions using C#. By combining client functions, orchestrator functions, and activity functions, developers can coordinate multiple operations and manage long-running processes without implementing workflow state management from scratch.
In this article, we explored the basic components of Azure Durable Functions, created an Azure Functions project using the .NET isolated worker model, implemented our first Durable Function, and deployed it to Azure. We also discussed common issues and troubleshooting techniques to help resolve problems during development and deployment.
You can extend this example to build more advanced workflows, such as order processing, payment handling, automated notifications, and multi-step business processes. As you gain experience, explore additional Durable Functions capabilities, including parallel execution, retry policies, timers, and human interaction workflows.
By understanding these fundamentals, you can start building reliable, scalable, and maintainable workflow-based applications using Azure Durable Functions and C#.
Thanks