Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Asynchronous Request-Reply pattern

Decouple backend processing from a frontend host, where backend processing needs to be asynchronous, but the frontend still needs a clear response.

For more information about this pattern, see Asynchronous Request-Reply pattern on the Azure Architecture Center.

Data flow of the async request-reply pattern

The implementation uses a managed identity to control access to your storage accounts and Service Bus in the code, which is highly recommended wherever possible as a security best practice.

The reference implementation was uses the Azure Functions Flex Consumption. Flex Consumption is a Linux-based serverless hosting plan that extends the Consumption billing model with additional enterprise capabilities: private networking (virtual network integration), selectable instance sizes, and faster/larger scale-out over other plans.

The typical way to generate a SAS token in code requires the storage account key. In this scenario, you won't have a storage account key, as that feature is distable in this implementation, so you'll need to find another way to generate the shared access signatures. To do that, we need to use an approach called user delegation SAS. By using a user delegation SAS, we can sign the signature with the Microsoft Entra ID credentials instead of the storage account key.

Deploying the sample

Prerequisites

Deploy the Azure resources

  1. Clone or download this repo.

  2. Navigate to the async-request-reply folder.

    cd async-request-reply
  3. Azure Login

    Our journey begins with logging into Azure. Use the command below:

    az login
    # Set the subscription
    az account set --subscription <subscription_id>
  4. Environment Setup

    We need to prepare our environment:

    LOCATION=eastus
    RESOURCEGROUP=rg-asyncrequestreply-${LOCATION}
  5. Create a resource group.

    az group create --name ${RESOURCEGROUP} --location ${LOCATION}
  6. Deploy the template. All the resources are going to be created on the resource group location.

    az deployment group create -g ${RESOURCEGROUP} -f deploy.bicep
  7. Wait for the deployment to complete.

Deploy the Functions app

  1. Navigate to the async-request-reply/src folder.

    cd src
  2. Deploy the app.

    FUNC_APP_NAME=$(az deployment group show -g ${RESOURCEGROUP} -n deploy --query properties.outputs.functionAppName.value -o tsv)
    
    func azure functionapp publish $FUNC_APP_NAME --dotnetIsolated

Try it out!

  1. Send an http request through the Async Processor Work Acceptor

    curl -X POST "https://${FUNC_APP_NAME}.azurewebsites.net/api/asyncprocessingworkacceptor" --header 'Content-Type: application/json' --header 'Accept: application/json' -k -i -d '{
       "id": "1234",
       "customername": "Contoso"
    }'

    The response will be something like:

     HTTP/1.1 202 Accepted
     Content-Length: 155
     Content-Type: application/json; charset=utf-8
     Date: Wed, 13 Dec 2023 20:18:55 GMT
     Location: https://<appservice-name>.azurewebsites.net/api/RequestStatus/<guid>

    Using a browser open the url from the Location field in the response. A file with the data you have sent will be downloaded.

    Note the app uses the WEBSITE_HOSTNAME environment variable. This environment variable is set automatically by the Azure App Service runtime environment. For more information, see Azure runtime environment

🧹 Clean up resources

Most of the Azure resources deployed in the prior steps will incur ongoing charges unless removed.

az group delete -n ${RESOURCEGROUP} -y

Running locally

You could open the solution with Visual Studio, then you need to create on the root local.settings.json

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "ServiceBusConnection__fullyQualifiedNamespace": "<yourData>",
    "DataStorage__blobServiceUri": "<yourData>"
  }
}

As far the implementation is using managed identity, you need to assign the role to your developer identity.

You need to add the following lines to the Bicep file to assign roles for Service Bus and Azure Storage permissions.

// Assign Role to allow sending messages to the Service Bus
resource serviceBusSenderRoleAssignmentUser 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(resourceGroup().id, functionApp.id, 'LocalUser', 'ServiceBusSenderRole')
  scope: serviceBusNamespace
  properties: {
    roleDefinitionId: senderServiceBusRole
    principalId: <your user object id>
    principalType: 'User'
  }
}

// Assign Role to allow receiving messages from the Service Bus
resource serviceBusReceiverRoleAssignmentUser 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(resourceGroup().id, functionApp.id, 'LocalUser', 'ServiceBusReceiverRole')
  scope: serviceBusNamespace
  properties: {
    roleDefinitionId: receiverServiceBusRole
    principalId: <your user object id>
    principalType: 'User'
  }
}

// Assign Role to allow Read, write, and delete Azure Storage containers and blobs. 
resource dataStorageBlobDataContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(resourceGroup().id, dataStorageAccount.id, 'LocalUser', 'StorageBlobDataContributorRole')
  scope: dataStorageAccount
  properties: {
    roleDefinitionId: storageBlobDataContributorRole
    principalId: <your user object id>
    principalType: 'User'
  }
}

Contributions

Please see our Contributor guide.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

With ❤️ from Azure Patterns & Practices, Azure Architecture Center.