Configure Azure Dapr PubSub Event Subscriptions

The azure-native:app:DaprSubscription resource, part of the Pulumi Azure Native provider, connects a Dapr pub/sub component to a topic and defines how messages are routed to Container Apps. This guide focuses on three capabilities: basic topic subscription with single-route delivery, content-based routing with conditional rules, and bulk message delivery and application scoping.

Dapr subscriptions belong to a Container Apps managed environment and require a configured pub/sub component and deployed container apps. The examples are intentionally small. Combine them with your own environment, pub/sub components, and application deployments.

Subscribe to a topic with a single route

Most subscriptions connect an environment to a pub/sub topic and route all messages to one application endpoint.

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

const daprSubscription = new azure_native.app.DaprSubscription("daprSubscription", {
    environmentName: "myenvironment",
    name: "mysubscription",
    pubsubName: "mypubsubcomponent",
    resourceGroupName: "examplerg",
    routes: {
        "default": "/products",
    },
    topic: "inventory",
});
import pulumi
import pulumi_azure_native as azure_native

dapr_subscription = azure_native.app.DaprSubscription("daprSubscription",
    environment_name="myenvironment",
    name="mysubscription",
    pubsub_name="mypubsubcomponent",
    resource_group_name="examplerg",
    routes={
        "default": "/products",
    },
    topic="inventory")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := app.NewDaprSubscription(ctx, "daprSubscription", &app.DaprSubscriptionArgs{
			EnvironmentName:   pulumi.String("myenvironment"),
			Name:              pulumi.String("mysubscription"),
			PubsubName:        pulumi.String("mypubsubcomponent"),
			ResourceGroupName: pulumi.String("examplerg"),
			Routes: &app.DaprSubscriptionRoutesArgs{
				Default: pulumi.String("/products"),
			},
			Topic: pulumi.String("inventory"),
		})
		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 daprSubscription = new AzureNative.App.DaprSubscription("daprSubscription", new()
    {
        EnvironmentName = "myenvironment",
        Name = "mysubscription",
        PubsubName = "mypubsubcomponent",
        ResourceGroupName = "examplerg",
        Routes = new AzureNative.App.Inputs.DaprSubscriptionRoutesArgs
        {
            Default = "/products",
        },
        Topic = "inventory",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.app.DaprSubscription;
import com.pulumi.azurenative.app.DaprSubscriptionArgs;
import com.pulumi.azurenative.app.inputs.DaprSubscriptionRoutesArgs;
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 daprSubscription = new DaprSubscription("daprSubscription", DaprSubscriptionArgs.builder()
            .environmentName("myenvironment")
            .name("mysubscription")
            .pubsubName("mypubsubcomponent")
            .resourceGroupName("examplerg")
            .routes(DaprSubscriptionRoutesArgs.builder()
                .default_("/products")
                .build())
            .topic("inventory")
            .build());

    }
}
resources:
  daprSubscription:
    type: azure-native:app:DaprSubscription
    properties:
      environmentName: myenvironment
      name: mysubscription
      pubsubName: mypubsubcomponent
      resourceGroupName: examplerg
      routes:
        default: /products
      topic: inventory

The pubsubName references a Dapr component configured in your Container Apps environment. The topic property specifies which pub/sub topic to subscribe to. The routes.default property sets the HTTP path where your container app receives messages; Dapr posts each message to this endpoint.

Route messages by content with conditional rules

When multiple event types share a topic, routing rules direct messages to specialized handlers based on content.

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

const daprSubscription = new azure_native.app.DaprSubscription("daprSubscription", {
    environmentName: "myenvironment",
    metadata: {
        foo: "bar",
        hello: "world",
    },
    name: "mysubscription",
    pubsubName: "mypubsubcomponent",
    resourceGroupName: "examplerg",
    routes: {
        "default": "/products",
        rules: [
            {
                match: "event.type == 'widget'",
                path: "/widgets",
            },
            {
                match: "event.type == 'gadget'",
                path: "/gadgets",
            },
        ],
    },
    topic: "inventory",
});
import pulumi
import pulumi_azure_native as azure_native

dapr_subscription = azure_native.app.DaprSubscription("daprSubscription",
    environment_name="myenvironment",
    metadata={
        "foo": "bar",
        "hello": "world",
    },
    name="mysubscription",
    pubsub_name="mypubsubcomponent",
    resource_group_name="examplerg",
    routes={
        "default": "/products",
        "rules": [
            {
                "match": "event.type == 'widget'",
                "path": "/widgets",
            },
            {
                "match": "event.type == 'gadget'",
                "path": "/gadgets",
            },
        ],
    },
    topic="inventory")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := app.NewDaprSubscription(ctx, "daprSubscription", &app.DaprSubscriptionArgs{
			EnvironmentName: pulumi.String("myenvironment"),
			Metadata: pulumi.StringMap{
				"foo":   pulumi.String("bar"),
				"hello": pulumi.String("world"),
			},
			Name:              pulumi.String("mysubscription"),
			PubsubName:        pulumi.String("mypubsubcomponent"),
			ResourceGroupName: pulumi.String("examplerg"),
			Routes: &app.DaprSubscriptionRoutesArgs{
				Default: pulumi.String("/products"),
				Rules: app.DaprSubscriptionRouteRuleArray{
					&app.DaprSubscriptionRouteRuleArgs{
						Match: pulumi.String("event.type == 'widget'"),
						Path:  pulumi.String("/widgets"),
					},
					&app.DaprSubscriptionRouteRuleArgs{
						Match: pulumi.String("event.type == 'gadget'"),
						Path:  pulumi.String("/gadgets"),
					},
				},
			},
			Topic: pulumi.String("inventory"),
		})
		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 daprSubscription = new AzureNative.App.DaprSubscription("daprSubscription", new()
    {
        EnvironmentName = "myenvironment",
        Metadata = 
        {
            { "foo", "bar" },
            { "hello", "world" },
        },
        Name = "mysubscription",
        PubsubName = "mypubsubcomponent",
        ResourceGroupName = "examplerg",
        Routes = new AzureNative.App.Inputs.DaprSubscriptionRoutesArgs
        {
            Default = "/products",
            Rules = new[]
            {
                new AzureNative.App.Inputs.DaprSubscriptionRouteRuleArgs
                {
                    Match = "event.type == 'widget'",
                    Path = "/widgets",
                },
                new AzureNative.App.Inputs.DaprSubscriptionRouteRuleArgs
                {
                    Match = "event.type == 'gadget'",
                    Path = "/gadgets",
                },
            },
        },
        Topic = "inventory",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.app.DaprSubscription;
import com.pulumi.azurenative.app.DaprSubscriptionArgs;
import com.pulumi.azurenative.app.inputs.DaprSubscriptionRoutesArgs;
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 daprSubscription = new DaprSubscription("daprSubscription", DaprSubscriptionArgs.builder()
            .environmentName("myenvironment")
            .metadata(Map.ofEntries(
                Map.entry("foo", "bar"),
                Map.entry("hello", "world")
            ))
            .name("mysubscription")
            .pubsubName("mypubsubcomponent")
            .resourceGroupName("examplerg")
            .routes(DaprSubscriptionRoutesArgs.builder()
                .default_("/products")
                .rules(                
                    DaprSubscriptionRouteRuleArgs.builder()
                        .match("event.type == 'widget'")
                        .path("/widgets")
                        .build(),
                    DaprSubscriptionRouteRuleArgs.builder()
                        .match("event.type == 'gadget'")
                        .path("/gadgets")
                        .build())
                .build())
            .topic("inventory")
            .build());

    }
}
resources:
  daprSubscription:
    type: azure-native:app:DaprSubscription
    properties:
      environmentName: myenvironment
      metadata:
        foo: bar
        hello: world
      name: mysubscription
      pubsubName: mypubsubcomponent
      resourceGroupName: examplerg
      routes:
        default: /products
        rules:
          - match: event.type == 'widget'
            path: /widgets
          - match: event.type == 'gadget'
            path: /gadgets
      topic: inventory

The routes.rules array defines conditional routing: each rule has a match expression that evaluates message properties and a path for matching messages. Messages that don’t match any rule fall back to routes.default. The metadata property passes key-value pairs to the underlying pub/sub component, configuring broker-specific behavior.

Optimize throughput with bulk delivery and scopes

High-volume subscriptions batch messages into single requests to reduce network overhead, and scopes control which apps receive messages.

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

const daprSubscription = new azure_native.app.DaprSubscription("daprSubscription", {
    bulkSubscribe: {
        enabled: true,
        maxAwaitDurationMs: 500,
        maxMessagesCount: 123,
    },
    environmentName: "myenvironment",
    name: "mysubscription",
    pubsubName: "mypubsubcomponent",
    resourceGroupName: "examplerg",
    routes: {
        "default": "/products",
    },
    scopes: [
        "warehouseapp",
        "customersupportapp",
    ],
    topic: "inventory",
});
import pulumi
import pulumi_azure_native as azure_native

dapr_subscription = azure_native.app.DaprSubscription("daprSubscription",
    bulk_subscribe={
        "enabled": True,
        "max_await_duration_ms": 500,
        "max_messages_count": 123,
    },
    environment_name="myenvironment",
    name="mysubscription",
    pubsub_name="mypubsubcomponent",
    resource_group_name="examplerg",
    routes={
        "default": "/products",
    },
    scopes=[
        "warehouseapp",
        "customersupportapp",
    ],
    topic="inventory")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := app.NewDaprSubscription(ctx, "daprSubscription", &app.DaprSubscriptionArgs{
			BulkSubscribe: &app.DaprSubscriptionBulkSubscribeOptionsArgs{
				Enabled:            pulumi.Bool(true),
				MaxAwaitDurationMs: pulumi.Int(500),
				MaxMessagesCount:   pulumi.Int(123),
			},
			EnvironmentName:   pulumi.String("myenvironment"),
			Name:              pulumi.String("mysubscription"),
			PubsubName:        pulumi.String("mypubsubcomponent"),
			ResourceGroupName: pulumi.String("examplerg"),
			Routes: &app.DaprSubscriptionRoutesArgs{
				Default: pulumi.String("/products"),
			},
			Scopes: pulumi.StringArray{
				pulumi.String("warehouseapp"),
				pulumi.String("customersupportapp"),
			},
			Topic: pulumi.String("inventory"),
		})
		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 daprSubscription = new AzureNative.App.DaprSubscription("daprSubscription", new()
    {
        BulkSubscribe = new AzureNative.App.Inputs.DaprSubscriptionBulkSubscribeOptionsArgs
        {
            Enabled = true,
            MaxAwaitDurationMs = 500,
            MaxMessagesCount = 123,
        },
        EnvironmentName = "myenvironment",
        Name = "mysubscription",
        PubsubName = "mypubsubcomponent",
        ResourceGroupName = "examplerg",
        Routes = new AzureNative.App.Inputs.DaprSubscriptionRoutesArgs
        {
            Default = "/products",
        },
        Scopes = new[]
        {
            "warehouseapp",
            "customersupportapp",
        },
        Topic = "inventory",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.azurenative.app.DaprSubscription;
import com.pulumi.azurenative.app.DaprSubscriptionArgs;
import com.pulumi.azurenative.app.inputs.DaprSubscriptionBulkSubscribeOptionsArgs;
import com.pulumi.azurenative.app.inputs.DaprSubscriptionRoutesArgs;
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 daprSubscription = new DaprSubscription("daprSubscription", DaprSubscriptionArgs.builder()
            .bulkSubscribe(DaprSubscriptionBulkSubscribeOptionsArgs.builder()
                .enabled(true)
                .maxAwaitDurationMs(500)
                .maxMessagesCount(123)
                .build())
            .environmentName("myenvironment")
            .name("mysubscription")
            .pubsubName("mypubsubcomponent")
            .resourceGroupName("examplerg")
            .routes(DaprSubscriptionRoutesArgs.builder()
                .default_("/products")
                .build())
            .scopes(            
                "warehouseapp",
                "customersupportapp")
            .topic("inventory")
            .build());

    }
}
resources:
  daprSubscription:
    type: azure-native:app:DaprSubscription
    properties:
      bulkSubscribe:
        enabled: true
        maxAwaitDurationMs: 500
        maxMessagesCount: 123
      environmentName: myenvironment
      name: mysubscription
      pubsubName: mypubsubcomponent
      resourceGroupName: examplerg
      routes:
        default: /products
      scopes:
        - warehouseapp
        - customersupportapp
      topic: inventory

The bulkSubscribe configuration enables batching: maxMessagesCount sets the batch size, and maxAwaitDurationMs controls how long Dapr waits to fill a batch before delivering. The scopes array restricts message delivery to specific container apps by name, preventing unauthorized apps from receiving messages.

Beyond these examples

These snippets focus on specific subscription-level features: topic subscription and message routing, bulk delivery optimization, and application scoping and metadata. They’re intentionally minimal rather than full event-driven architectures.

The examples reference pre-existing infrastructure such as Container Apps managed environments, Dapr pub/sub component configurations, and container apps for message delivery. They focus on configuring the subscription rather than provisioning the surrounding infrastructure.

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

  • Dead letter topic configuration (deadLetterTopic)
  • Advanced route rule expressions and operators
  • Retry policies and error handling
  • Multiple pub/sub components in one environment

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

Let's configure Azure Dapr PubSub Event Subscriptions

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Setup
What are the required properties for creating a Dapr subscription?
You must provide name (subscription name), environmentName (Managed Environment name), resourceGroupName, pubsubName (Dapr PubSub component), and topic (topic name to subscribe to).
What properties can't I change after creating the subscription?
The name, environmentName, and resourceGroupName properties are immutable. Changing any of these requires destroying and recreating the resource.
How do I add custom metadata to my subscription?
Use the metadata property with key-value pairs, such as {"foo": "bar", "hello": "world"}.
Event Routing
How do I route events to my application endpoint?
Configure routes with a default path (e.g., /products) for all events. For conditional routing, add rules with match expressions and specific path values, like "match": "event.type == 'widget'" routing to "/widgets".
What's the difference between default route and route rules?
The default route handles all events that don’t match any rules. Route rules use match expressions to conditionally route specific event types to different paths.
Performance & Optimization
How do I enable bulk message processing for better performance?
Configure bulkSubscribe with enabled: true, maxAwaitDurationMs (e.g., 500), and maxMessagesCount (e.g., 123) to process multiple messages in batches.
Can I restrict which apps receive subscription events?
Yes, use the scopes array to specify app names (e.g., ["warehouseapp", "customersupportapp"]) that should receive events from this subscription.
Error Handling
How do I configure dead letter handling for failed messages?
Set the deadLetterTopic property to specify a topic name where failed messages should be sent.
Resource Management
How do I import an existing Dapr subscription?
Use pulumi import azure-native:app:DaprSubscription <name> /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.App/managedEnvironments/{environmentName}/daprSubscriptions/{name}.

Using a different cloud?

Explore messaging guides for other cloud providers: