Azure Function : Read messages from queue and update record in dataverse

In the previous article, I described how a message can be sent from D365 to the Azure service bus queue.
Refer - send a message to azure service bus queue from Dataverse

The messages that are in the queue can be processed, we can either use power automate cloud flow or the azure function for the same. Since the prev article was about the power automate flow, let me create an azure function to read the message and post an update back to dataverse to update account's description

Creating an Azure function

Go to the visual studio and create a new project and look for azure function template

Configure the project

Provide additional information, choose the function, connection string setting name and queue name and create the project

  1. Install Packages
    The below packages have been added

  2. Updating the connection string in the local.settings.json

    - For the service bus connection string go to the Azure portal and under the service bus namespace click on the shared access policies and look for RootManageSharedAccessKey and pick the primary connection string
    - For the dataverse connection string, we need the service principal user to be created. The setup can be found here

    Register the app in azure active directory
    Manage application users in the power platform

  3. Writing the Azure Function
    - The project structure looks below

     [assembly: FunctionsStartup(typeof(FunctionReadQueueMessages.Startup))]
     namespace FunctionReadQueueMessages
     {
    
         public class Startup : FunctionsStartup
         {
             public override void Configure(IFunctionsHostBuilder builder)
             {
                 var configuration = builder.GetContext().Configuration;
    
                 builder.Services
                     .AddScoped((_)=>
                         new ServiceClient(configuration["dataverseconnectionstring"]));
             }
             public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
             {
                 builder.ConfigurationBuilder.AddEnvironmentVariables().Build();
             }
         }
    
         public class UpdateAccountFunction
         {
             private readonly ServiceClient _serviceClient;
             public UpdateAccountFunction(ServiceClient serviceClient)
             {
                 _serviceClient = serviceClient;
             }
    
             [FunctionName("UpdateAccountFunction")]
             public void Run([ServiceBusTrigger("account-queue", Connection = "servicebusconnectionstring")] string myQueueItem, ILogger log)
             {
                 try
                 {
                     log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
                     // Parse the incoming message
                     // For simplicity, assuming the message contains JSON with account ID and updated fields
                     var accountUpdateData = Newtonsoft.Json.JsonConvert.DeserializeObject<AccountUpdateData>(myQueueItem);
                     // Retrieve the account by its ID
                     Entity account = _serviceClient.Retrieve("account", accountUpdateData.Accountid, new Microsoft.Xrm.Sdk.Query.ColumnSet(true));
    
                     // Update account fields
                     account["description"] = $"Hello {accountUpdateData.Name} - Message processed in Azure queue successfully";
                     account["accountid"] = accountUpdateData.Accountid;
                     // Update the account record in Dynamics 365
                     _serviceClient.Update(account);
                 }
                 catch (Exception ex)
                 {
                     log.LogError(ex, "An error occurred");
                 }
             }
         }
     }
     // Define a class to hold account update data
     public class AccountUpdateData
     {
         public Guid Accountid { get; set; }
         public string Name { get; set; }
     }
    

    The above code reads messages from the queue and updates the account record in the dataverse with a description

    1. Building the Azure function
      we can run the function locally, test, debug and then publish it to the azure function.

    2. Publish the Azure Function

      Right-click on the Project and click publish, which will lead to the below screen

      Create a new function app or choose the existing one
      - Create new

      • Existing function app

        I will choose the existing one since I have added it already and then configure the app settings

        Add both servicebusconnectionstring and dataverseconnectionstring and click ok

        Publish

        1. Viewing the Azure function in the portal
          go to the Azure portal and search for the function app

        2. Testing the function

          Heading over to Dynamics 365 and adding a new account and save

        3. The account description is now updated

  4. Testing locally

For this, I will disable the Azure function in the portal

Now, send a message to the service bus queue from Dataverse

Added a new record, since the azure function is stopped, we can see the message in the service bus queue.

Running the azure function from visual studio

Notice, the function has picked up the message from the queue and processed it successfully

Summary :
This way azure functions and a service bus can be utilized to send messages from Dataverse and also to process those messages. This helps in offloading long-running code to Azure and keep the plugins lightweight while the azure can do the heavy lifting.

Other useful links
Temmy raharjo's blog