Azure Functions can process messages from Azure Service Bus queues or topics, acting as a trigger for your serverless code.

Here’s a Service Bus triggered Azure Function in C#:

using System;
using System.Text;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

public static class ServiceBusQueueTriggerFunction
{
    [FunctionName("ServiceBusQueueProcessor")]
    public static async Task Run(
        [ServiceBusTrigger("myqueue", Connection = "ServiceBusConnection")]
        ServiceBusReceivedMessage message,
        ILogger log)
    {
        log.LogInformation($"C# ServiceBus queue trigger function processed message: {message.MessageId}");
        log.LogInformation($"Message body: {Encoding.UTF8.GetString(message.Body)}");

        // Your processing logic here
        // For example, parse the message body, interact with other services, etc.

        // If processing fails, you can throw an exception.
        // The message will be automatically retried based on your Service Bus queue's retry policy.
        // If you want to explicitly abandon the message for immediate retry (not waiting for the retry policy),
        // you can use await message.AbandonAsync();
        // If you want to dead-letter the message immediately, you can use await message.DeadLetterAsync("ProcessingError");

        // If processing is successful, the message is automatically completed and removed from the queue.
    }
}

And here’s the ServiceBusConnection setting in your local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true", // Or your actual storage connection string
    "ServiceBusConnection": "Endpoint=sb://your-service-bus-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=YOUR_SHARED_ACCESS_KEY"
  }
}

This function is triggered by a message arriving in the Azure Service Bus queue named myqueue. The ServiceBusConnection app setting provides the connection string to your Service Bus namespace. The ServiceBusReceivedMessage object contains the message content and metadata.

The primary problem this pattern solves is enabling event-driven architectures where your business logic reacts to events published to Service Bus. Instead of constantly polling a queue or implementing complex background workers, you simply deploy an Azure Function. The Azure Functions host manages the lifecycle, scaling, and message delivery guarantees for you.

Internally, the Azure Functions Service Bus trigger binding works by creating a ServiceBusClient using the provided connection string. It then subscribes to the specified queue or topic subscription. When a message arrives, the binding deserializes it into the ServiceBusReceivedMessage type (or a custom type if you configure it) and passes it to your function. Upon successful execution of your function (i.e., no unhandled exceptions), the binding automatically completes the message, ensuring it’s removed from the queue. If an exception occurs, the binding will abandon the message, and Service Bus will redeliver it according to its configured retry policy.

The key levers you control are:

  • Queue/Topic Name: Specified in the ServiceBusTrigger attribute (e.g., "myqueue").

  • Connection String: The Connection property in the attribute points to an app setting containing your Service Bus connection string.

  • Message Handling: Within your function code, you decide how to process the message. You can explicitly abandon or dead-letter messages if needed for advanced error handling, but usually, letting exceptions drive retries is sufficient.

  • Max Concurrent Calls: In your host.json file, you can configure serviceBus settings like maxConcurrentCalls to control how many messages your function processes in parallel. For example, to process up to 10 messages concurrently:

    {
      "version": "2.0",
      "extensions": {
        "serviceBus": {
          "maxConcurrentCalls": 10
        }
      }
    }
    
  • Prefetch Count: Also in host.json, you can set prefetchCount (default is 0, meaning no prefetching). Setting this to a value greater than 0, like 100, allows the trigger to fetch messages in advance, potentially improving throughput by reducing latency between message arrival and function execution.

{
  "version": "2.0",
  "extensions": {
    "serviceBus": {
      "maxConcurrentCalls": 10,
      "prefetchCount": 100
    }
  }
}

When you configure prefetchCount, the Azure Functions host will attempt to fetch up to that many messages from the Service Bus and hold them locally. As your function processes messages, new ones are automatically fetched to maintain the prefetch buffer. This can significantly improve the performance of high-throughput scenarios because the function doesn’t have to wait for each message to be fetched individually from Service Bus before it can start processing. The Service Bus SDK handles the batching and delivery of these prefetched messages to the trigger binding.

The next concept to explore is processing messages from Service Bus Topics using subscriptions.

Want structured learning?

Take the full Azure-functions course →