Skip to main content

Command Palette

Search for a command to run...

MQTT on Azure with .NET

Published
24 min read
MQTT on Azure with .NET
P
Senior Software Engineer specialising in cloud architecture, distributed systems, and modern .NET development, with over two decades of experience designing and delivering enterprise platforms in financial, insurance, and high-scale commercial environments. My focus is on building systems that are reliable, scalable, and maintainable over the long term. I’ve led modernisation initiatives moving legacy platforms to cloud-native Azure architectures, designed high-throughput streaming solutions to eliminate performance bottlenecks, and implemented secure microservices environments using container-based deployment models and event-driven integration patterns. From an architecture perspective, I have strong practical experience applying approaches such as Vertical Slice Architecture, Domain-Driven Design, Clean Architecture, and Hexagonal Architecture. I’m particularly interested in modular system design that balances delivery speed with long-term sustainability, and I enjoy solving complex problems involving distributed workflows, performance optimisation, and system reliability. I enjoy mentoring engineers, contributing to architectural decisions, and helping teams simplify complex systems into clear, maintainable designs. I’m always open to connecting with other engineers, architects, and technology leaders working on modern cloud and distributed system challenges.

MQTT looks simple at first. The basic model is easy to understand, but the architectural choice on Azure is not just about whether Azure can accept MQTT traffic. It is about what kind of system you are building around that traffic.

In .NET and Azure, the two managed options worth comparing are Azure IoT Hub and Azure Event Grid MQTT Broker. Both can sit behind MQTT clients. Both can receive telemetry. Both can feed Azure Functions. Both can become the entry point into a larger distributed system. They are not the same product, though, and treating them as interchangeable is where teams usually get into trouble.

IoT Hub is a device platform. Event Grid MQTT Broker is a broker and eventing bridge. That difference matters more than any individual feature checklist.

If you are connecting real devices, tracking device identity, managing device state, sending commands to known devices, or integrating with IoT specific services, IoT Hub should usually be your starting point. If you need a general MQTT publish and subscribe model with custom topic spaces, client to client messaging, broadcast patterns, and routing into Azure services, Event Grid MQTT Broker is often the cleaner fit.

Below I'll compare both options from a .NET engineer's point of view. It focuses on system shape, runtime behaviour, code, security, routing, and the practical decision points that matter when you move past the proof of concept.

To start with, try to remember these points........

Azure IoT Hub is for managed device ingestion and device operations.
Azure Event Grid MQTT Broker is for managed MQTT pub/sub and Azure event routing.

That sounds blunt, but it prevents bad design. IoT Hub is not just an MQTT broker with an Azure logo. Event Grid MQTT Broker is not just a new name for IoT Hub. They overlap at the protocol edge, then diverge quickly.

Here is the decision in one diagram.

A basic decision table also helps.

Requirement Better fit
Device telemetry from known devices Azure IoT Hub
Device twins, direct methods, cloud to device messages, device lifecycle Azure IoT Hub
Device Provisioning Service integration Azure IoT Hub
Custom hierarchical MQTT topics Azure Event Grid MQTT Broker
MQTT clients publishing and subscribing to each other Azure Event Grid MQTT Broker
MQTT v5 features, shared subscriptions, retained messages, topic spaces Azure Event Grid MQTT Broker
Routing MQTT messages into Azure Functions or Event Hubs Either, depending on the edge model
Internal business messaging between backend services Usually neither, use Service Bus, Event Grid events, or Event Hubs
A low level broker you fully control Self host Mosquitto, EMQX, HiveMQ, or another broker

First, keep MQTT at the edge

The biggest architectural mistake is letting MQTT leak into the whole estate. MQTT is excellent at the device or client edge. Its lightweight, efficient, and resilient across unstable networks. But once a message is inside your Azure boundary, your internal services usually need stronger business semantics than a raw topic string and a payload.

A good architecture starting point:

The ingestion boundary should translate from an MQTT concern into an application concern. A device topic such as factory/line1/machine7/temperature may become a MachineTemperatureRecorded event. A command topic such as devices/CXa-23112/prompt may become a DeviceCommandRequested event. That small translation step keeps the rest of the platform clean.

It also gives you a better place to apply validation, idempotency, schema versioning, poison message handling, audit storage, monitoring, and business level routing. MQTT topics are a transport detail. Your domain events are your application contract.

Option 1: Azure IoT Hub

Azure IoT Hub is the more obvious choice when the thing connecting to Azure is a device, gateway, appliance, vehicle, sensor, embedded board, or industrial controller. The word device matters. IoT Hub gives each device an identity and a relationship with the cloud. You are not only accepting messages. You are managing a fleet.

IoT Hub supports device communication over MQTT v3.1.1 on port 8883 and MQTT v3.1.1 over WebSockets on port 443. The WebSocket option is useful when corporate, school, factory, or customer networks block non HTTPS ports. The Azure IoT device SDKs support C#, Java, Node.js, C, and Python, and the .NET SDK lets you choose MQTT as the transport.

The typical shape is simple.

Use IoT Hub when you care about the identity and lifecycle of devices, not just messages. That usually includes per device credentials, device provisioning, cloud to device messages, direct methods, device twins, device status, and routing telemetry into downstream services.

Sending telemetry to IoT Hub from .NET

The normal .NET approach is to use the Azure IoT device SDK rather than hand crafting MQTT packets. The SDK hides the IoT Hub MQTT topic conventions and gives you a device level API.

Install the device client package:

dotnet add package Microsoft.Azure.Devices.Client

A simple telemetry sender:

using System.Text;
using System.Text.Json;
using Microsoft.Azure.Devices.Client;

namespace DeviceSimulator;

public sealed class TelemetryPublisher(string connectionString)
{
    public async Task PublishAsync(CancellationToken stopToken)
    {
        using var deviceClient = DeviceClient.CreateFromConnectionString(
            connectionString,
            TransportType.Mqtt);

        await deviceClient.OpenAsync(stopToken);

        var reading = new
        {
            deviceId = "machine-7",
            temperature = 21.4,
            humidity = 61,
            recordedAtUtc = DateTimeOffset.UtcNow
        };

        var json = JsonSerializer.Serialize(reading);

        using var message = new Message(Encoding.UTF8.GetBytes(json))
        {
            ContentType = "application/json",
            ContentEncoding = "utf-8"
        };

        message.Properties["messageType"] = "telemetry";
        message.Properties["schemaVersion"] = "1";

        await deviceClient.SendEventAsync(message, stopToken);
    }
}

A console app wrapper:

using DeviceSimulator;

var connectionString = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_CONNECTION_STRING")
    ?? throw new InvalidOperationException("Missing IOTHUB_DEVICE_CONNECTION_STRING.");

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));

var publisher = new TelemetryPublisher(connectionString);
await publisher.PublishAsync(cts.Token);

The important design point is that the device code does not know about Service Bus, Cosmos DB, SQL, or internal business services. It only knows how to connect as a device and send telemetry to IoT Hub.

Processing IoT Hub telemetry with Azure Functions

IoT Hub exposes a built in Event Hubs compatible endpoint. That makes Azure Functions a natural processing layer. You can use this function to validate messages, write raw data to storage, enrich the event, and then publish a cleaner business event.

Install the Event Hubs extension for Azure Functions isolated worker:

dotnet add package Microsoft.Azure.Functions.Worker.Extensions.EventHubs

A simple function:

using System.Text.Json;
using Azure.Messaging.EventHubs;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace TelemetryIngestion;

public sealed class ProcessTelemetryFromIoTHub(ILogger<ProcessTelemetryFromIoTHub> logger)
{
    [Function(nameof(ProcessTelemetryFromIoTHub))]
    public async Task RunAsync(
        [EventHubTrigger(
            "%IotHubEventHubName%",
            Connection = "IotHubEventHubConnection",
            ConsumerGroup = "%IotHubConsumerGroup%")]
        EventData[] events,
        CancellationToken stopToken)
    {
        foreach (var eventData in events)
        {
            var body = eventData.EventBody.ToString();

            logger.LogInformation(
                "Received IoT Hub message. PartitionKey: {PartitionKey}, SequenceNumber: {SequenceNumber}",
                eventData.PartitionKey,
                eventData.SequenceNumber);

            var telemetry = JsonSerializer.Deserialize<DeviceTelemetry>(body);

            if (telemetry is null)
            {
                logger.LogWarning("Invalid telemetry payload: {Payload}", body);
                continue;
            }

            var domainEvent = new MachineTemperatureRecorded(
                telemetry.DeviceId,
                telemetry.Temperature,
                telemetry.RecordedAtUtc);

            await PublishDomainEventAsync(domainEvent, stopToken);
        }
    }

    private static Task PublishDomainEventAsync(
        MachineTemperatureRecorded domainEvent,
        CancellationToken stopToken)
    {
        // Publish to Service Bus, Event Grid, Event Hubs, or an outbox backed dispatcher.
        return Task.CompletedTask;
    }
}

public sealed record DeviceTelemetry(
    string DeviceId,
    decimal Temperature,
    decimal Humidity,
    DateTimeOffset RecordedAtUtc);

public sealed record MachineTemperatureRecorded(
    string DeviceId,
    decimal Temperature,
    DateTimeOffset RecordedAtUtc);

This function deliberately stops MQTT at the boundary. The rest of the system receives a business event. That is the kind of separation that keeps a platform maintainable.

Routing IoT Hub messages

IoT Hub message routing lets you direct device to cloud messages to downstream Azure services. Supported routing endpoints include the built in endpoint, storage containers, Service Bus queues, Service Bus topics, Event Hubs, and Cosmos DB. That gives you a clean way to separate operational paths.

For example, you might route all raw telemetry to storage, route high priority alerts to Service Bus, and route analytics events to Event Hubs.

This is good when different consumers have different reliability and delivery needs. A support dashboard may need the latest known status. An analytics pipeline may need a high volume stream. An operational workflow may need a queue or topic that supports retries, dead lettering, and back pressure.

Sending commands back to devices with IoT Hub

IoT systems are rarely one way. You often need to send a command back to a device. IoT Hub has cloud to device messaging and direct methods for this. A command might ask a device to reload configuration, increase sampling frequency, reset a module, or start a local operation.

For a backend service, you use the Azure IoT service SDK.

dotnet add package Microsoft.Azure.Devices

A simple cloud to device sender:

using System.Text;
using System.Text.Json;
using Microsoft.Azure.Devices;

namespace DeviceCommandApi;

public sealed class DeviceCommandSender(string iotHubServiceConnectionString)
{
    public async Task SendRestartCommandAsync(string deviceId, CancellationToken stopToken)
    {
        using var serviceClient = ServiceClient.CreateFromConnectionString(
            iotHubServiceConnectionString);

        var payload = new
        {
            command = "restart-module",
            module = "temperature-sampler",
            requestedAtUtc = DateTimeOffset.UtcNow
        };

        var json = JsonSerializer.Serialize(payload);

        using var message = new Message(Encoding.UTF8.GetBytes(json))
        {
            ContentType = "application/json",
            ContentEncoding = "utf-8",
            Ack = DeliveryAcknowledgement.Full
        };

        message.Properties["commandType"] = "restart-module";
        message.Properties["schemaVersion"] = "1";

        await serviceClient.SendAsync(deviceId, message, stopToken);
    }
}

That API is device oriented. You send to a known device identity. That is the point. When your business operation is tied to a specific registered device, IoT Hub gives you the right abstraction.

Where IoT Hub fits well

IoT Hub is a strong fit when you have a large device estate and the cloud needs to understand those devices as first class resources. The device identity is not incidental. It is part of your security model, your operations model, and your support model.

A building management system is a good example. Each heating controller, air quality sensor, and gateway has an identity. The platform needs telemetry, but it also needs to know whether each device is connected, when it last reported, which firmware it runs, and what configuration it should use. IoT Hub gives you a better foundation for that than a generic broker.

A vehicle telemetry platform is another example. You may receive high volume messages, but you also need secure device registration, route based ingestion, operational commands, and downstream processing. Again, IoT Hub is not just transporting messages. It is modelling a relationship between devices and the cloud.

Industrial systems often land in the same place. A gateway can aggregate local machine telemetry and forward it to IoT Hub. The cloud can route messages into Event Hubs for analytics, Service Bus for operational workflows, and storage for audit or replay.

Where IoT Hub is a weaker fit

IoT Hub is less natural when you want normal MQTT topic freedom. IoT Hub has its own MQTT topic conventions. If your design depends on clients publishing and subscribing to arbitrary hierarchical topics, IoT Hub will feel constrained.

It is also not the right choice for general backend messaging. If one .NET service needs to tell another .NET service that an order was paid, use Service Bus or Event Grid events. Do not introduce MQTT simply because it can move a message. Transport novelty is not architecture.

IoT Hub can also be the wrong choice when your clients are not really devices. For example, if browser based dashboards, mobile apps, cloud services, and edge services need to participate in a shared MQTT topic space, Event Grid MQTT Broker usually maps better to the requirement.

Option 2: Azure Event Grid MQTT Broker

Azure Event Grid MQTT Broker is a managed MQTT broker capability inside Event Grid namespaces. It supports MQTT v3.1.1, MQTT v3.1.1 over WebSockets, MQTT v5, and MQTT v5 over WebSockets. Clients connect over TLS. Standard MQTT uses port 8883, while MQTT over WebSockets uses port 443.

The key difference is topic freedom. Event Grid MQTT Broker lets you create topic spaces and permission bindings. That gives you an MQTT style access model based around topic templates, client groups, publishers, and subscribers.

The typical shape:

Use this when MQTT itself is the integration model. A device may publish telemetry, another client may subscribe to a command topic, a backend may inject a command, and Event Grid may route selected messages into Azure Functions or Event Hubs.

Topic spaces and permission bindings

Event Grid MQTT Broker introduces a few concepts that matter. A client represents a connecting MQTT client. A client group lets you organise clients. A topic space describes the topic templates that clients can access. A permission binding grants a client group permission to publish or subscribe to a topic space.

That model is more broker like than IoT Hub. It suits systems where topics are part of the design.

This gives you a clean way to express who can publish and who can subscribe. It also avoids putting all authorisation logic inside application code. The broker can reject clients and topic access before the message reaches your business services.

Publishing to Event Grid MQTT Broker from .NET with MQTTnet

For Event Grid MQTT Broker, you normally use a generic MQTT client library. MQTTnet is the common .NET choice. It supports MQTT clients, TLS, WebSockets, and MQTT up to version 5.

Install the package:

dotnet add package MQTTnet

The exact authentication shape depends on how you configure the Event Grid namespace. Event Grid MQTT Broker supports certificate authentication, Microsoft Entra ID token authentication, OAuth 2.0 JWT, and webhook based authentication. The following example shows the application shape with username and password or token based credentials. In production, avoid storing secrets in configuration files. Use Key Vault, managed identity where supported, and proper certificate handling.

using System.Text.Json;
using MQTTnet;
using MQTTnet.Protocol;

namespace EventGridMqttPublisher;

public sealed class MqttTelemetryPublisher(
    string hostName,
    string clientId,
    string userName,
    string password)
{
    public async Task PublishAsync(CancellationToken stopToken)
    {
        var factory = new MqttFactory();
        using var mqttClient = factory.CreateMqttClient();

        var options = new MqttClientOptionsBuilder()
            .WithClientId(clientId)
            .WithTcpServer(hostName, 8883)
            .WithCredentials(userName, password)
            .WithTls()
            .WithCleanSession()
            .Build();

        await mqttClient.ConnectAsync(options, stopToken);

        var payload = JsonSerializer.Serialize(new
        {
            deviceId = "machine-7",
            temperature = 21.4,
            recordedAtUtc = DateTimeOffset.UtcNow
        });

        var message = new MqttApplicationMessageBuilder()
            .WithTopic("factory/line1/machine7/temperature")
            .WithPayload(payload)
            .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
            .Build();

        await mqttClient.PublishAsync(message, stopToken);

        await mqttClient.DisconnectAsync(cancellationToken: stopToken);
    }
}

This code is intentionally broker centric. The topic matters. Subscribers can receive the message through MQTT, and Event Grid routing can also push it into Azure services.

Subscribing with MQTTnet

A subscriber looks similar. The difference is that you attach a message handler and subscribe to a topic or topic pattern allowed by your topic space and permission binding.

using System.Text;
using MQTTnet;
using MQTTnet.Protocol;

namespace EventGridMqttSubscriber;

public sealed class MachineTelemetrySubscriber(
    string hostName,
    string clientId,
    string userName,
    string password,
    ILogger<MachineTelemetrySubscriber> logger)
{
    public async Task RunAsync(CancellationToken stopToken)
    {
        var factory = new MqttFactory();
        using var mqttClient = factory.CreateMqttClient();

        mqttClient.ApplicationMessageReceivedAsync += args =>
        {
            var topic = args.ApplicationMessage.Topic;
            var payload = Encoding.UTF8.GetString(args.ApplicationMessage.PayloadSegment);

            logger.LogInformation(
                "Received MQTT message. Topic: {Topic}, Payload: {Payload}",
                topic,
                payload);

            return Task.CompletedTask;
        };

        var options = new MqttClientOptionsBuilder()
            .WithClientId(clientId)
            .WithTcpServer(hostName, 8883)
            .WithCredentials(userName, password)
            .WithTls()
            .WithCleanSession()
            .Build();

        await mqttClient.ConnectAsync(options, stopToken);

        var subscribeOptions = factory.CreateSubscribeOptionsBuilder()
            .WithTopicFilter(filter =>
            {
                filter.WithTopic("factory/line1/+/temperature");
                filter.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce);
            })
            .Build();

        await mqttClient.SubscribeAsync(subscribeOptions, stopToken);

        await Task.Delay(Timeout.InfiniteTimeSpan, stopToken);
    }
}

This is the kind of code you would write for dashboards, gateways, local processors, or backend services that genuinely participate in MQTT pub/sub.

Routing Event Grid MQTT messages into Azure Functions

Event Grid MQTT Broker can route MQTT messages to an Event Grid namespace topic or a custom topic. From there, you can use an event subscription to push messages to Azure Functions, Event Hubs, Service Bus, webhooks, and other supported destinations.

A routed MQTT message is represented as a CloudEvent. The MQTT topic appears as the CloudEvent subject. That makes function code fairly direct.

Install the Event Grid extension for Azure Functions isolated worker:

dotnet add package Microsoft.Azure.Functions.Worker.Extensions.EventGrid

Then process the CloudEvent:

using Azure.Messaging;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace MqttEventIngestion;

public sealed class ProcessRoutedMqttEvent(ILogger<ProcessRoutedMqttEvent> logger)
{
    [Function(nameof(ProcessRoutedMqttEvent))]
    public async Task RunAsync(
        [EventGridTrigger] CloudEvent cloudEvent,
        CancellationToken stopToken)
    {
        logger.LogInformation(
            "Received routed MQTT event. Type: {Type}, Subject: {Subject}",
            cloudEvent.Type,
            cloudEvent.Subject);

        if (!string.Equals(cloudEvent.Type, "MQTT.EventPublished", StringComparison.OrdinalIgnoreCase))
        {
            logger.LogInformation("Ignoring non MQTT event type {Type}", cloudEvent.Type);
            return;
        }

        var topic = cloudEvent.Subject ?? string.Empty;
        var payload = cloudEvent.Data?.ToString() ?? string.Empty;

        var normalised = new MqttMessageReceived(
            topic,
            payload,
            cloudEvent.Time ?? DateTimeOffset.UtcNow);

        await PublishInternalEventAsync(normalised, stopToken);
    }

    private static Task PublishInternalEventAsync(
        MqttMessageReceived message,
        CancellationToken stopToken)
    {
        // Publish to Service Bus, Event Grid events, Event Hubs, or an outbox backed dispatcher.
        return Task.CompletedTask;
    }
}

public sealed record MqttMessageReceived(
    string Topic,
    string Payload,
    DateTimeOffset ReceivedAtUtc);

This gives you a clean bridge from MQTT pub/sub into normal Azure event processing.

Publishing from a backend without a persistent MQTT connection

One useful Event Grid MQTT Broker feature is HTTP Publish. It lets a backend service publish an MQTT message through an HTTPS POST rather than holding a persistent MQTT session open. This is useful for command services, serverless functions, and ordinary backend APIs that need to publish to an MQTT topic but should not behave like long lived MQTT clients.

The HTTP shape maps request details to MQTT publish properties such as topic, QoS, retain flag, response topic, correlation data, user properties, and payload.

A simplified .NET service:

using System.Net.Http.Headers;
using System.Text;
using Azure.Core;
using Azure.Identity;

namespace EventGridHttpPublish;

public sealed class MqttHttpPublisher(HttpClient httpClient, TokenCredential credential)
{
    public async Task PublishCommandAsync(
        Uri brokerEndpoint,
        string topic,
        string commandJson,
        CancellationToken stopToken)
    {
        var token = await credential.GetTokenAsync(
            new TokenRequestContext(["https://eventgrid.azure.net/.default"]),
            stopToken);

        var encodedTopic = Uri.EscapeDataString(topic);
        var requestUri = new Uri(
            brokerEndpoint,
            $"/mqtt/messages?topic={encodedTopic}&api-version=2025-02-15-preview");

        using var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);
        request.Headers.TryAddWithoutValidation("mqtt-qos", "1");
        request.Headers.TryAddWithoutValidation("mqtt-retain", "0");
        request.Content = new StringContent(commandJson, Encoding.UTF8, "application/json");

        using var response = await httpClient.SendAsync(request, stopToken);
        response.EnsureSuccessStatusCode();
    }
}

public static class Composition
{
    public static MqttHttpPublisher CreatePublisher(HttpClient httpClient)
    {
        return new MqttHttpPublisher(httpClient, new DefaultAzureCredential());
    }
}

This is a good option when your backend is already HTTP native and you want the broker to deliver the command to MQTT subscribers. It also avoids scaling a large number of persistent MQTT sessions from server side code.

Where Event Grid MQTT Broker fits well

Event Grid MQTT Broker fits well when your domain is naturally topic based. For example, a building platform might use topics such as buildings/{buildingId}/floors/{floorId}/sensors/{sensorId}/state. Dashboards can subscribe to subsets. Alert processors can subscribe to other subsets. Backend services can publish commands to specific topic spaces.

It also fits when multiple kinds of clients need to talk over MQTT. A gateway might publish telemetry. A dashboard might subscribe. A cloud service might publish commands. Another service might subscribe to replies. IoT Hub can support device to cloud and cloud to device patterns, but Event Grid MQTT Broker is more natural when MQTT pub/sub is the shared interaction model.

It is also attractive when you want Event Grid's routing model. MQTT messages can enter the broker, then Event Grid can route them into serverless functions, streams, queues, and event handlers. That lets you mix direct MQTT subscribers with cloud event processing.

Retained messages are another broker style feature. They let new subscribers receive the latest known value for a topic without waiting for the next publish. This is useful for device state, configuration, and control signals.

Where Event Grid MQTT Broker is a weaker fit

Event Grid MQTT Broker is not a full IoT fleet management platform in the same sense as IoT Hub. If your system needs device twins, direct methods, IoT specific provisioning flows, and the conventional Azure IoT device lifecycle, IoT Hub remains the better abstraction. It may also be the wrong choice when you only need high volume ingestion for analytics and no MQTT subscribers. In that case, Event Hubs may be enough. If you only need reliable command processing between services, Service Bus may be enough. If you only need event notification between Azure services, Event Grid events may be enough.

Dont pick Event Grid MQTT Broker just because MQTT looks cool. Pick it because MQTT topics, broker mediated publish and subscribe, and client level messaging are actually part of the requirement.

Comparing the two options in more detail

The two services overlap at the network edge. After that, they solve different problems.

Area Azure IoT Hub Azure Event Grid MQTT Broker
Primary purpose Device connectivity and IoT operations MQTT broker based publish and subscribe
MQTT role Protocol option for device communication Core product capability
Topic model IoT Hub specific MQTT conventions Custom topic spaces and topic templates
Client model Device identity first MQTT client and client group first
Device operations Stronger fit Weaker fit
Client to client pub/sub Not the main model Natural fit
Cloud event routing Supported through IoT Hub routing and endpoints Supported through Event Grid routing
.NET client style Azure IoT SDK MQTTnet or another MQTT client
Backend command style IoT Hub service SDK, cloud to device, direct methods MQTT publish or HTTP Publish to MQTT topic
Best default Real IoT device platforms General MQTT broker scenarios

The hidden question is not "which service supports MQTT?". The hidden question is "what does a connected client mean in this system?".

If the client is a managed device, use IoT Hub. If the client is a participant in a topic based messaging fabric, use Event Grid MQTT Broker.

Security model

For IoT Hub, security is device identity first. You register devices, issue credentials, and control device access to the hub. Device SDKs use the selected authentication mechanism to establish the connection. Backend services use service credentials or managed identity based patterns where available for management and integration tasks.

For Event Grid MQTT Broker, security is more broker oriented. You authenticate clients through supported mechanisms such as certificates, Microsoft Entra ID, OAuth 2.0 JWT, or webhook authentication. You then authorise access through client groups, topic spaces, and permission bindings.

The design consequence is important. With IoT Hub, the question is usually "is this registered device allowed to connect and perform this IoT operation?". With Event Grid MQTT Broker, the question is usually "is this authenticated MQTT client allowed to publish or subscribe to this topic space?".

Both models are valid. They simply optimise for different problems.

Reliability and ordering

MQTT has Quality of Service levels, but you still need to design your application as if duplicate delivery can happen. That means idempotency at the ingestion boundary. If a device sends a message with a natural event id or sequence number, preserve it. If it does not, consider generating a deterministic id based on device id, timestamp, topic, and payload hash, then store enough state to detect duplicates where it matters.

Event Grid routing uses at least once delivery semantics for routed MQTT messages and does not guarantee ordering for event delivery. That is normal for distributed event systems, but it means your handlers must tolerate duplicates and out of order messages. IoT Hub pipelines can also involve retries, partitions, multiple consumers, and downstream delivery behaviour that forces the same discipline.

The practical rule is this:

Never make money movement, machine control, or workflow state depend on exactly once delivery from the transport alone.

Use idempotent handlers, versioned state changes, optimistic concurrency, and clear command acknowledgements. For backend workflows, publish internal messages through Service Bus or an outbox pattern after you normalise the MQTT input.

Observability

For IoT Hub, monitor device connection behaviour, telemetry volume, routing failures, function failures, downstream queue depth, and processing latency. Include device id, message type, schema version, route, and correlation id in logs. Avoid logging raw payloads if they may contain sensitive operational data.

For Event Grid MQTT Broker, monitor connection failures, successful connections, disconnections, failed publishes, failed subscriptions, routing failures, and downstream delivery failures. Include client id, topic, topic space, event type, subject, and correlation properties in logs.

The observability shape should match the architecture.

The most useful logs are not "received message" logs. The useful logs answer specific support questions. Which client sent the message? Which topic did it use? Which business event did we create? Which downstream route handled it? Did the handler reject it, retry it, or accept it?

A practical architecture for IoT Hub

A production IoT Hub architecture usually separates raw ingestion from business processing.

The device sends telemetry. IoT Hub accepts and routes it. A function converts it into application events. Raw storage gives you audit and replay capability. Cosmos DB or SQL can hold current state. Service Bus can drive workflows that need retry, ordering by business key, and dead lettering.

This design works well because it avoids making IoT Hub responsible for business orchestration. IoT Hub remains the device ingress and operations boundary.

A practical architecture for Event Grid MQTT Broker

An Event Grid MQTT Broker architecture often has two valid paths at the same time. One path is MQTT client to MQTT client. The other path is MQTT to Azure event processing.

This design works well when MQTT is part of the interaction model, not just the ingestion protocol. Dashboards, processors, and devices can subscribe directly to MQTT topics, while Azure services can still receive routed events.

Common mistakes

Using IoT Hub as a generic MQTT broker. It can communicate over MQTT, but its MQTT surface exists to support IoT Hub's device model. If your application needs arbitrary topic based pub/sub, choose the product that was designed for that.

Using Event Grid MQTT Broker as if it automatically gives you a complete device platform. It gives you a strong managed broker and routing model, but you still need to design device lifecycle, provisioning, command semantics, and support workflows if those are part of your domain.

Sending raw MQTT payloads directly into business services. That couples your internal architecture to topic names and payload quirks. Put an ingestion function or service in the middle. Validate, normalise, version, and publish clear business events.

Assuming QoS removes the need for idempotency. It does not. You still need to handle duplicates, retries, out of order arrival, partial failures, and downstream outages.

Forgetting operations. MQTT demos are usually smooth. Production systems need certificate rotation, token expiry handling, reconnect strategy, dead letter handling, metrics, alerting, replay, and support tools.

Recommendation

Start with IoT Hub when you are building a device platform. It is the safer default for real IoT fleets because it gives you device oriented concepts rather than just message transport.

Start with Event Grid MQTT Broker when you are building an MQTT broker based integration model. It is the better fit when custom topics, pub/sub, MQTT v5 features, topic spaces, retained messages, HTTP Publish, and routing into Azure are central to the design.

Do not choose either one for internal .NET service to service messaging unless MQTT is genuinely required. For backend workflows, Service Bus, Event Grid events, and Event Hubs are usually cleaner. MQTT should normally sit at the edge of the platform, where its lightweight publish and subscribe model gives you real value.