Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions infra/abbreviations.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
"devicesProvisioningServices": "provs-",
"devicesProvisioningServicesCertificates": "pcert-",
"documentDBDatabaseAccounts": "cosmos-",
"durableTaskSchedulers": "dts-",
"durableTaskHubs": "th-",
"eventGridDomains": "evgd-",
"eventGridDomainsTopics": "evgt-",
"eventGridEventSubscriptions": "evgs-",
Expand Down
18 changes: 18 additions & 0 deletions infra/core/durable_task/dts-access.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
param principalID string
param roleDefinitionID string
param dtsName string
param principalType string

resource dts 'Microsoft.DurableTask/schedulers@2025-04-01-preview' existing = {
name: dtsName
}

resource dtsRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(dts.id, principalID, roleDefinitionID)
scope: dts
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionID)
principalId: principalID
principalType: principalType
}
}
31 changes: 31 additions & 0 deletions infra/core/durable_task/dts.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
param ipAllowlist array
param location string
param tags object = {}
param name string
param taskhubname string
param skuName string
param skuCapacity int = 0

resource dts 'Microsoft.DurableTask/schedulers@2025-04-01-preview' = {
location: location
tags: tags
name: name
properties: {
ipAllowlist: ipAllowlist
sku: skuName == 'Dedicated' ? {
name: skuName
capacity: skuCapacity
} : {
name: skuName
}
}
}

resource taskhub 'Microsoft.DurableTask/schedulers/taskHubs@2025-04-01-preview' = {
parent: dts
name: taskhubname
}

output dts_NAME string = dts.name
output dts_URL string = dts.properties.endpoint
output TASKHUB_NAME string = taskhub.name
4 changes: 4 additions & 0 deletions infra/core/host/function.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ param diEndpoint string
param openAIEndpoint string
param searchServiceName string
param appInsightsName string
param dtsURL string = ''
param taskHubName string = ''

resource sourceStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
name: sourceStorageAccountName
Expand Down Expand Up @@ -106,6 +108,8 @@ resource flexFunctionApp 'Microsoft.Web/sites@2023-12-01' = {
DI_ENDPOINT: diEndpoint
AZURE_OPENAI_ENDPOINT: openAIEndpoint
SEARCH_SERVICE_ENDPOINT: searchServiceEndpoint
DURABLE_TASK_SCHEDULER_CONNECTION_STRING: 'Endpoint=${dtsURL};Authentication=ManagedIdentity;ClientID=${identityClientId}'
TASKHUB_NAME: taskHubName
}
}
}
Expand Down
1 change: 1 addition & 0 deletions infra/core/storage/storage-account.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
accessTier: 'Hot'
allowBlobPublicAccess: false
allowCrossTenantReplication: false
allowSharedKeyAccess: false
supportsHttpsTrafficOnly: true
defaultToOAuthAuthentication: true
minimumTlsVersion: 'TLS1_2'
Expand Down
57 changes: 57 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,23 @@ targetScope = 'subscription'
param environmentName string
param location string

param dtsName string = ''
param taskHubName string = ''
param dtsLocation string = location
param dtsSkuName string = 'Consumption'
param dtsCapacity int = 0

@description('Id of the user identity to be used for testing and debugging. This is not required in production. Leave empty if not needed.')
param principalId string = ''

var abbrs = loadJsonContent('./abbreviations.json')

var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))

var functionAppName = '${abbrs.webSitesFunctions}${resourceToken}'
var functionContainerName = 'app-package-${functionAppName}'
var dtsResourceName = !empty(dtsName) ? dtsName : '${abbrs.durableTaskSchedulers}${resourceToken}'
var taskHubResourceName = !empty(taskHubName) ? taskHubName : '${abbrs.durableTaskHubs}${resourceToken}'

var tags = { 'azd-env-name': environmentName }

Expand Down Expand Up @@ -112,6 +123,8 @@ module flexFunction 'core/host/function.bicep' = {
diEndpoint: documentIntelligence.outputs.endpoint
openAIEndpoint: openAI.outputs.endpoint
searchServiceName: searchService.outputs.name
dtsURL: dts.outputs.dts_URL
taskHubName: dts.outputs.TASKHUB_NAME
}
}

Expand All @@ -128,6 +141,50 @@ module eventgrid 'core/integration/eventgrid.bicep' = {

output SOURCE_STORAGE_ACCOUNT_NAME string = storage[0].outputs.storageAccountName

// Durable Task Scheduler
module dts 'core/durable_task/dts.bicep' = {
scope: resourceGroup
name: 'dtsResource'
params: {
name: dtsResourceName
taskhubname: taskHubResourceName
location: dtsLocation
tags: tags
ipAllowlist: [
'0.0.0.0/0'
]
skuName: dtsSkuName
skuCapacity: dtsCapacity
}
}

// Durable Task Data Contributor role ID
var dtsRoleDefinitionId = '0ad04412-c4d5-4796-b79c-f76d14c8d402'

// Allow access from function app to DTS using user assigned managed identity
module dtsRoleAssignment 'core/durable_task/dts-access.bicep' = {
name: 'dtsRoleAssignment'
scope: resourceGroup
params: {
roleDefinitionID: dtsRoleDefinitionId
principalID: userAssignedIdentity.outputs.identityPrincipalId
principalType: 'ServicePrincipal'
dtsName: dts.outputs.dts_NAME
}
}

// Allow the deployer identity to access the DTS dashboard
module dtsDashboardRoleAssignment 'core/durable_task/dts-access.bicep' = if (!empty(principalId)) {
name: 'dtsDashboardRoleAssignment'
scope: resourceGroup
params: {
roleDefinitionID: dtsRoleDefinitionId
principalID: principalId
principalType: 'User'
dtsName: dts.outputs.dts_NAME
}
}

output RESOURCE_GROUP_NAME string = resourceGroup.name
output SYSTEM_TOPIC_NAME string = eventgrid.outputs.systemTopicName
output FUNCTION_APP_NAME string = functionAppName
Expand Down
12 changes: 9 additions & 3 deletions src/host.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
{
"version": "2.0",
"logging": {
"logLevel": {
"DurableTask.AzureManagedBackend": "Information"
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
},
"extensions": {
"durableTask": {
"tracing": {
"DistributedTracingEnabled": true,
"Version": "V2"
"hubName": "%TASKHUB_NAME%",
"storageProvider": {
"type": "azureManaged",
"connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# DO NOT include azure-functions-worker in this file
# The Python Worker is managed by Azure Functions platform
# Manually managing azure-functions-worker may cause unexpected issues
aiohappyeyeballs==2.4.4
aiohttp==3.11.11
aiosignal==1.3.2
aiohappyeyeballs==2.6.1
aiohttp==3.13.5
aiosignal==1.4.0
annotated-types==0.7.0
anyio==4.8.0
async-timeout==5.0.1
Expand All @@ -13,7 +13,7 @@ azure-ai-documentintelligence==1.0.0b4
azure-common==1.1.28
azure-core==1.32.0
azure-functions==1.21.3
azure-functions-durable==1.2.10
azure-functions-durable==1.5.0
azure-identity==1.19.0
azure-search-documents==11.6.0b6
azure-storage-blob==12.24.0
Expand Down
Loading