Architecture

Azure Service Bus SBMP Retirement: Migrate to Azure.Messaging.ServiceBus

SBMP and the WindowsAzure.ServiceBus and Microsoft.Azure.ServiceBus libraries reached end of support on 30 September 2026. Find affected code, switch to AMQP and move to Azure.Messaging.ServiceBus.

10 min read
On this page

On 30 September 2026 Microsoft ended support for the Service Bus Messaging Protocol (SBMP) and retired the WindowsAzure.ServiceBus, Microsoft.Azure.ServiceBus and com.microsoft.azure.servicebus libraries. Applications still on SBMP need to move to AMQP: the fastest fix for WindowsAzure.ServiceBus is to append ;TransportType=Amqp to the connection string, and the lasting fix is to migrate to Azure.Messaging.ServiceBus version 7 or later (or com.azure.messaging.servicebus for Java), which use AMQP by default. Azure Functions apps should move to version 5.x of the Service Bus extension, which is built on the new library.

Who this is for and what you will have at the end

This guide is for .NET and integration developers who own senders, receivers or Azure Functions that use Azure Service Bus and were written against the older SDKs. It's also for platform teams who need to find every affected workload before support questions turn into incidents.

At the end you will have:

  • An inventory method for finding legacy packages, namespaces and SBMP connection strings.
  • A stopgap that moves WindowsAzure.ServiceBus clients from SBMP to AMQP without a rewrite.
  • A mapping from legacy types to Azure.Messaging.ServiceBus, with working send and receive code.
  • The Azure Functions extension and host.json changes.
  • Network, authentication and verification steps.

What changed on 30 September 2026

Microsoft's retirement notice, repeated across the Service Bus documentation, says:

  • The libraries WindowsAzure.ServiceBus, Microsoft.Azure.ServiceBus and com.microsoft.azure.servicebus are retired because they don't conform to the Azure SDK guidelines.
  • Support for the SBMP protocol has ended, and you can no longer use the protocol after 30 September 2026.
  • The older libraries can still be used, but they no longer receive official support or updates from Microsoft.
Legacy libraryLanguageNotesReplacement
WindowsAzure.ServiceBus (Microsoft.ServiceBus.Messaging namespace).NET FrameworkUses SBMP by default; AMQP 1.0 available from version 2.1 via TransportTypeAzure.Messaging.ServiceBus
Microsoft.Azure.ServiceBus.NETOfficially deprecatedAzure.Messaging.ServiceBus
com.microsoft.azure.servicebusJavaRetiredcom.azure.messaging.servicebus
Functions Service Bus extension 4.x and earlier.NET FunctionsIn-process 4.x exposes Microsoft.Azure.ServiceBus typesExtension 5.x, which binds to Azure.Messaging.ServiceBus types

All supported Azure SDK client libraries for Service Bus (.NET, Java, JavaScript and TypeScript, Python, and the Java JMS 2.0 provider) use AMQP 1.0.

Prerequisites

  • Source code and build pipelines for every Service Bus sender and receiver.
  • The .NET SDK on a build machine to inventory packages.
  • Access to the Service Bus namespace in the Azure portal to check entities, dead-letter queues and access control.
  • Firewall rules you can change if AMQP ports are blocked.

Step 1: Find affected applications

Packages. In each solution, list package references including transitive ones. On .NET 9 SDK or earlier use the verb-first form; .NET 10 introduced dotnet package list:

# .NET 9 SDK and earlier
dotnet list package --include-transitive
 
# .NET 10 SDK and later, JSON output for scripting
dotnet package list --include-transitive --format json

Look for WindowsAzure.ServiceBus, Microsoft.Azure.ServiceBus and Microsoft.Azure.WebJobs.Extensions.ServiceBus versions below 5.

Code. Search for the namespaces Microsoft.ServiceBus.Messaging (WindowsAzure.ServiceBus) and Microsoft.Azure.ServiceBus, and for types such as QueueClient, TopicClient, SubscriptionClient, MessagingFactory, NamespaceManager and BrokeredMessage.

Configuration. Connection strings used by WindowsAzure.ServiceBus that don't contain TransportType=Amqp or TransportType=AmqpWebSockets are on SBMP.

Functions. A host.json with messageHandlerOptions, sessionHandlerOptions or batchOptions under serviceBus belongs to extension 4.x or earlier. If the app also uses the in-process model, note that Microsoft has announced the end of support for the in-process model on 10 November 2026, so plan the isolated worker move at the same time.

Record each application, its library, protocol, the entities it uses and whether it sends, receives or both.

Step 2: Stopgap for WindowsAzure.ServiceBus: switch to AMQP

If an application can't be rewritten immediately, stop it using SBMP. Append TransportType=Amqp to the connection string:

Endpoint=sb://contoso.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=<key>;TransportType=Amqp

If outbound 5671 and 5672 are blocked, use TransportType=AmqpWebSockets instead, which runs over 443. You can also set the TransportType option in the client constructors.

Microsoft documents a few behavioural differences when this library runs on AMQP:

  • MessagingFactorySettings.OperationTimeout is ignored.
  • MessageReceiver.Receive(TimeSpan.Zero) behaves like Receive(TimeSpan.FromSeconds(10)).
  • Messages can only be completed by lock token from the receiver that originally received them.

Message bodies also change shape. On SBMP the library serialises a BrokeredMessage with DataContractSerializer. On AMQP, bodies that map to AMQP primitive types (strings, numbers, byte[], Guid, DateTime, lists and maps of those) are encoded as AMQP values, and a Stream is sent as raw bytes; a custom .NET object is still serialised with DataContractSerializer and sent as binary data. Test that every consumer can still read what each sender produces, especially if some senders change before others.

This keeps traffic flowing, but the library is still retired. Treat it as a bridge to Step 3.

Step 3: Migrate code to Azure.Messaging.ServiceBus

The new library replaces the separate queue, topic and subscription clients with one top-level ServiceBusClient that owns a single AMQP connection and creates senders, receivers and processors.

LegacyAzure.Messaging.ServiceBus
QueueClient, TopicClient, MessageSender (send)ServiceBusSender from client.CreateSender()
QueueClient, SubscriptionClient, MessageReceiver (receive)ServiceBusReceiver or ServiceBusProcessor
RegisterMessageHandler with MessageHandlerOptionsServiceBusProcessor with ProcessMessageAsync and ProcessErrorAsync
RegisterSessionHandler, AcceptMessageSessionAsyncServiceBusSessionProcessor, AcceptNextSessionAsync, AcceptSessionAsync
Message, BrokeredMessageServiceBusMessage (send), ServiceBusReceivedMessage (receive)
ManagementClient, NamespaceManagerServiceBusAdministrationClient
Send-via / ViaPartitionKey transactionsServiceBusClientOptions.EnableCrossEntityTransactions = true
Building the dead-letter path yourselfServiceBusReceiverOptions { SubQueue = SubQueue.DeadLetter }
Setting DeadLetterReason in propertiesDeadLetterMessageAsync(message, reason, description)

Sending

await using ServiceBusClient client =
    new("contoso.servicebus.windows.net", new DefaultAzureCredential());
 
ServiceBusSender sender = client.CreateSender("orders");
 
using ServiceBusMessageBatch batch = await sender.CreateMessageBatchAsync();
foreach (Order order in orders)
{
    ServiceBusMessage message = new(JsonSerializer.Serialize(order))
    {
        MessageId = order.Id
    };
    if (!batch.TryAddMessage(message))
    {
        throw new InvalidOperationException($"Order {order.Id} is too large for the batch.");
    }
}
await sender.SendMessagesAsync(batch);

Set MessageId explicitly. BrokeredMessage constructors generated a GUID automatically; the new constructors leave it unset, which breaks duplicate detection if you relied on it. A string body is encoded as UTF-8.

Processing

ServiceBusProcessorOptions options = new()
{
    AutoCompleteMessages = false,
    MaxConcurrentCalls = 4
};
 
await using ServiceBusProcessor processor = client.CreateProcessor("orders", options);
 
processor.ProcessMessageAsync += async args =>
{
    string body = args.Message.Body.ToString();
    await HandleOrderAsync(body);
    await args.CompleteMessageAsync(args.Message);
};
 
processor.ProcessErrorAsync += args =>
{
    logger.LogError(args.Exception, "Error source: {Source}", args.ErrorSource);
    return Task.CompletedTask;
};
 
await processor.StartProcessingAsync();
// On shutdown:
await processor.StopProcessingAsync();

With AutoCompleteMessages left at its default of true, the processor completes each message after the handler returns, and if the handler throws without settling the message, the processor abandons it. StopProcessingAsync stops receiving new messages while in-flight messages finish. Other processor options include PrefetchCount, ReceiveMode, MaxAutoLockRenewalDuration and SubQueue.

There's no batch settlement API, because the service has no batch settlement operation. Complete messages individually and await them together with Task.WhenAll if needed.

Authentication

Azure.Messaging.ServiceBus uses Azure.Identity, so you can drop SAS keys from configuration. Assign one of the built-in data roles at the narrowest scope that works:

RoleAccess
Azure Service Bus Data OwnerFull access to the namespace and its entities
Azure Service Bus Data SenderSend to queues and topics
Azure Service Bus Data ReceiverReceive from queues and subscriptions

Role assignments can take up to five minutes to propagate. Once every client uses Microsoft Entra ID, you can disable local (SAS) authentication on the namespace.

Step 4: Upgrade Azure Functions to extension 5.x

Install version 5.x of Microsoft.Azure.Functions.Worker.Extensions.ServiceBus (isolated worker) or Microsoft.Azure.WebJobs.Extensions.ServiceBus (in-process). For non-.NET languages, use extension bundle 4.x, [4.0.0, 5.0.0). Extension 5.x binds to Azure.Messaging.ServiceBus types such as ServiceBusReceivedMessage and adds identity-based connections.

Update host.json. Extension 4.x nested settings under handler options:

{
    "version": "2.0",
    "extensions": {
        "serviceBus": {
            "prefetchCount": 100,
            "messageHandlerOptions": {
                "autoComplete": true,
                "maxConcurrentCalls": 32,
                "maxAutoRenewDuration": "00:05:00"
            }
        }
    }
}

Extension 5.x flattens them and renames some:

{
    "version": "2.0",
    "extensions": {
        "serviceBus": {
            "prefetchCount": 0,
            "autoCompleteMessages": true,
            "maxConcurrentCalls": 16,
            "maxAutoLockRenewalDuration": "00:05:00",
            "transportType": "amqpTcp"
        }
    }
}

Two details to carry across. maxConcurrentCalls is per scaled instance and is effectively multiplied by the number of cores, so 16 on a two-core plan allows 32 concurrent calls. Set transportType to amqpWebSockets when only port 443 is open. In the isolated worker, if you bind ServiceBusMessageActions to settle messages yourself, set AutoCompleteMessages to false on the trigger.

Step 5: Open the right network paths

ProtocolOutbound portsUse
AMQP5671, 5672Default for current SDKs
HTTPS443REST API, AMQP over WebSockets, and token acquisition

Port 443 is generally needed even when AMQP uses 5671, because SDK management operations and Microsoft Entra token requests run over HTTPS. If only 443 is allowed, use ServiceBusTransportType.AmqpWebSockets in ServiceBusClientOptions.TransportType.

Verification

  1. Rerun the package inventory and confirm no project references a retired library or a Functions extension below 5.x.
  2. Search configuration stores for Service Bus connection strings and confirm none are used with WindowsAzure.ServiceBus without an AMQP TransportType.
  3. In a test environment, send messages from each producer and consume them with the migrated consumer; compare bodies, MessageId values and application properties.
  4. Watch the active and dead-letter message counts for each queue and subscription during the first production days after cut-over.
  5. If you moved to Microsoft Entra authentication, confirm each workload's identity has only the Sender or Receiver role it needs.

Troubleshooting

Client can't connect after switching to AMQP. Outbound 5671 and 5672 are probably blocked. Open them, or switch to AMQP over WebSockets on 443.

Duplicate messages after migration. Duplicate detection depends on MessageId. The legacy constructor generated one; ServiceBusMessage doesn't. Set it from a business key.

Messages redelivered or moved to dead-letter unexpectedly. Check settlement. With AutoCompleteMessages = true, completing manually as well is redundant; with false, every path must complete, abandon or dead-letter the message, or the lock expires and delivery count rises.

Consumers can't read bodies from older senders. A legacy sender passed a custom .NET object to BrokeredMessage, which was serialised with DataContractSerializer. Either deserialise with the same serializer in the new consumer during the transition, or change senders first to send JSON strings or bytes.

Functions concurrency or lock renewal differs after the upgrade. Extension 5.x documents a flattened host.json schema (maxConcurrentCalls, autoCompleteMessages, maxAutoLockRenewalDuration directly under serviceBus) instead of the 4.x messageHandlerOptions block. Move each value to its 5.x name, and remember the 5.x maxConcurrentCalls default is 16 per instance, multiplied by cores.

Access denied right after assigning a role. Role assignments can take up to five minutes to propagate. Retry before changing scope.

Checklist

  • Inventory complete: every sender, receiver and Function app listed with library and protocol.
  • No WindowsAzure.ServiceBus client left on SBMP; stopgap connection strings use TransportType=Amqp or AmqpWebSockets.
  • Code migrated to Azure.Messaging.ServiceBus (or com.azure.messaging.servicebus), with MessageId set explicitly.
  • Functions on Service Bus extension 5.x with an updated host.json.
  • Firewall allows 5671, 5672 and 443, or clients use WebSockets.
  • Microsoft Entra roles assigned at entity or namespace scope; SAS disabled where possible.
  • Dead-letter queues monitored after cut-over.

If Dataverse posts to these queues, see Dataverse integration choices: webhooks, Service Bus, plug-ins and flows. For broader broker selection, see Kafka vs RabbitMQ vs AWS SQS for event-driven architecture.

References

Questions people ask

What did Microsoft retire for Azure Service Bus on 30 September 2026?

Microsoft retired the WindowsAzure.ServiceBus, Microsoft.Azure.ServiceBus and com.microsoft.azure.servicebus libraries and ended support for the SBMP protocol. The old libraries can still be used but get no official support or updates, and SBMP can no longer be used.

Which Service Bus library used SBMP?

The legacy WindowsAzure.ServiceBus package uses the SOAP-based Service Bus Messaging Protocol (SBMP) by default. AMQP 1.0 support was added in version 2.1 but has to be enabled explicitly, for example by appending TransportType=Amqp to the connection string.

Is there a quick fix that doesn't require rewriting code?

For WindowsAzure.ServiceBus you can append ;TransportType=Amqp (or AmqpWebSockets) to the connection string so the client stops using SBMP. Application code stays the same apart from a few behavioural differences, but the library itself is still retired, so plan the move to Azure.Messaging.ServiceBus.

Which ports does Azure Service Bus need with AMQP?

AMQP uses outbound TCP 5671 and 5672, and HTTPS 443 is generally also required for management operations and Microsoft Entra token requests. If only 443 is open, use the AMQP over WebSockets transport.

Azure Service BusAMQP.NETAzure Functions
  1. Webhooks that don't double-charge: retries, idempotency and dead letters

    Build webhook receivers that survive retries and duplicate deliveries: verify, deduplicate on the event ID, queue to Azure Service Bus, process idempotently and handle dead letters.

    Architecture12 min read
  2. Business Central API Webhooks: Create, Validate and Renew Subscriptions

    Receive Business Central change notifications instead of polling: build a validating receiver in Azure Functions, create a subscription, process notifications and renew before the three-day expiry.

    Architecture10 min read
  3. Dataverse Integration Choices: Webhooks vs Service Bus vs Plug-ins vs Flows

    Compare Dataverse plug-ins, webhooks, Azure Service Bus endpoints and Power Automate triggers for sending events to external systems, with limits, failure behaviour and setup steps.

    Architecture12 min read