diff --git a/infra/abbreviations.json b/infra/abbreviations.json index a4fc9df..d7677a1 100644 --- a/infra/abbreviations.json +++ b/infra/abbreviations.json @@ -37,6 +37,8 @@ "devicesProvisioningServices": "provs-", "devicesProvisioningServicesCertificates": "pcert-", "documentDBDatabaseAccounts": "cosmos-", + "durableTaskSchedulers": "dts-", + "durableTaskHubs": "th-", "eventGridDomains": "evgd-", "eventGridDomainsTopics": "evgt-", "eventGridEventSubscriptions": "evgs-", diff --git a/infra/app/dts-Access.bicep b/infra/app/dts-Access.bicep new file mode 100644 index 0000000..1c435b9 --- /dev/null +++ b/infra/app/dts-Access.bicep @@ -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 + } +} diff --git a/infra/app/dts.bicep b/infra/app/dts.bicep new file mode 100644 index 0000000..e3c77b7 --- /dev/null +++ b/infra/app/dts.bicep @@ -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 == 'Consumption' ? { + name: skuName + } : { + name: skuName + capacity: skuCapacity + } + } +} + +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 diff --git a/infra/app/processor.bicep b/infra/app/processor.bicep index 17ebd2b..b34e529 100644 --- a/infra/app/processor.bicep +++ b/infra/app/processor.bicep @@ -13,9 +13,17 @@ param instanceMemoryMB int = 2048 param maximumInstanceCount int = 100 param identityId string = '' param identityClientId string = '' +param dtsURL string = '' +param taskHubName string = '' var applicationInsightsIdentity = 'ClientId=${identityClientId};Authorization=AAD' +// Durable Task Scheduler settings +var dtsSettings = !empty(dtsURL) ? { + DURABLE_TASK_SCHEDULER_CONNECTION_STRING: 'Endpoint=${dtsURL};Authentication=ManagedIdentity;ClientID=${identityClientId}' + TASKHUB_NAME: taskHubName +} : {} + module processor '../core/host/functions-flexconsumption.bicep' = { name: '${serviceName}-functions-module' params: { @@ -28,7 +36,8 @@ module processor '../core/host/functions-flexconsumption.bicep' = { { AzureWebJobsStorage__clientId : identityClientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING: applicationInsightsIdentity - }) + }, + dtsSettings) applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId runtimeName: runtimeName diff --git a/infra/main.bicep b/infra/main.bicep index 7ba2e25..0c94d4a 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -24,10 +24,19 @@ param resourceGroupName string = '' param storageAccountName string = '' param vNetName string = '' param disableLocalAuth bool = true +param dtsName string = '' +param taskHubName string = '' +param dtsLocation string = location +param dtsSkuName string = 'Consumption' +param dtsCapacity int = 1 +@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 = deployer().objectId var abbrs = loadJsonContent('./abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName } +var dtsResourceName = !empty(dtsName) ? dtsName : '${abbrs.durableTaskSchedulers}${resourceToken}' +var taskHubResourceName = !empty(taskHubName) ? taskHubName : '${abbrs.durableTaskHubs}${resourceToken}' // Organize resources in a resource group resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { @@ -65,6 +74,8 @@ module processor './app/processor.bicep' = { appSettings: { } virtualNetworkSubnetId: serviceVirtualNetwork.outputs.appSubnetID + dtsURL: dts.outputs.dts_URL + taskHubName: dts.outputs.TASKHUB_NAME } } @@ -191,6 +202,50 @@ module appInsightsRoleAssignmentApi './core/monitor/appinsights-access.bicep' = } } +// Durable Task Scheduler +module dts './app/dts.bicep' = { + scope: rg + 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 'app/dts-Access.bicep' = { + name: 'dtsRoleAssignment' + scope: rg + params: { + roleDefinitionID: dtsRoleDefinitionId + principalID: processorUserAssignedIdentity.outputs.identityPrincipalId + principalType: 'ServicePrincipal' + dtsName: dts.outputs.dts_NAME + } +} + +// Allow the deployer identity to access the DTS dashboard +module dtsDashboardRoleAssignment 'app/dts-Access.bicep' = { + name: 'dtsDashboardRoleAssignment' + scope: rg + params: { + roleDefinitionID: dtsRoleDefinitionId + principalID: principalId + principalType: 'User' + dtsName: dts.outputs.dts_NAME + } +} + // App outputs output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString output AZURE_LOCATION string = location diff --git a/order_processor/host.json b/order_processor/host.json index 9df9136..f43cb7c 100644 --- a/order_processor/host.json +++ b/order_processor/host.json @@ -5,6 +5,19 @@ "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" + }, + "enableLiveMetricsFilters": true + }, + "logLevel": { + "DurableTask.AzureManagedBackend": "Information" + } + }, + "extensions": { + "durableTask": { + "hubName": "%TASKHUB_NAME%", + "storageProvider": { + "type": "azureManaged", + "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING" } } },