3 minute read

When we need to communicate with on-premise environment, we usually utilize Service Bus. Up until now, most of the messages have been fire and forget, however recently, we had a situation where we needed to perform two way communication (basically request and response) to basically perform some calculation on top of on-prem data and return the data.

There are actually multiple solutions to this problem - we could go with on-premises data gateway (which would most-likely require running a web server at customer’s environment - and handling auth etc.), we could go with Azure Relay (but that requires calling it via HTTP from Power Automate, and it’s Service Bus behind the scenes anyways), so we decided to go with Service Bus.

Service Bus supports sessions which can be used for implementing a request and response pattern.

The implementation is really simple. Everything starts when a Power Automate gets triggered with some data payload (from frontend in our case). Next, we generate the session’s ID by using expression guid() - we persist it in a variable, because we will also need it later.

In Service Bus, we create two topics - request with on-premise subscriber and response with Power Automate subscriber (make sure to enable sessions).

Next we send the message to the queue/topic and fill Session Id with the ID generated earlier. We also provide the message’s payload and any other things we need. This will send the message.

You then have a simple listener created via CreateProcessor:

public async Task DoWork(CancellationToken stoppingToken)
{
    _serviceBusProcessor = _serviceBusClient.CreateProcessor("<topic>", "<subscription>", new ServiceBusProcessorOptions
    {
        AutoCompleteMessages = true,
        MaxConcurrentCalls = 1,
        MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(1)
    });
    _serviceBusProcessor.ProcessMessageAsync += MessageHandler;
    _serviceBusProcessor.ProcessErrorAsync += ErrorHandler;

    await _serviceBusProcessor.StartProcessingAsync(stoppingToken);

    while (!stoppingToken.IsCancellationRequested)
    {
        await Task.Delay(10000, stoppingToken);
    }
}

And in the MessageHandler:

private async Task MessageHandler(ProcessMessageEventArgs args)
{
    var sessionId = args.Message.SessionId;
    var message = args.Message.Body.ToObjectFromJson<Message>();

    try
    {
        // ... do your processing
        await _serviceBusSender.SendMessageAsync(new ServiceBusMessage
        {
            SessionId = sessionId,
            ReplyToSessionId = sessionId,
            Body = BinaryData.FromObjectAsJson(new Message
            {
                // Your response data...
            }),
        });
        await args.CompleteMessageAsync(args.Message);
    }
    // You should do proper error handling...
    catch (Exception ex)
    {
        await args.DeadLetterMessageAsync(args.Message, ex.Message);
    }

}

Next in Power Automate, you will add a trigger into the flow’s body (yes, that’s right, you can use triggers mid-flow). In this case, we use When a message is received in a topic subscription (peek-lock) trigger. The important thing is to provide the Session id parameter, so the flow will get triggered only on the reply to the initial message. When you receive the message, make sure to complete the message since the trigger is only peek-lock (the auto-complete doesn’t support sessions).

This way, the flow pauses and just resumes running when a response arrives and thanks to it, you don’t need to manually store flow’s state and restore it.

If you are using HTTP Request trigger / Response action (or a connector based on these), remember the maximum execution time is 2 minutes, so if the execution time is longer, make sure to enable the asynchronous response pattern and properly consume it on the client.

To submit comments, go to GitHub Discussions.