Azure Service Bus Autoforwarding
Autoforwarding is an Azure Service Bus feature that automatically routes messages from a source queue or subscription to a target queue or topic within the same namespace. This helps decouple publishers and consumers and makes it much easier to route messages.
Why Use Autoforwarding?
Autoforwarding is commonly used to build clean and manageable messaging systems:
1. Queue Ownership vs. Topic Dependency
In large systems, different development teams often need to consume messages from a shared topic. Reading directly from topic subscriptions can cause coordination problems because a subscription is strictly dependent on its parent topic. If you consume directly from a subscription, you are bound to the parent topic's lifecycle, access policies, and management rules, meaning you don't have full control.
By using autoforwarding, you can configure the subscription to forward all incoming messages to a standalone queue. A queue is a first-class resource that your team can own completely. You can independently manage its settings (such as lock durations, time-to-live, and dead-letter policies), permissions, and scaling without being dependent on the parent topic or affecting other subscribers.
2. Message Consolidation
You can consolidate messages from multiple subscriptions or queues into a single processing queue. Instead of configuring consumer applications to poll several distinct endpoints, they only need to poll a single target queue that you own, simplifying how you scale and deploy your apps.
3. Decoupling Publishers and Consumers
Publishers only need to know about a generic entry point topic. Autoforwarding handles sending those messages to the correct queues behind the scenes, shielding publishers from any changes to how consumers process messages.
Core Mechanics and Limitations
When working with autoforwarding, keep these key behaviors in mind:
- Atomic Transfer: The move from the source entity to the target entity happens atomically inside the broker. A message is not removed from the source until it has been successfully written to the destination.
- Hop Count Limits: Azure Service Bus enforces a limit of 4 hops for autoforwarded messages. If a message exceeds 4 consecutive forwarding steps, it is dead-lettered to prevent infinite routing loops.
- Dead-Letter Destination: If a target queue is full, disabled, or deleted when a message is forwarded, the message goes to the transfer dead-letter queue of the source entity, at the path
$Transfer/$DeadLetterQueue, not to the destination's dead-letter queue, and not to the source's ordinary one. - Same Namespace Only: A forwarding destination must live in the same namespace as its source. Moving messages between namespaces requires a relay of your own, or Azure Functions / Logic Apps.
- Each Hop Is Billed: A forwarded message is charged as an additional operation, because it is a receive and a send. A four-hop chain costs roughly four times a direct delivery.
Configuring Auto-Forwarding
Forwarding is a property of the source entity, set when it is created or updated, not a separate resource. Two independent properties control it:
| Property | Effect |
|---|---|
forwardTo |
Active messages arriving at this entity are moved to the named destination instead of waiting for a consumer. |
forwardDeadLetteredMessagesTo |
Messages that dead-letter here are moved to the named destination. This is how you build a single consolidated dead-letter queue for a whole namespace. |
The second is easy to overlook and often the more useful of the two. Rather than checking a dead-letter queue on every subscription, point them all at one failed-messages queue and monitor that instead.
In Bicep
resource ordersSub 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2021-11-01' = {
name: 'orders-processor'
parent: ordersTopic
properties: {
forwardTo: 'orders-work-queue'
forwardDeadLetteredMessagesTo: 'failed-messages'
maxDeliveryCount: 5
}
}
With the Azure CLI
az servicebus topic subscription update \
--resource-group my-rg \
--namespace-name my-namespace \
--topic-name orders \
--name orders-processor \
--forward-to orders-work-queue
A permissions detail that causes most first-time failures: the identity configuring the forwarding needs Send rights on the destination entity, not just Manage on the source. If the destination is in a different resource group or was created by another team, this is usually the missing piece.
Where It Bites
Messages vanish from the source
Forwarding is invisible from the consuming side. A queue with forwardTo set will always look empty, because messages are moved the instant they arrive. If a queue reports zero depth while publishers insist they are sending, check whether it forwards somewhere.
Chains are hard to see
Nothing in the portal draws the graph. A message can traverse four entities before reaching a consumer, and each hop is only visible by inspecting that entity's own properties. Document your chains, or you will end up reconstructing them under pressure during an incident.
Deleting a destination breaks the source
Removing a forwarding target does not clear the source's forwardTo. The source keeps forwarding into a void, and every message lands in the transfer dead-letter queue. Always clear the property before deleting the destination.
Transactions do not span the hop
Sending to an entity that auto-forwards is not supported inside a transaction scope. If your publisher relies on transactional sends, forward from a subscription rather than from the queue it writes to.
Frequently Asked Questions
Can I auto-forward between two different namespaces?
No. Source and destination must be in the same Service Bus namespace. Crossing namespaces requires something that receives and re-sends, such as an Azure Function, a Logic App, or your own relay process.
How many times can a message be forwarded?
Up to four hops. Beyond that the message is dead-lettered rather than forwarded again, which prevents a misconfigured loop from cycling forever.
Where do messages go if the destination is full or deleted?
To the transfer dead-letter queue of the source entity, at $Transfer/$DeadLetterQueue, not the destination's dead-letter queue and not the source's ordinary one. This is the single most common reason auto-forwarded messages appear to disappear.
Does auto-forwarding preserve message properties?
Yes. The message is moved intact, so body, application properties and system properties all survive the hop. The enqueued time reflects arrival at each entity, so it will differ from the original.
How do I tell whether a queue is forwarding?
Check the entity's forwardTo property. It is set on the source, so a queue that appears permanently empty is worth inspecting. An empty queue and a forwarding queue look identical from the outside.