Skip to main content
Version: v1.5 (stable)

Local Service Bus development with Topaz

This tutorial walks through a complete Azure Service Bus local development workflow using Topaz: create a namespace, send and receive messages on queues and topics via the Azure CLI and the Azure SDK — all without connecting to real Azure.

What you will build

  • A local Service Bus namespace running on Topaz
  • A queue and a topic/subscription pair managed via the Azure CLI
  • .NET and Python snippets for sending and receiving messages using ServiceBusClient
  • A MassTransit configuration pointing at Topaz

Prerequisites

  • Topaz installed and running (see Getting started)
  • DNS setup completed
  • Topaz certificate trusted by your OS and tooling
  • Azure CLI installed (az --version)
  • Topaz cloud registered in Azure CLI (see Azure CLI integration)

Step 1: Start Topaz

topaz-host \
--default-subscription 00000000-0000-0000-0000-000000000001 \
--log-level Information

Step 2: Set the active cloud to Topaz

az cloud set -n Topaz
export AZURE_CORE_INSTANCE_DISCOVERY=false
az login
az account set --subscription 00000000-0000-0000-0000-000000000001

Step 3: Create a resource group and Service Bus namespace

az group create \
--name rg-local \
--location westeurope

az servicebus namespace create \
--name sbns-local \
--resource-group rg-local \
--location westeurope \
--sku Standard

Topaz assigns the namespace a local AMQP hostname: sbns-local.servicebus.topaz.local.dev.

Step 4: Create a queue

az servicebus queue create \
--name orders \
--namespace-name sbns-local \
--resource-group rg-local

Verify the queue was created:

az servicebus queue show \
--name orders \
--namespace-name sbns-local \
--resource-group rg-local \
--output table

Step 5: Create a topic and subscription

az servicebus topic create \
--name events \
--namespace-name sbns-local \
--resource-group rg-local

az servicebus topic subscription create \
--name all-events \
--topic-name events \
--namespace-name sbns-local \
--resource-group rg-local

Step 6: Send and receive messages — Azure SDK

Install the Azure Service Bus client:

dotnet add package Azure.Messaging.ServiceBus

The connection string uses UseDevelopmentEmulator=true, which tells the SDK to connect over plain AMQP (port 8889) without TLS — the same flag used for the Microsoft Service Bus Emulator:

using Azure.Messaging.ServiceBus;

var connectionString =
"Endpoint=sb://sbns-local.servicebus.topaz.local.dev:8889;" +
"SharedAccessKeyName=RootManageSharedAccessKey;" +
"SharedAccessKey=SAS_KEY_VALUE;" +
"UseDevelopmentEmulator=true;";

await using var client = new ServiceBusClient(connectionString);

// --- Send a message ----------------------------------------------------------
await using var sender = client.CreateSender("orders");
await sender.SendMessageAsync(new ServiceBusMessage("Order #1001"));
Console.WriteLine("Sent: Order #1001");

// --- Receive a message -------------------------------------------------------
await using var receiver = client.CreateReceiver("orders");
var message = await receiver.ReceiveMessageAsync(maxWaitTime: TimeSpan.FromSeconds(5));

if (message != null)
{
Console.WriteLine($"Received: {message.Body}"); // Order #1001
await receiver.CompleteMessageAsync(message);
}
Switching to production

Replace the connection string with the one from the Azure portal. The SDK code — senders, receivers, message processing — is identical.

Step 7: Publish to a topic and receive from a subscription

// Publish to the "events" topic
await using var topicSender = client.CreateSender("events");
await topicSender.SendMessageAsync(new ServiceBusMessage("UserRegistered"));

// Receive from the "all-events" subscription
await using var subReceiver = client.CreateReceiver("events", "all-events");
var event1 = await subReceiver.ReceiveMessageAsync(maxWaitTime: TimeSpan.FromSeconds(5));

if (event1 != null)
{
Console.WriteLine($"Subscription received: {event1.Body}");
await subReceiver.CompleteMessageAsync(event1);
}

Step 8: Using MassTransit

MassTransit communicates over AMQP with TLS (port 5671). Use the TLS connection string variant instead:

Install the MassTransit Azure Service Bus transport:

dotnet add package MassTransit.Azure.ServiceBus.Core

Configure the host:

using MassTransit;

services.AddMassTransit(x =>
{
x.AddConsumer<OrderConsumer>();

x.UsingAzureServiceBus((context, cfg) =>
{
// TLS connection string — port 5671, no UseDevelopmentEmulator flag
cfg.Host(
"Endpoint=sb://sbns-local.servicebus.topaz.local.dev:5671;" +
"SharedAccessKeyName=RootManageSharedAccessKey;" +
"SharedAccessKey=SAS_KEY_VALUE;");

cfg.ReceiveEndpoint("orders", e =>
{
e.ConfigureConsumer<OrderConsumer>(context);
});
});
});

Define a consumer:

public class OrderConsumer : IConsumer<OrderMessage>
{
public Task Consume(ConsumeContext<OrderMessage> context)
{
Console.WriteLine($"Processing order: {context.Message.OrderId}");
return Task.CompletedTask;
}
}

public record OrderMessage(string OrderId);
Certificate trust for MassTransit

MassTransit uses the standard .NET TLS stack, so the Topaz certificate must be trusted at the OS level. Follow the certificate trust steps in Getting started.

Step 9: Automate with Testcontainers

When running tests, start Topaz automatically using Testcontainers:

var container = new ContainerBuilder("thecloudtheory/topaz-host:latest")
.WithPortBinding(8889) // plain AMQP (UseDevelopmentEmulator)
.WithPortBinding(5671) // AMQPS/TLS (MassTransit)
.WithPortBinding(8899) // ARM / Resource Manager
.Build();

await container.StartAsync();

Then create the namespace and queue via the ARM SDK before each test class, using the same provisioning pattern shown in the Testcontainers guide.

Common gotchas

SymptomCauseFix
Connection refused on port 8889UseDevelopmentEmulator=true required but missing from connection stringAdd UseDevelopmentEmulator=true; to the connection string
SSL handshake failed in MassTransitTopaz certificate not trusted at OS levelTrust the certificate as described in Getting started
MessagingEntityNotFoundExceptionQueue or topic does not exist yetCreate the queue/topic first via the Azure CLI or ARM SDK
MassTransit connects but never receivesWrong port — MassTransit needs port 5671 (TLS), not 8889Use GetServiceBusConnectionStringWithTls(...) or point to port 5671
Star on GitHub