Configure Azure IoT Operations Broker Listeners

The azure-native:iotoperations:BrokerListener resource, part of the Pulumi Azure Native provider, defines network listeners for Azure IoT Operations MQTT brokers. This guide focuses on two capabilities: port and protocol configuration, and TLS certificate management through both automatic (cert-manager) and manual methods.

Broker listeners belong to an IoT Operations broker instance running on Azure Arc-enabled Kubernetes. They reference authentication resources, cert-manager Issuers for automatic certificate provisioning, and Kubernetes secrets for manual certificate management. The examples are intentionally small. Combine them with your own broker infrastructure, authentication policies, and certificate management setup.

Expose MQTT on the default port

Most IoT deployments start with a basic listener on port 1883, the standard unencrypted MQTT port.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const brokerListener = new azure_native.iotoperations.BrokerListener("brokerListener", {
    brokerName: "resource-name123",
    extendedLocation: {
        name: "qmbrfwcpwwhggszhrdjv",
        type: azure_native.iotoperations.ExtendedLocationType.CustomLocation,
    },
    instanceName: "resource-name123",
    listenerName: "resource-name123",
    properties: {
        ports: [{
            port: 1883,
        }],
    },
    resourceGroupName: "rgiotoperations",
});
import pulumi
import pulumi_azure_native as azure_native

broker_listener = azure_native.iotoperations.BrokerListener("brokerListener",
    broker_name="resource-name123",
    extended_location={
        "name": "qmbrfwcpwwhggszhrdjv",
        "type": azure_native.iotoperations.ExtendedLocationType.CUSTOM_LOCATION,
    },
    instance_name="resource-name123",
    listener_name="resource-name123",
    properties={
        "ports": [{
            "port": 1883,
        }],
    },
    resource_group_name="rgiotoperations")
package main

import (
	iotoperations "github.com/pulumi/pulumi-azure-native-sdk/iotoperations/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := iotoperations.NewBrokerListener(ctx, "brokerListener", &iotoperations.BrokerListenerArgs{
			BrokerName: pulumi.String("resource-name123"),
			ExtendedLocation: &iotoperations.ExtendedLocationArgs{
				Name: pulumi.String("qmbrfwcpwwhggszhrdjv"),
				Type: pulumi.String(iotoperations.ExtendedLocationTypeCustomLocation),
			},
			InstanceName: pulumi.String("resource-name123"),
			ListenerName: pulumi.String("resource-name123"),
			Properties: &iotoperations.BrokerListenerPropertiesArgs{
				Ports: iotoperations.ListenerPortArray{
					&iotoperations.ListenerPortArgs{
						Port: pulumi.Int(1883),
					},
				},
			},
			ResourceGroupName: pulumi.String("rgiotoperations"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var brokerListener = new AzureNative.IoTOperations.BrokerListener("brokerListener", new()
    {
        BrokerName = "resource-name123",
        ExtendedLocation = new AzureNative.IoTOperations.Inputs.ExtendedLocationArgs
        {
            Name = "qmbrfwcpwwhggszhrdjv",
            Type = AzureNative.IoTOperations.ExtendedLocationType.CustomLocation,
        },
        InstanceName = "resource-name123",
        ListenerName = "resource-name123",
        Properties = new AzureNative.IoTOperations.Inputs.BrokerListenerPropertiesArgs
        {
            Ports = new[]
            {
                new AzureNative.IoTOperations.Inputs.ListenerPortArgs
                {
                    Port = 1883,
                },
            },
        },
        ResourceGroupName = "rgiotoperations",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.iotoperations.BrokerListener;
import com.pulumi.azurenative.iotoperations.BrokerListenerArgs;
import com.pulumi.azurenative.iotoperations.inputs.ExtendedLocationArgs;
import com.pulumi.azurenative.iotoperations.inputs.BrokerListenerPropertiesArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var brokerListener = new BrokerListener("brokerListener", BrokerListenerArgs.builder()
            .brokerName("resource-name123")
            .extendedLocation(ExtendedLocationArgs.builder()
                .name("qmbrfwcpwwhggszhrdjv")
                .type("CustomLocation")
                .build())
            .instanceName("resource-name123")
            .listenerName("resource-name123")
            .properties(BrokerListenerPropertiesArgs.builder()
                .ports(ListenerPortArgs.builder()
                    .port(1883)
                    .build())
                .build())
            .resourceGroupName("rgiotoperations")
            .build());

    }
}
resources:
  brokerListener:
    type: azure-native:iotoperations:BrokerListener
    properties:
      brokerName: resource-name123
      extendedLocation:
        name: qmbrfwcpwwhggszhrdjv
        type: CustomLocation
      instanceName: resource-name123
      listenerName: resource-name123
      properties:
        ports:
          - port: 1883
      resourceGroupName: rgiotoperations

The ports array defines which ports the listener exposes. Each port entry specifies a port number; without additional configuration, the listener uses MQTT protocol without TLS. The brokerName and instanceName tie this listener to an existing IoT Operations broker, while extendedLocation points to the Azure Arc custom location where the broker runs.

Configure multiple ports with TLS variants

Production deployments typically expose multiple protocols to support different client capabilities, mixing encrypted and unencrypted connections.

import * as pulumi from "@pulumi/pulumi";
import * as azure_native from "@pulumi/azure-native";

const brokerListener = new azure_native.iotoperations.BrokerListener("brokerListener", {
    brokerName: "resource-name123",
    extendedLocation: {
        name: "qmbrfwcpwwhggszhrdjv",
        type: azure_native.iotoperations.ExtendedLocationType.CustomLocation,
    },
    instanceName: "resource-name123",
    listenerName: "resource-name123",
    properties: {
        ports: [
            {
                authenticationRef: "example-authentication",
                port: 8080,
                protocol: azure_native.iotoperations.BrokerProtocolType.WebSockets,
            },
            {
                authenticationRef: "example-authentication",
                port: 8443,
                protocol: azure_native.iotoperations.BrokerProtocolType.WebSockets,
                tls: {
                    certManagerCertificateSpec: {
                        issuerRef: {
                            group: "jtmuladdkpasfpoyvewekmiy",
                            kind: azure_native.iotoperations.CertManagerIssuerKind.Issuer,
                            name: "example-issuer",
                        },
                    },
                    mode: azure_native.iotoperations.TlsCertMethodMode.Automatic,
                },
            },
            {
                authenticationRef: "example-authentication",
                port: 1883,
            },
            {
                authenticationRef: "example-authentication",
                port: 8883,
                tls: {
                    manual: {
                        secretRef: "example-secret",
                    },
                    mode: azure_native.iotoperations.TlsCertMethodMode.Manual,
                },
            },
        ],
        serviceType: azure_native.iotoperations.ServiceType.LoadBalancer,
    },
    resourceGroupName: "rgiotoperations",
});
import pulumi
import pulumi_azure_native as azure_native

broker_listener = azure_native.iotoperations.BrokerListener("brokerListener",
    broker_name="resource-name123",
    extended_location={
        "name": "qmbrfwcpwwhggszhrdjv",
        "type": azure_native.iotoperations.ExtendedLocationType.CUSTOM_LOCATION,
    },
    instance_name="resource-name123",
    listener_name="resource-name123",
    properties={
        "ports": [
            {
                "authentication_ref": "example-authentication",
                "port": 8080,
                "protocol": azure_native.iotoperations.BrokerProtocolType.WEB_SOCKETS,
            },
            {
                "authentication_ref": "example-authentication",
                "port": 8443,
                "protocol": azure_native.iotoperations.BrokerProtocolType.WEB_SOCKETS,
                "tls": {
                    "cert_manager_certificate_spec": {
                        "issuer_ref": {
                            "group": "jtmuladdkpasfpoyvewekmiy",
                            "kind": azure_native.iotoperations.CertManagerIssuerKind.ISSUER,
                            "name": "example-issuer",
                        },
                    },
                    "mode": azure_native.iotoperations.TlsCertMethodMode.AUTOMATIC,
                },
            },
            {
                "authentication_ref": "example-authentication",
                "port": 1883,
            },
            {
                "authentication_ref": "example-authentication",
                "port": 8883,
                "tls": {
                    "manual": {
                        "secret_ref": "example-secret",
                    },
                    "mode": azure_native.iotoperations.TlsCertMethodMode.MANUAL,
                },
            },
        ],
        "service_type": azure_native.iotoperations.ServiceType.LOAD_BALANCER,
    },
    resource_group_name="rgiotoperations")
package main

import (
	iotoperations "github.com/pulumi/pulumi-azure-native-sdk/iotoperations/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := iotoperations.NewBrokerListener(ctx, "brokerListener", &iotoperations.BrokerListenerArgs{
			BrokerName: pulumi.String("resource-name123"),
			ExtendedLocation: &iotoperations.ExtendedLocationArgs{
				Name: pulumi.String("qmbrfwcpwwhggszhrdjv"),
				Type: pulumi.String(iotoperations.ExtendedLocationTypeCustomLocation),
			},
			InstanceName: pulumi.String("resource-name123"),
			ListenerName: pulumi.String("resource-name123"),
			Properties: &iotoperations.BrokerListenerPropertiesArgs{
				Ports: iotoperations.ListenerPortArray{
					&iotoperations.ListenerPortArgs{
						AuthenticationRef: pulumi.String("example-authentication"),
						Port:              pulumi.Int(8080),
						Protocol:          pulumi.String(iotoperations.BrokerProtocolTypeWebSockets),
					},
					&iotoperations.ListenerPortArgs{
						AuthenticationRef: pulumi.String("example-authentication"),
						Port:              pulumi.Int(8443),
						Protocol:          pulumi.String(iotoperations.BrokerProtocolTypeWebSockets),
						Tls: &iotoperations.TlsCertMethodArgs{
							CertManagerCertificateSpec: &iotoperations.CertManagerCertificateSpecArgs{
								IssuerRef: &iotoperations.CertManagerIssuerRefArgs{
									Group: pulumi.String("jtmuladdkpasfpoyvewekmiy"),
									Kind:  pulumi.String(iotoperations.CertManagerIssuerKindIssuer),
									Name:  pulumi.String("example-issuer"),
								},
							},
							Mode: pulumi.String(iotoperations.TlsCertMethodModeAutomatic),
						},
					},
					&iotoperations.ListenerPortArgs{
						AuthenticationRef: pulumi.String("example-authentication"),
						Port:              pulumi.Int(1883),
					},
					&iotoperations.ListenerPortArgs{
						AuthenticationRef: pulumi.String("example-authentication"),
						Port:              pulumi.Int(8883),
						Tls: &iotoperations.TlsCertMethodArgs{
							Manual: &iotoperations.X509ManualCertificateArgs{
								SecretRef: pulumi.String("example-secret"),
							},
							Mode: pulumi.String(iotoperations.TlsCertMethodModeManual),
						},
					},
				},
				ServiceType: pulumi.String(iotoperations.ServiceTypeLoadBalancer),
			},
			ResourceGroupName: pulumi.String("rgiotoperations"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using AzureNative = Pulumi.AzureNative;

return await Deployment.RunAsync(() => 
{
    var brokerListener = new AzureNative.IoTOperations.BrokerListener("brokerListener", new()
    {
        BrokerName = "resource-name123",
        ExtendedLocation = new AzureNative.IoTOperations.Inputs.ExtendedLocationArgs
        {
            Name = "qmbrfwcpwwhggszhrdjv",
            Type = AzureNative.IoTOperations.ExtendedLocationType.CustomLocation,
        },
        InstanceName = "resource-name123",
        ListenerName = "resource-name123",
        Properties = new AzureNative.IoTOperations.Inputs.BrokerListenerPropertiesArgs
        {
            Ports = new[]
            {
                new AzureNative.IoTOperations.Inputs.ListenerPortArgs
                {
                    AuthenticationRef = "example-authentication",
                    Port = 8080,
                    Protocol = AzureNative.IoTOperations.BrokerProtocolType.WebSockets,
                },
                new AzureNative.IoTOperations.Inputs.ListenerPortArgs
                {
                    AuthenticationRef = "example-authentication",
                    Port = 8443,
                    Protocol = AzureNative.IoTOperations.BrokerProtocolType.WebSockets,
                    Tls = new AzureNative.IoTOperations.Inputs.TlsCertMethodArgs
                    {
                        CertManagerCertificateSpec = new AzureNative.IoTOperations.Inputs.CertManagerCertificateSpecArgs
                        {
                            IssuerRef = new AzureNative.IoTOperations.Inputs.CertManagerIssuerRefArgs
                            {
                                Group = "jtmuladdkpasfpoyvewekmiy",
                                Kind = AzureNative.IoTOperations.CertManagerIssuerKind.Issuer,
                                Name = "example-issuer",
                            },
                        },
                        Mode = AzureNative.IoTOperations.TlsCertMethodMode.Automatic,
                    },
                },
                new AzureNative.IoTOperations.Inputs.ListenerPortArgs
                {
                    AuthenticationRef = "example-authentication",
                    Port = 1883,
                },
                new AzureNative.IoTOperations.Inputs.ListenerPortArgs
                {
                    AuthenticationRef = "example-authentication",
                    Port = 8883,
                    Tls = new AzureNative.IoTOperations.Inputs.TlsCertMethodArgs
                    {
                        Manual = new AzureNative.IoTOperations.Inputs.X509ManualCertificateArgs
                        {
                            SecretRef = "example-secret",
                        },
                        Mode = AzureNative.IoTOperations.TlsCertMethodMode.Manual,
                    },
                },
            },
            ServiceType = AzureNative.IoTOperations.ServiceType.LoadBalancer,
        },
        ResourceGroupName = "rgiotoperations",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.iotoperations.BrokerListener;
import com.pulumi.azurenative.iotoperations.BrokerListenerArgs;
import com.pulumi.azurenative.iotoperations.inputs.ExtendedLocationArgs;
import com.pulumi.azurenative.iotoperations.inputs.BrokerListenerPropertiesArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var brokerListener = new BrokerListener("brokerListener", BrokerListenerArgs.builder()
            .brokerName("resource-name123")
            .extendedLocation(ExtendedLocationArgs.builder()
                .name("qmbrfwcpwwhggszhrdjv")
                .type("CustomLocation")
                .build())
            .instanceName("resource-name123")
            .listenerName("resource-name123")
            .properties(BrokerListenerPropertiesArgs.builder()
                .ports(                
                    ListenerPortArgs.builder()
                        .authenticationRef("example-authentication")
                        .port(8080)
                        .protocol("WebSockets")
                        .build(),
                    ListenerPortArgs.builder()
                        .authenticationRef("example-authentication")
                        .port(8443)
                        .protocol("WebSockets")
                        .tls(TlsCertMethodArgs.builder()
                            .certManagerCertificateSpec(CertManagerCertificateSpecArgs.builder()
                                .issuerRef(CertManagerIssuerRefArgs.builder()
                                    .group("jtmuladdkpasfpoyvewekmiy")
                                    .kind("Issuer")
                                    .name("example-issuer")
                                    .build())
                                .build())
                            .mode("Automatic")
                            .build())
                        .build(),
                    ListenerPortArgs.builder()
                        .authenticationRef("example-authentication")
                        .port(1883)
                        .build(),
                    ListenerPortArgs.builder()
                        .authenticationRef("example-authentication")
                        .port(8883)
                        .tls(TlsCertMethodArgs.builder()
                            .manual(X509ManualCertificateArgs.builder()
                                .secretRef("example-secret")
                                .build())
                            .mode("Manual")
                            .build())
                        .build())
                .serviceType("LoadBalancer")
                .build())
            .resourceGroupName("rgiotoperations")
            .build());

    }
}
resources:
  brokerListener:
    type: azure-native:iotoperations:BrokerListener
    properties:
      brokerName: resource-name123
      extendedLocation:
        name: qmbrfwcpwwhggszhrdjv
        type: CustomLocation
      instanceName: resource-name123
      listenerName: resource-name123
      properties:
        ports:
          - authenticationRef: example-authentication
            port: 8080
            protocol: WebSockets
          - authenticationRef: example-authentication
            port: 8443
            protocol: WebSockets
            tls:
              certManagerCertificateSpec:
                issuerRef:
                  group: jtmuladdkpasfpoyvewekmiy
                  kind: Issuer
                  name: example-issuer
              mode: Automatic
          - authenticationRef: example-authentication
            port: 1883
          - authenticationRef: example-authentication
            port: 8883
            tls:
              manual:
                secretRef: example-secret
              mode: Manual
        serviceType: LoadBalancer
      resourceGroupName: rgiotoperations

Each port in the ports array can specify a different protocol (MQTT or WebSockets) and TLS configuration. The tls property controls certificate provisioning: mode set to Automatic uses cert-manager with certManagerCertificateSpec pointing to an Issuer, while Manual mode references a Kubernetes secret via manual.secretRef. The authenticationRef property links each port to an authentication resource that validates client credentials. Setting serviceType to LoadBalancer exposes the listener externally, while ClusterIP keeps it internal to the cluster.

Beyond these examples

These snippets focus on specific listener-level features: port configuration and protocol selection, TLS certificate management (automatic and manual), and service exposure (ClusterIP and LoadBalancer). They’re intentionally minimal rather than full IoT broker deployments.

The examples reference pre-existing infrastructure such as IoT Operations broker and instance resources, Azure Arc custom location, authentication and authorization resources, and cert-manager Issuer and Kubernetes secrets. They focus on configuring the listener rather than provisioning the entire IoT Operations stack.

To keep things focused, common listener patterns are omitted, including:

  • Authorization policies (authorizationRef)
  • NodePort configuration for external access
  • Service naming (serviceName)
  • Advanced TLS options (certificate rotation, SAN configuration)

These omissions are intentional: the goal is to illustrate how each listener feature is wired, not provide drop-in IoT modules. See the BrokerListener resource reference for all available configuration options.

Let's configure Azure IoT Operations Broker Listeners

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration Basics
What properties can't I change after creating a broker listener?
The following properties are immutable: extendedLocation, brokerName, instanceName, listenerName, and resourceGroupName. Changes to these require resource replacement.
What's the minimal configuration needed for a broker listener?
You need extendedLocation (with type CustomLocation), brokerName, instanceName, listenerName, resourceGroupName, and at least one port in the ports array.
What type must extendedLocation be?
The extendedLocation.type must be CustomLocation, as shown in all examples.
TLS & Security
What's the difference between automatic and manual TLS modes?
Automatic mode uses certManagerCertificateSpec with an issuerRef to automatically provision certificates via cert-manager. Manual mode uses manual.secretRef to reference an existing Kubernetes secret containing the certificate.
How do I configure TLS for a listener port?
Add a tls object to the port configuration with mode set to either Automatic or Manual. For automatic, provide certManagerCertificateSpec with an issuerRef. For manual, provide manual.secretRef pointing to your certificate secret.
Are authentication and authorization required for each port?
No, authenticationRef and authorizationRef are optional. The simple example shows a port without these fields.
Networking & Ports
Can I configure multiple ports on a single listener?
Yes, the ports array supports multiple port configurations. Each port can have its own protocol, authentication, authorization, and TLS settings.
Do I need to specify a protocol for each port?
No, the protocol field is optional. You can specify Mqtt or WebSockets explicitly, or leave it unspecified.
What are the standard MQTT port numbers?
Port 1883 is standard for unencrypted MQTT, and port 8883 is standard for MQTT over TLS. The examples also show 8080 for WebSockets and 8443 for WebSockets over TLS.
What's the difference between ClusterIp and LoadBalancer service types?
ClusterIp exposes the listener only within the Kubernetes cluster, while LoadBalancer provisions an external load balancer for external access.
Advanced Configuration
When should I use the nodePort property?
The nodePort property is optional and specifies a static port on each cluster node. It’s shown in the full example but not required for basic configurations.
Can I mix TLS and non-TLS ports in the same listener?
Yes, you can configure some ports with TLS and others without. The complex example shows port 8080 without TLS and port 8443 with TLS.

Using a different cloud?

Explore messaging guides for other cloud providers: