Configure Azure Dapr PubSub Event Subscriptions

The azure-native:app:DaprSubscription resource, part of the Pulumi Azure Native provider, defines a Dapr pub/sub subscription within a Container Apps environment: which topic to subscribe to, how to route events, and which applications can receive them. This guide focuses on three capabilities: event routing (default and conditional), bulk message delivery, and application scoping.

Dapr subscriptions connect to pub/sub components configured in a Container Apps managed environment and may reference deployed container apps for scoping. The examples are intentionally small. Combine them with your own environment, pub/sub components, and application deployments.

Subscribe to a topic with a default route

Most subscriptions connect an environment to a pub/sub topic and route all events to a single 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 specifies which pub/sub topic to subscribe to. The routes.default property sets the HTTP path where your application receives events; Dapr delivers all messages from the topic to this endpoint.

Route events conditionally with match expressions

Applications often route different event types to specialized handlers rather than processing everything through one 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",
    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 based on event properties. Each rule has a match expression (using Common Expression Language syntax) and a path. Dapr evaluates rules in order; events matching “event.type == ‘widget’” go to “/widgets”, while “gadget” events go to “/gadgets”. Unmatched events fall back to the default route. The metadata property passes key-value pairs to the underlying pub/sub component for broker-specific configuration.

Optimize throughput with bulk subscribe and scopes

High-volume subscriptions benefit from bulk message delivery to reduce network overhead. Scopes restrict which applications can receive events.

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 batch delivery. Dapr waits up to maxAwaitDurationMs (500 milliseconds) or until maxMessagesCount (123 messages) accumulates before delivering a batch to your application. The scopes array limits subscription visibility to specific apps; only “warehouseapp” and “customersupportapp” receive events from this subscription, even if other apps in the environment subscribe to the same topic.

Beyond these examples

These snippets focus on specific subscription-level features: topic routing (default and conditional), bulk message delivery, and application scoping. 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 referenced in scopes. 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)
  • Custom metadata for pub/sub components
  • Error handling and retry policies

These omissions are intentional: the goal is to illustrate how each subscription feature is wired, not provide drop-in event processing 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 properties can't I change after creating a Dapr subscription?
The name, environmentName, and resourceGroupName properties are immutable. Changing any of these requires replacing the resource.
What's the minimal configuration needed for a Dapr subscription?
You need environmentName, name, pubsubName, resourceGroupName, topic, and a routes object with at least a default path.
How do I specify which PubSub component to use?
Set the pubsubName property to the name of your Dapr PubSub component (e.g., mypubsubcomponent).
Event Routing
How do I route events to my application endpoint?
Configure routes.default with the path where events should be delivered (e.g., /products). This handles all events that don’t match specific rules.
How do I route different event types to different endpoints?
Use routes.rules with match expressions and paths. For example, match: "event.type == 'widget'" with path: "/widgets" routes widget events to the /widgets endpoint.
What happens to events that don't match any route rules?
They’re sent to the routes.default path, which acts as a fallback for unmatched events.
Can I configure dead letter handling for failed events?
Yes, use the deadLetterTopic property to specify a topic name where failed events should be sent.
Advanced Features
How do I enable bulk message processing?
Configure bulkSubscribe with enabled: true. You can also set maxMessagesCount (e.g., 123) and maxAwaitDurationMs (e.g., 500) to control batch size and timing.
How do I restrict a subscription to specific apps?
Use the scopes array to list app names (e.g., ["warehouseapp", "customersupportapp"]). Only these apps will receive events from the subscription.
Can I pass custom metadata to my subscription?
Yes, use the metadata object to pass key-value pairs (e.g., {"foo": "bar", "hello": "world"}).

Using a different cloud?

Explore messaging guides for other cloud providers: