.github/skills/migrate-backend-to-dts/SKILL.md
Migrate existing Azure Durable Functions apps from existing backend storage providers (Azure Storage, Netherite, MSSQL) to the Durable Task Scheduler. Use when switching backends, converting to azureManaged storage provider, upgrading from Azure Storage default provider, migrating from Netherite Event Hubs-based backend, migrating from Microsoft SQL Server backend, or modernizing Durable Functions infrastructure. Applies to .NET, Python, JavaScript/TypeScript, and Java Durable Functions apps that need to adopt the managed Durable Task Scheduler service.
npx skillsauth add Azure-Samples/Durable-Task-Scheduler migrate-backend-to-dtsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Step-by-step guide for migrating Azure Durable Functions apps from existing backend storage providers to the Durable Task Scheduler (DTS).
Microsoft.Azure.WebJobs.Extensions.DurableTask), migrate to isolated worker first.Inspect your host.json to determine which backend you're migrating from:
| Current storageProvider.type | Backend | Key Indicator |
|-------------------------------|---------|---------------|
| (missing or empty) | Azure Storage (default) | No explicit type; uses AzureWebJobsStorage connection |
| "azure" | Azure Storage (explicit) | Same as default |
| "netherite" | Netherite | Requires Event Hubs connection string |
| "mssql" | Microsoft SQL | Requires SQL Server connection string |
.NET (.csproj):
| Package | Backend |
|---------|---------|
| Microsoft.Azure.WebJobs.Extensions.DurableTask (no suffix) | Azure Storage (in-process — must also migrate to isolated) |
| Microsoft.Azure.Functions.Worker.Extensions.DurableTask (no suffix) | Azure Storage (isolated) |
| Microsoft.Azure.DurableTask.Netherite.AzureFunctions | Netherite |
| Microsoft.DurableTask.SqlServer.AzureFunctions | MSSQL |
Python (requirements.txt): azure-functions-durable — backend is configured in host.json only.
JavaScript (package.json): durable-functions — backend is configured in host.json only.
Java (build.gradle/pom.xml): azure-functions-java-library — backend is configured in host.json only.
Remove your old storageProvider block and replace it with the DTS configuration.
// BEFORE — Azure Storage (default, no storageProvider block)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub"
}
}
}
// AFTER — Durable Task Scheduler
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "%TASKHUB_NAME%",
"storageProvider": {
"type": "azureManaged",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
}
}
// BEFORE — Azure Storage (explicit type)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub",
"storageProvider": {
"type": "azure",
"connectionStringName": "AzureWebJobsStorage"
}
}
}
}
// AFTER — Durable Task Scheduler
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "%TASKHUB_NAME%",
"storageProvider": {
"type": "azureManaged",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
}
}
// BEFORE — Netherite
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub",
"storageProvider": {
"type": "netherite",
"storageConnectionName": "AzureWebJobsStorage",
"eventHubsConnectionName": "EventHubsConnection",
"partitionCount": 12
}
}
}
}
// AFTER — Durable Task Scheduler
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "%TASKHUB_NAME%",
"storageProvider": {
"type": "azureManaged",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
}
}
Netherite cleanup: After migration, remove the
EventHubsConnectionsetting and consider deprovisioning the Event Hubs namespace if no longer needed. DTS handles partitioning internally —partitionCountis not needed.
// BEFORE — Microsoft SQL Server
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub",
"storageProvider": {
"type": "mssql",
"connectionStringName": "SQLDB_Connection",
"taskEventLockTimeout": "00:02:00",
"createDatabaseIfNotExists": true,
"schemaName": "dt"
}
}
}
}
// AFTER — Durable Task Scheduler
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "%TASKHUB_NAME%",
"storageProvider": {
"type": "azureManaged",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
}
}
MSSQL cleanup: After migration, remove
SQLDB_Connectionfrom app settings. Thedt.*schema tables in your SQL database can be dropped once you've confirmed the migration is successful.
For Python, JavaScript, and Java apps, migration is configuration-only — no code changes or package changes are required. You only need to update host.json.
There are two key differences from .NET:
"durabletask-scheduler" (not "azureManaged")// BEFORE — host.json (Azure Storage default)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub"
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.0.0, 5.0.0)"
}
}
// AFTER — host.json (Durable Task Scheduler)
{
"version": "2.0",
"logging": {
"logLevel": {
"DurableTask.Core": "Warning"
}
},
"extensions": {
"durableTask": {
"hubName": "default",
"storageProvider": {
"type": "durabletask-scheduler",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Preview",
"version": "[4.29.0, 5.0.0)"
}
}
requirements.txt — no changes needed:
azure-functions
azure-functions-durable
local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "python",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
}
}
// BEFORE — host.json (Azure Storage default)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub"
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.0.0, 5.0.0)"
}
}
// AFTER — host.json (Durable Task Scheduler)
{
"version": "2.0",
"logging": {
"logLevel": {
"DurableTask.Core": "Warning"
}
},
"extensions": {
"durableTask": {
"hubName": "default",
"storageProvider": {
"type": "durabletask-scheduler",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Preview",
"version": "[4.29.0, 5.0.0)"
}
}
package.json — no changes needed:
{
"dependencies": {
"@azure/functions": "^4.0.0",
"durable-functions": "^3.0.0"
}
}
local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "node",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
}
}
// BEFORE — host.json (Azure Storage default)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub"
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.0.0, 5.0.0)"
}
}
// AFTER — host.json (Durable Task Scheduler)
{
"version": "2.0",
"logging": {
"logLevel": {
"DurableTask.Core": "Warning"
}
},
"extensions": {
"durableTask": {
"hubName": "default",
"storageProvider": {
"type": "durabletask-scheduler",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Preview",
"version": "[4.29.0, 5.0.0)"
}
}
pom.xml — no changes needed. Your existing dependencies stay the same:
<dependency>
<groupId>com.microsoft.azure.functions</groupId>
<artifactId>azure-functions-java-library</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>com.microsoft</groupId>
<artifactId>durabletask-azure-functions</artifactId>
<version>1.7.0</version>
</dependency>
local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "java",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None"
}
}
The target configuration is the same regardless of which existing backend you're migrating from. Replace the old storageProvider block and update the extension bundle as shown above. The only additional step is removing the old backend's connection strings from your app settings:
EventHubsConnection (or your Event Hubs connection name)SQLDB_Connection (or your SQL connection name)Non-.NET languages do not need package changes — skip to Step 4.
<!-- REMOVE one of these (whichever you have): -->
<PackageReference Include="Microsoft.Azure.DurableTask.Netherite.AzureFunctions" Version="*" />
<PackageReference Include="Microsoft.DurableTask.SqlServer.AzureFunctions" Version="*" />
<!-- ADD: -->
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged" Version="*" />
<PackageReference Include="Azure.Identity" Version="1.*" />
This is a larger migration. Replace the entire package set:
<!-- REMOVE all in-process packages: -->
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.DurableTask" Version="*" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="*" />
<!-- ADD isolated worker packages: -->
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.*" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged" Version="*" />
<PackageReference Include="Azure.Identity" Version="1.*" />
The in-process to isolated migration also requires code changes (new
Program.cs, attribute changes, etc.). See Microsoft's migration guide.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;Authentication=None",
"TASKHUB_NAME": "default"
}
}
DURABLE_TASK_SCHEDULER_CONNECTION_STRING=Endpoint=https://<scheduler-name>.<region>.durabletask.io;Authentication=DefaultAzure
TASKHUB_NAME=<your-taskhub-name>
| Backend | Settings to Remove |
|---------|-------------------|
| Azure Storage | AzureWebJobsStorage is still needed for Functions runtime, but no longer used for Durable state |
| Netherite | EventHubsConnection (or your Event Hubs connection name) |
| MSSQL | SQLDB_Connection (or your SQL connection name) |
DTS uses identity-based authentication. No shared keys.
No authentication needed — the emulator accepts unauthenticated requests:
Endpoint=http://localhost:8080;Authentication=None
Durable Task Scheduler Task Hub Contributor role on the DTS resourceEndpoint=https://<scheduler-name>.<region>.durabletask.io;Authentication=DefaultAzure
If your orchestrations pass large inputs/outputs (>10 KB), enable large payload storage to avoid message size limits:
.NET:
{
"extensions": {
"durableTask": {
"storageProvider": {
"type": "azureManaged",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING",
"largePayloadStorageEnabled": true,
"largePayloadStorageThresholdBytes": 10240
},
"hubName": "%TASKHUB_NAME%"
}
}
}
Python / JavaScript / Java:
{
"extensions": {
"durableTask": {
"storageProvider": {
"type": "durabletask-scheduler",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING",
"largePayloadStorageEnabled": true,
"largePayloadStorageThresholdBytes": 10240
},
"hubName": "default"
}
}
}
This offloads large payloads to Azure Blob Storage via the AzureWebJobsStorage connection.
# 1. Start the DTS emulator
docker run -d -p 8080:8080 -p 8082:8082 --name dts-emulator mcr.microsoft.com/dts/dts-emulator:latest
# 2. Start Azurite (required for Azure Functions runtime)
azurite start
# 3. Run your Function App
func start
# 4. Open the DTS dashboard to monitor orchestrations
open http://localhost:8082
Trigger your orchestrations and verify they complete successfully. Check the dashboard for execution history.
Running orchestrations do not carry over between backends. Before switching:
There is no tool to export/import orchestration state between backends.
DTS only supports the isolated worker model for .NET. If your app uses Microsoft.Azure.WebJobs.Extensions.DurableTask (in-process), you must migrate to isolated worker first. See Migrate to isolated worker.
Task hub names from your old backend won't conflict with DTS — they are separate systems. You can use any name for your DTS task hub.
partitionCount configuration — DTS manages partitions automaticallydt.* schema tables can be dropped after confirming successful migrationtaskEventLockTimeout, createDatabaseIfNotExists, and schemaName settings are not used by DTSThese APIs (SetCustomStatus, RaiseEventAsync, WaitForExternalEvent) work identically on DTS. No code changes needed.
Durable Entities are supported on DTS with .NET isolated worker. No code changes needed — only the backend configuration changes.
host.json updated with azureManaged (or durabletask-scheduler) storage providerMicrosoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged added (.NET only)Azure.Identity package added (.NET only)Durable Task Scheduler Task Hub Contributor)development
Build durable, fault-tolerant workflows in Python using the Durable Task SDK with Azure Durable Task Scheduler. Use when creating orchestrations, activities, entities, or implementing patterns like function chaining, fan-out/fan-in, human interaction, or stateful agents. Applies to any Python application requiring durable execution, state persistence, or distributed transactions without Azure Functions dependency.
development
Build durable, fault-tolerant workflows in JavaScript/TypeScript using the Durable Task SDK with Azure Durable Task Scheduler. Use when creating orchestrations, activities, entities, or implementing patterns like function chaining, fan-out/fan-in, human interaction, or durable timers. Applies to any Node.js application requiring durable execution, state persistence, or distributed coordination without Azure Functions dependency.
development
Build durable, fault-tolerant workflows in Java using the Durable Task SDK with Azure Durable Task Scheduler. Use when creating orchestrations, activities, or implementing patterns like function chaining, fan-out/fan-in, human interaction, or monitoring. Applies to any Java application requiring durable execution, state persistence, or distributed transactions without Azure Functions dependency.
development
Build durable, fault-tolerant workflows in .NET using the Durable Task SDK with Azure Durable Task Scheduler. Use when creating orchestrations, activities, entities, or implementing patterns like function chaining, fan-out/fan-in, human interaction, or stateful agents. Applies to any .NET application requiring durable execution, state persistence, or distributed transactions without Azure Functions dependency.