From 29a0fb2e74d3e9ebdcc1f01b99132e843fd12071 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Fri, 19 Dec 2025 10:17:11 -0300 Subject: [PATCH 1/3] Addressing Pull request comment --- priority-queue/Readme.md | 73 +++------------ .../bicep/azure/azure-function-apps.bicep | 82 ---------------- priority-queue/bicep/main.bicep | 93 +++++++++++++------ priority-queue/bicep/{azure => }/sites.bicep | 0 4 files changed, 80 insertions(+), 168 deletions(-) delete mode 100644 priority-queue/bicep/azure/azure-function-apps.bicep rename priority-queue/bicep/{azure => }/sites.bicep (100%) diff --git a/priority-queue/Readme.md b/priority-queue/Readme.md index a5f5d707..32887321 100644 --- a/priority-queue/Readme.md +++ b/priority-queue/Readme.md @@ -4,8 +4,6 @@ This directory contains an example of the [Priority Queue pattern](https://learn This example demonstrates how priority queues can be implemented by using Service Bus topics and subscriptions. A time-triggered Azure Function is responsible for sending messages to a topic, each with a priority assigned. The receiving Azure Functions read messages from subscriptions that have the corresponding priority. -For local execution, the sample demonstrates the producer/consumer model, where each consumer processes only one type of message based on its priority. - In the Azure Deployment, the _PriorityQueueConsumerHigh_ Azure function could scale out to 200 instances, while the _PriorityQueueConsumerLow_ Azure function could scale out only to 40 instances. It simulates high priority messages being read from the queue more urgently than low priority messages. The Azure deployment also demonstrates operational aspects of applications running on Azure. Monitoring tools are essential to understand how the sample operates. Azure Functions in the solution **must** be configured to use the diagnostics mechanism. Otherwise, trace information generated by the example will not be visible. @@ -47,80 +45,37 @@ Install the prerequisites and follow the steps to deploy and run an example of t az group create -n $RESOURCE_GROUP_NAME -l $LOCATION ``` -1. Deploy the supporting Azure resources. - - ```bash - CURRENT_USER_OBJECT_ID=$(az ad signed-in-user show -o tsv --query id) - SERVICE_BUS_NAMESPACE_NAME="sbns-priority-queue-$(LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | fold -w 7 | head -n 1)" - - # This takes about two minutes - az deployment group create -n deploy-priority-queue -f bicep/main.bicep -g $RESOURCE_GROUP_NAME -p queueNamespaces=$SERVICE_BUS_NAMESPACE_NAME principalId=$CURRENT_USER_OBJECT_ID - ``` - -1. Configure the samples to use the created Azure resources. - - ```bash - # Retrieve the primary connection string for the Service Bus namespace. - SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE="${SERVICE_BUS_NAMESPACE_NAME}.servicebus.windows.net" - - sed "s|{SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|${SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|g" ./PriorityQueueSender/local.settings.template.json > ./PriorityQueueSender/local.settings.json - sed "s|{SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|${SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|g" ./PriorityQueueConsumerHigh/local.settings.template.json > ./PriorityQueueConsumerHigh/local.settings.json - sed "s|{SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|${SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|g" ./PriorityQueueConsumerLow/local.settings.template.json > ./PriorityQueueConsumerLow/local.settings.json - ``` - -1. [Run Azurite](https://learn.microsoft.com/azure/storage/common/storage-use-azurite#run-azurite) blob storage emulation service. +## Deploy the example to Azure - > Azure Functions require an Azure Storage account as a backing resource. When running locally, you can use Azurite, the local storage emulator, to fulfill this requirement. -Alternatively, you may configure the AzureWebJobsStorage setting to use a real Azure Storage account if preferred. - -1. Launch the Azure Function PriorityQueueSender to generate Low and High messages. - - ```bash - cd ./PriorityQueueSender - func start - ``` - -1. In a new terminal, launch the Azure Function PriorityQueueConsumerLow to consume messages. - - ```bash - cd ./PriorityQueueConsumerLow - func start -p 15000 - ``` - - > Please note: For demo purposes, the sample application will write content to the screen. - -1. In a new terminal, launch the Azure Function PriorityQueueConsumerHigh to consume messages. - - ```bash - cd ./PriorityQueueConsumerHigh - func start -p 15001 - ``` - - > Please note: For demo purposes, the sample application will write content to the screen. - -## Deploy the example to Azure (Optional) - -This Bicep template sets up the core infrastructure for a priority-based message processing system using Azure Functions. It creates a secure Storage Account, an Application Insights instance for monitoring, and uses a previously created Service Bus namespace to enable communication between the sender and consumer functions. The deployment includes three Azure Function Apps: one sender and two consumers, each with different scaling limits to simulate message prioritization. +This Bicep template sets up the core infrastructure for a priority-based message processing system using Azure Functions. It creates a secure Storage Account, an Application Insights instance for monitoring, and uses a Service Bus namespace to enable communication between the sender and consumer functions. The deployment includes three Azure Function Apps: one sender and two consumers, each with different scaling limits to simulate message prioritization. The funcPriorityQueueConsumerHigh function can scale out to 200 instances, allowing it to process high-priority messages quickly. The funcPriorityQueueConsumerLow function is limited to 40 instances, handling lower-priority messages with less urgency. All function apps use the FlexConsumption plan and are connected to Application Insights for diagnostics and monitoring. Role assignments are configured to securely grant access to the Service Bus and Storage resources using managed identities. All Azure Function Apps share the same Storage Account and Application Insights instance (It is essential to understand how the sample operates), which centralizes observability and logging. ```bash + SERVICE_BUS_NAMESPACE_NAME="sbns-priority-queue-$(LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | fold -w 7 | head -n 1)" # This takes about three minutes - az deployment group create -n deploy-priority-queue-sites -f bicep/azure/azure-function-apps.bicep -g $RESOURCE_GROUP_NAME -p serviceBusNamespaceName=$SERVICE_BUS_NAMESPACE_NAME + az deployment group create -n deploy-priority-queue-sites -f bicep/main.bicep -g $RESOURCE_GROUP_NAME -p serviceBusNamespaceName=$SERVICE_BUS_NAMESPACE_NAME ``` After deploying the infrastructure, you need to publish each Azure Function to its corresponding Function App using Azure Functions Core Tools: ```bash + # Retrieve the primary connection string for the Service Bus namespace. + SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE="${SERVICE_BUS_NAMESPACE_NAME}.servicebus.windows.net" + + sed "s|{SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|${SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|g" ./PriorityQueueSender/local.settings.template.json > ./PriorityQueueSender/local.settings.json + sed "s|{SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|${SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|g" ./PriorityQueueConsumerHigh/local.settings.template.json > ./PriorityQueueConsumerHigh/local.settings.json + sed "s|{SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|${SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|g" ./PriorityQueueConsumerLow/local.settings.template.json > ./PriorityQueueConsumerLow/local.settings.json + cd ./PriorityQueueSender - func azure functionapp publish funcPriorityQueueSender + func azure functionapp publish funcPriorityQueueSender --dotnet-isolated cd .. cd ./PriorityQueueConsumerLow - func azure functionapp publish funcPriorityQueueConsumerLow + func azure functionapp publish funcPriorityQueueConsumerLow --dotnet-isolated cd .. cd ./PriorityQueueConsumerHigh - func azure functionapp publish funcPriorityQueueConsumerHigh + func azure functionapp publish funcPriorityQueueConsumerHigh --dotnet-isolated cd .. ``` diff --git a/priority-queue/bicep/azure/azure-function-apps.bicep b/priority-queue/bicep/azure/azure-function-apps.bicep deleted file mode 100644 index 7ff9fe27..00000000 --- a/priority-queue/bicep/azure/azure-function-apps.bicep +++ /dev/null @@ -1,82 +0,0 @@ -targetScope = 'resourceGroup' - -@minLength(5) -@description('Location of the resources. Defaults to resource group location.') -param location string = resourceGroup().location - -@description('The name of the existing Service Bus namespace used for message queuing between the sender and consumer functions.') -param serviceBusNamespaceName string - -@description('Defines the name of the Storage Account used by the Function Apps. It uses a unique string based on the resource group ID to ensure global uniqueness.') -param storageAccountName string = 'st${uniqueString(resourceGroup().id)}' - -@description('Sets the name of the Application Insights resource for monitoring and diagnostics. Like the storage account, it uses a unique string based on the resource group ID.') -param appInsightsName string = 'ai${uniqueString(resourceGroup().id)}' - -var senderRoleId = '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' //Azure Service Bus Data Sender -var receiverRoleId = '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' //Azure Service Bus Data Receiver - -resource storageAccount 'Microsoft.Storage/storageAccounts@2025-01-01' = { - name: storageAccountName - location: location - sku: { - name: 'Standard_LRS' - } - kind: 'StorageV2' - properties: { - defaultToOAuthAuthentication: true - publicNetworkAccess: 'Enabled' - allowCrossTenantReplication: false - minimumTlsVersion: 'TLS1_2' - allowBlobPublicAccess: false - allowSharedKeyAccess: false - networkAcls: { - bypass: 'AzureServices' - virtualNetworkRules: [] - ipRules: [] - defaultAction: 'Allow' - } - supportsHttpsTrafficOnly: true - encryption: { - services: { - file: { - keyType: 'Account' - enabled: true - } - blob: { - keyType: 'Account' - enabled: true - } - } - keySource: 'Microsoft.Storage' - } - } -} - -resource appInsights 'Microsoft.Insights/components@2020-02-02' = { - name: appInsightsName - location: location - kind: 'web' - properties: { - Application_Type: 'web' - } -} - -module functionApp './sites.bicep' = [ - for name in [ - 'funcPriorityQueueSender' - 'funcPriorityQueueConsumerLow' - 'funcPriorityQueueConsumerHigh' - ]: { - name: name - params: { - location: location - functionAppName: name - storageAccountName: storageAccount.name - serviceBusNamespaceName: serviceBusNamespaceName - roleId: name == 'funcPriorityQueueSender' ? senderRoleId : receiverRoleId - appInsightsName: appInsights.name - scaleUp: name == 'funcPriorityQueueConsumerHigh' ? 200 : 40 - } - } -] diff --git a/priority-queue/bicep/main.bicep b/priority-queue/bicep/main.bicep index fc0b8636..e23239c7 100644 --- a/priority-queue/bicep/main.bicep +++ b/priority-queue/bicep/main.bicep @@ -6,25 +6,21 @@ param location string = resourceGroup().location @minLength(15) @description('Service Bus Namespace Name.') -param queueNamespaces string +param serviceBusNamespaceName string -@minLength(36) -@description('The principal ID used to run the Azure Functions. In Azure, this should be the managed identity (system-assigned or user-assigned) of the Azure Function. When running locally, it should be your user identity.') -param principalId string +@description('Defines the name of the Storage Account used by the Function Apps. It uses a unique string based on the resource group ID to ensure global uniqueness.') +param storageAccountName string = 'st${uniqueString(resourceGroup().id)}' + +@description('Sets the name of the Application Insights resource for monitoring and diagnostics. Like the storage account, it uses a unique string based on the resource group ID.') +param appInsightsName string = 'ai${uniqueString(resourceGroup().id)}' var logAnalyticsName = 'loganalytics-${uniqueString(subscription().subscriptionId, resourceGroup().id)}' -var senderServiceBusRole = subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' -) // Azure Service Bus Data Sender -var receiverServiceBusRole = subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' -) // Azure Service Bus Data Receiver +var senderRoleId = '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' //Azure Service Bus Data Sender +var receiverRoleId = '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' //Azure Service Bus Data Receiver resource queueNamespacesResource 'Microsoft.ServiceBus/namespaces@2025-05-01-preview' = { - name: queueNamespaces + name: serviceBusNamespaceName location: location sku: { name: 'Standard' @@ -186,24 +182,67 @@ resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-pr } } -// Assign Role to allow sending messages to the Service Bus -resource serviceBusSenderRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(resourceGroup().id, principalId, 'ServiceBusSenderRole') - scope: queueNamespacesResource +resource storageAccount 'Microsoft.Storage/storageAccounts@2025-01-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' properties: { - roleDefinitionId: senderServiceBusRole - principalId: principalId - principalType: 'User' // 'ServicePrincipal' if this was App Service with a managed identity + defaultToOAuthAuthentication: true + publicNetworkAccess: 'Enabled' + allowCrossTenantReplication: false + minimumTlsVersion: 'TLS1_2' + allowBlobPublicAccess: false + allowSharedKeyAccess: false + networkAcls: { + bypass: 'AzureServices' + virtualNetworkRules: [] + ipRules: [] + defaultAction: 'Allow' + } + supportsHttpsTrafficOnly: true + encryption: { + services: { + file: { + keyType: 'Account' + enabled: true + } + blob: { + keyType: 'Account' + enabled: true + } + } + keySource: 'Microsoft.Storage' + } } } -// Assign Role to allow receiving messages from the Service Bus -resource serviceBusReceiverRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(resourceGroup().id, principalId, 'ServiceBusReceiverRole') - scope: queueNamespacesResource +resource appInsights 'Microsoft.Insights/components@2020-02-02' = { + name: appInsightsName + location: location + kind: 'web' properties: { - roleDefinitionId: receiverServiceBusRole - principalId: principalId - principalType: 'User' // 'ServicePrincipal' if this was App Service with a managed identity + Application_Type: 'web' } } + +module functionApp './sites.bicep' = [ + for name in [ + 'funcPriorityQueueSender' + 'funcPriorityQueueConsumerLow' + 'funcPriorityQueueConsumerHigh' + ]: { + name: name + params: { + location: location + functionAppName: name + storageAccountName: storageAccount.name + serviceBusNamespaceName: serviceBusNamespaceName + roleId: name == 'funcPriorityQueueSender' ? senderRoleId : receiverRoleId + appInsightsName: appInsights.name + scaleUp: name == 'funcPriorityQueueConsumerHigh' ? 200 : 40 + } + } +] diff --git a/priority-queue/bicep/azure/sites.bicep b/priority-queue/bicep/sites.bicep similarity index 100% rename from priority-queue/bicep/azure/sites.bicep rename to priority-queue/bicep/sites.bicep From 04243fffd8fb2d74d3d0f8bd681c87b81a59e0ca Mon Sep 17 00:00:00 2001 From: "Federico Arambarri (CLARIUS CONSULTING SA)" Date: Fri, 19 Dec 2025 11:21:01 -0300 Subject: [PATCH 2/3] Update priority-queue/bicep/main.bicep Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- priority-queue/bicep/main.bicep | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/priority-queue/bicep/main.bicep b/priority-queue/bicep/main.bicep index e23239c7..93ee1f32 100644 --- a/priority-queue/bicep/main.bicep +++ b/priority-queue/bicep/main.bicep @@ -16,8 +16,8 @@ param appInsightsName string = 'ai${uniqueString(resourceGroup().id)}' var logAnalyticsName = 'loganalytics-${uniqueString(subscription().subscriptionId, resourceGroup().id)}' -var senderRoleId = '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' //Azure Service Bus Data Sender -var receiverRoleId = '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' //Azure Service Bus Data Receiver +var senderRoleId = '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' // Azure Service Bus Data Sender +var receiverRoleId = '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' // Azure Service Bus Data Receiver resource queueNamespacesResource 'Microsoft.ServiceBus/namespaces@2025-05-01-preview' = { name: serviceBusNamespaceName From 28ecf6f756d81ac124ad9b871cd38825dd4e8aea Mon Sep 17 00:00:00 2001 From: "Federico Arambarri (CLARIUS CONSULTING SA)" Date: Fri, 19 Dec 2025 11:21:22 -0300 Subject: [PATCH 3/3] Update priority-queue/Readme.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- priority-queue/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priority-queue/Readme.md b/priority-queue/Readme.md index 32887321..997c8f73 100644 --- a/priority-queue/Readme.md +++ b/priority-queue/Readme.md @@ -59,7 +59,7 @@ All Azure Function Apps share the same Storage Account and Application Insights After deploying the infrastructure, you need to publish each Azure Function to its corresponding Function App using Azure Functions Core Tools: ```bash - # Retrieve the primary connection string for the Service Bus namespace. + # Construct the fully qualified namespace (hostname) for the Service Bus. SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE="${SERVICE_BUS_NAMESPACE_NAME}.servicebus.windows.net" sed "s|{SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|${SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE}|g" ./PriorityQueueSender/local.settings.template.json > ./PriorityQueueSender/local.settings.json