This template repository contains a Durable Functions sample demonstrating the fan-out/fan-in pattern in Python (v2 programming model), backed by the Azure Durable Task Scheduler (DTS). The sample can be easily deployed to Azure using the Azure Developer CLI (azd). It uses a user-assigned managed identity and can optionally deploy a virtual network.
Durable Functions orchestrates stateful, long-running, multi-step logic with durable execution. State is persisted by a backend provider. This sample uses the Azure Durable Task Scheduler provider, a fully managed backend purpose-built for Durable Functions and the Durable Task Framework. It replaces the Azure Storage backend and provides a dedicated dashboard for monitoring orchestrations.
This sample uses the standard Functions extension bundle (
Microsoft.Azure.Functions.ExtensionBundle, version[4.*, 5.0.0)), which provides theazureManagedstorage provider for the Durable Task Scheduler backend.
- Python 3.11+
- Azure Functions Core Tools v4
- Azure Developer CLI (
azd) - Azure CLI
- Docker (only for the local DTS emulator)
- To run/debug in Visual Studio Code:
You can initialize a project from this azd template in one of these ways:
-
Use
azd initfrom an empty local folder:azd init --template durable-functions-quickstart-python-azd
-
Or clone directly:
git clone https://github.com/Azure-Samples/durable-functions-quickstart-python-azd.git cd durable-functions-quickstart-python-azd
Copy the sample local settings file into place:
cp src/local.settings.json.sample src/local.settings.jsonThe resulting src/local.settings.json already points at the local DTS emulator:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "python",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;Authentication=None",
"TASKHUB_NAME": "default"
}
}AzureWebJobsStorage is still used by the Functions host itself (not for Durable state) — the local storage emulator Azurite covers that.
From the src folder, create a virtual environment and install dependencies:
cd src
python -m venv .venv
source .venv/bin/activate # On Windows use: .venv\Scripts\activate
pip install -r requirements.txt
cd ..The DTS emulator runs in Docker. It exposes the DTS endpoint on port 8080 and the DTS dashboard on port 8082:
docker run -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latestOpen the dashboard at http://localhost:8082 to watch orchestrations in real time.
In a separate terminal, start Azurite so AzureWebJobsStorage resolves:
npx azurite --skipApiVersionCheck --location ~/azurite-dataOr:
docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite-
From the
srcfolder, start the Functions host:func start
You should see output similar to:
Functions: http_start: http://localhost:7071/api/orchestrators/{functionName} fetch_orchestration: orchestrationTrigger fetch_title: activityTrigger -
In another terminal (or your browser), hit the HTTP trigger: http://localhost:7071/api/orchestrators/fetch_orchestration
It returns the orchestration instance ID and status URLs. Watch the orchestration progress in the DTS dashboard at http://localhost:8082.
-
Press Ctrl+C to stop the Functions host when finished.
- Open the repository folder in VS Code (
code .). - Ensure the DTS emulator (and Azurite) are running, as described above.
- Press Run/Debug (F5) to start the app in the debugger.
- Trigger the orchestration with an HTTP request to http://localhost:7071/api/orchestrators/fetch_orchestration.
Fanning out is easy with regular functions — just send multiple messages to a queue. Fanning back in is harder because you have to track completion and aggregate results. Durable Functions makes this simple:
@myApp.orchestration_trigger(context_name="context")
def fetch_orchestration(context: df.DurableOrchestrationContext):
"""Orchestrator function that fans out to fetch article titles in parallel."""
logger = logging.getLogger("fetch_orchestration")
logger.info("Fetching data.")
urls = [
"https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-overview",
"https://learn.microsoft.com/azure/azure-functions/durable/durable-task-scheduler/durable-task-scheduler",
"https://learn.microsoft.com/azure/azure-functions/functions-scenarios",
"https://learn.microsoft.com/azure/azure-functions/functions-create-ai-enabled-apps",
]
tasks = [context.call_activity("fetch_title", url) for url in urls]
results = yield context.task_all(tasks)
return "; ".join(results)Deactivate the virtual environment if it is still active:
deactivateFrom the repo root, provision all resources (function app, storage, Log Analytics, Application Insights, user-assigned managed identity, Durable Task Scheduler + task hub, and role assignments) and deploy your code:
azd auth login
azd upTo skip the virtual-network prompt, pre-set the parameter:
azd env set VNET_ENABLED false
azd upYou'll be prompted for:
| Parameter | Description |
|---|---|
| Environment name | Unique deployment context for your app. |
| Azure subscription | Subscription where resources are created. |
| Azure location | Region from the DTS-supported allowlist in infra/main.bicep (default: northcentralus). |
When deployment completes, azd prints the function app endpoints. In Azure, the Durable Task Scheduler is wired to the function app via the DURABLE_TASK_SCHEDULER_CONNECTION_STRING app setting, which uses the user-assigned managed identity (Authentication=ManagedIdentity;ClientID=<uami-clientId>).
az functionapp function list \
--resource-group <resource-group-name> \
--name <function-app-name> \
--query "[].{name:name, url:invokeUrlTemplate}" \
--output tableThen call:
https://<function-app-name>.azurewebsites.net/api/orchestrators/fetch_orchestration
In Azure, open the deployed Durable Task Scheduler resource (type Microsoft.DurableTask/schedulers) in the Azure portal and follow the Dashboard link to inspect orchestrations, activities, history, and instance state. Locally, the dashboard is served by the emulator at http://localhost:8082.
To find the scheduler name quickly:
azd showRun azd up again any time to re-provision and redeploy. Deployed code is always overwritten by the latest deployment package.
azd downIf you see the following transient error after azd up, rerun the command:
ERROR: error executing step command 'deploy --all': failed deploying service 'api': publishing zip file: deployment failed: [KuduSpecializer] Kudu has been restarted during deployment