🔀 Azure Eventing

Azure Event Grid
Complete Guide

From beginner to architecture level — system topics, custom topics, event domains, filtering, delivery guarantees, push vs pull, CloudEvents schema, Function Apps, Logic Apps, Service Bus, monitoring, and every event-driven pattern you need.

Beginner → Architecture22 Sections~10M events/secCloudEvents 1.0Push & Pull Delivery

01What Is Azure Event Grid?

Azure Event Grid is a fully managed event routing service that enables reactive, event-driven architectures by connecting event sources to event handlers with near-zero latency. Unlike messaging systems that store data, Event Grid is a notification backbone — it tells subscribers that something happenedand where to find the details. Built on a serverless model, it scales to ~10 million events per second with no infrastructure to manage.

Near-Zero Latency
Events routed to handlers in milliseconds — push-based, no polling required from subscribers
🌐
60+ Azure Sources
Built-in system topics for Blob Storage, Service Bus, Key Vault, Resource Manager, IoT Hub, and more
🎯
Advanced Filtering
Route events by type, subject prefix/suffix, or deep JSON property values — per subscription
🔁
Reliable Delivery
At-least-once delivery with exponential retry over 24 hours + dead-letter to blob storage
☁️
CloudEvents 1.0
Supports the CNCF CloudEvents open standard — interoperable with any CloudEvents-compatible system
📬
Push & Pull
Push to webhooks, Azure services, or pull from Event Grid namespaces for full consumer control

02Core Concepts & Terminology

Understanding Event Grid's vocabulary is essential before building event-driven systems. These terms define the building blocks — from the events themselves to the topics they flow through and the handlers that process them. Mastering this terminology helps you design clean subscription topologies and troubleshoot delivery issues quickly in production. Think of it as the mental model that maps directly to the Azure portal UI and CLI commands you'll use daily.

TermDefinition
EventThe smallest unit of information — what happened. Max 1 MB. Includes subject, type, data, and metadata.
Event SourceThe service or app that generated the event (Blob Storage, Key Vault, your custom app, etc.)
TopicAn endpoint where event sources publish events. Subscribers attach event subscriptions to topics.
System TopicA built-in topic representing an Azure service (Storage account, Service Bus namespace, etc.)
Custom TopicA user-created topic endpoint for publishing events from your own applications
Event DomainA management container for thousands of related topics — for multi-tenant SaaS scenarios
Event SubscriptionDefines where and how events from a topic are delivered. Has filters and a handler endpoint.
Event HandlerThe destination that receives and processes events (Function App, Logic App, webhook, Service Bus, etc.)
Dead-Letter DestinationA Blob Storage container where undeliverable events are sent after retry exhaustion
PublisherAny application or Azure service that sends events to a topic
SubscriberAny application or service that creates an event subscription and receives events
Namespace (new)The new resource model for Event Grid supporting pull delivery and MQTT — separate from classic topics

03Event Grid Schema vs CloudEvents Schema

Event Grid supports two event envelope formats: the original Azure-proprietary schema and the CNCF CloudEvents 1.0 standard. Choosing the right schema impacts interoperability — CloudEvents lets your event handlers work across Azure, AWS, GCP, and open-source platforms like Knative and Dapr without modification. For new projects, always prefer CloudEvents; for legacy integrations already using the Event Grid schema, migration is straightforward but optional. The schema you choose is set at the topic level and determines how subscribers parse incoming payloads.

Event Grid Schema (Classic)

json
[
  {
    "id": "b4a8e5e2-1234-4c3f-a9b0-12345abc6789",
    "topic": "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/myaccount",
    "subject": "/blobServices/default/containers/uploads/blobs/report.pdf",
    "eventType": "Microsoft.Storage.BlobCreated",
    "eventTime": "2026-05-04T10:30:00.000Z",
    "dataVersion": "1.0",
    "metadataVersion": "1",
    "data": {
      "api": "PutBlob",
      "clientRequestId": "abc123",
      "requestId": "def456",
      "eTag": "0x8D4BCC2E4835CD0",
      "contentType": "application/pdf",
      "contentLength": 524288,
      "blobType": "BlockBlob",
      "url": "https://myaccount.blob.core.windows.net/uploads/report.pdf",
      "sequencer": "00000000000004420000000000028963"
    }
  }
]

CloudEvents Schema (Recommended)

json
{
  "specversion": "1.0",
  "type": "Microsoft.Storage.BlobCreated",
  "source": "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/myaccount",
  "id": "b4a8e5e2-1234-4c3f-a9b0-12345abc6789",
  "time": "2026-05-04T10:30:00.000Z",
  "subject": "/blobServices/default/containers/uploads/blobs/report.pdf",
  "datacontenttype": "application/json",
  "data": {
    "api": "PutBlob",
    "url": "https://myaccount.blob.core.windows.net/uploads/report.pdf",
    "contentType": "application/pdf",
    "contentLength": 524288,
    "blobType": "BlockBlob"
  }
}
DimensionEvent Grid SchemaCloudEvents 1.0
StandardAzure-proprietaryCNCF open standard
InteroperabilityAzure services onlyAny CloudEvents-compatible system
ValidationEvent Grid subscription validation handshakeHTTP 200 with no body required
Recommended forLegacy integrationsAll new architectures
SDK supportAzure.Messaging.EventGridAzure.Messaging.EventGrid + CloudNative.CloudEvents
Use CloudEvents for New ProjectsCloudEvents 1.0 is the CNCF-standardized format. Using it makes your event handling code portable across clouds and compatible with open-source tools like Knative, Dapr, and Argo Events.

04Topics — System, Custom & Domain

Topics are the central routing endpoints in Event Grid — every event flows through a topic before reaching subscribers. System topics are automatically managed by Azure services (like Storage or Key Vault), custom topics are endpoints you create for your own application events, and event domains group thousands of topics under one management umbrella for multi-tenant scenarios. Choosing the right topic type determines your publishing model, access control boundaries, and how you scale your event architecture across teams and tenants.

System Topics

System topics are automatically available for Azure services. You create a system topic on an existing Azure resource — you don't publish to it yourself, the Azure service does.

Azure ServiceEvent Types (examples)
Azure Blob StorageBlobCreated, BlobDeleted, BlobRenamed, DirectoryCreated
Azure Service BusActiveMessagesAvailableWithNoListeners, DeadletterMessagesAvailableWithNoListeners
Azure Key VaultSecretNewVersionCreated, SecretNearExpiry, SecretExpired, CertificateNearExpiry
Azure Container RegistryImagePushed, ImageDeleted, ChartPushed, ChartDeleted
Azure Event HubsCaptureFileCreated
Azure MapsGeofenceEntered, GeofenceExited, GeofenceResult
Azure Resource ManagerResourceWriteSuccess, ResourceDeleteSuccess, ResourceActionSuccess
Azure App ConfigurationKeyValueModified, KeyValueDeleted
Azure SignalRClientConnectionConnected, ClientConnectionDisconnected
Azure Machine LearningRunCompleted, ModelRegistered, DatasetDriftDetected
IoT HubDeviceTelemetry, DeviceCreated, DeviceDeleted, DeviceConnected
Azure Communication ServicesSMSReceived, SMSDeliveryReportReceived, CallEnded

Create System Topic via CLI

bash
# Create a system topic for a Storage Account
az eventgrid system-topic create \
  --resource-group myRG \
  --name myStorageSystemTopic \
  --location eastus \
  --topic-type Microsoft.Storage.StorageAccounts \
  --source /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.Storage/storageAccounts/mystorageaccount

Custom Topics

Custom topics are endpoints you create and publish to from your own applications. You get an endpoint URL and an access key (or use Managed Identity).

bash
# Create a custom topic
az eventgrid topic create \
  --resource-group myRG \
  --name order-events \
  --location eastus \
  --input-schema cloudeventschemav1_0 \
  --public-network-access enabled

05Event Handlers & Subscriptions

Event handlers are the destinations that receive and process events — they define what happens when an event arrives. Event Grid supports a wide range of handler types from serverless functions to message brokers, giving you flexibility to match each event to the right processing model. Subscriptions tie a handler to a topic with optional filters, creating a declarative routing rule that Event Grid enforces automatically. In production, you'll typically have multiple subscriptions per topic, each routing a subset of events to a specialized handler.

Handler TypeDescriptionBest For
Azure FunctionServerless compute — scales automatically per eventCustom processing logic, transformations
Logic AppLow-code workflow orchestrationMulti-step integrations, approvals, SaaS connectors
Webhook (HTTPS)Any HTTPS endpoint — your own API or third-partyCustom microservices, external systems
Service Bus QueueReliable message delivery with ordering and DLQDecoupled processing, reliable task dispatch
Service Bus TopicFan-out to multiple subscribers via topic filtersMultiple downstream consumers per event
Event HubHigh-throughput streaming of events into a hubAnalytics pipelines, aggregation
Storage QueueSimple, durable queue for background tasksLow-cost, simple task dispatch
Azure RelayBridge to on-premises endpointsHybrid cloud/on-prem event routing

Create Event Subscription (Function Handler)

bash
# Create subscription pointing to an Azure Function
az eventgrid event-subscription create \
  --name blob-created-sub \
  --source-resource-id /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.Storage/storageAccounts/mystorageaccount \
  --endpoint-type azurefunction \
  --endpoint /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.Web/sites/myFunctionApp/functions/ProcessBlob \
  --included-event-types Microsoft.Storage.BlobCreated \
  --subject-begins-with /blobServices/default/containers/uploads/ \
  --deadletter-endpoint /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.Storage/storageAccounts/mydeadletter/blobServices/default/containers/deadletter

Create Event Subscription — ARM / Bicep

bicep
resource eventSubscription 'Microsoft.EventGrid/eventSubscriptions@2022-06-15' = {
  name: 'order-created-subscription'
  scope: customTopic
  properties: {
    destination: {
      endpointType: 'ServiceBusTopic'
      properties: {
        resourceId: serviceBusTopic.id
      }
    }
    filter: {
      includedEventTypes: [ 'Orders.Created', 'Orders.Updated' ]
      subjectBeginsWith: '/orders/'
      advancedFilters: [
        {
          operatorType: 'NumberGreaterThan'
          key: 'data.amount'
          value: 100
        }
      ]
    }
    deadLetterDestination: {
      endpointType: 'StorageBlob'
      properties: {
        resourceId: storageAccount.id
        blobContainerName: 'deadletter'
      }
    }
    retryPolicy: {
      maxDeliveryAttempts: 30
      eventTimeToLiveInMinutes: 1440
    }
    eventDeliverySchema: 'CloudEventSchemaV1_0'
  }
}

06Event Filtering & Routing

Event filtering is what makes Event Grid a smart router rather than a dumb pipe — each subscription can declare exactly which events it wants based on type, subject path, or deep JSON properties in the event data. This server-side filtering means your handlers only receive relevant events, reducing compute costs and simplifying handler logic. In production, effective filtering is the difference between a clean architecture and a noisy one where every handler must discard irrelevant events. Combine subject prefix filters with advanced property filters to create precise, cost-efficient routing rules.

Filter Types

Filter TypePropertyDescription
Event Type FilterincludedEventTypesOnly deliver specific event types (e.g. BlobCreated)
Subject Begins WithsubjectBeginsWithFilter by subject prefix — e.g. /containers/uploads/
Subject Ends WithsubjectEndsWithFilter by subject suffix — e.g. .pdf or .jpg
Advanced — StringContainsdata.* propertyFilter on a JSON property inside the event data
Advanced — NumberGreaterThandata.* propertyNumeric comparison on event data properties
Advanced — BoolEqualsdata.* propertyBoolean match on event data properties
Advanced — StringIndata.* propertyValue must be in a specified list
Advanced — IsNullOrUndefineddata.* propertyProperty is absent or null

Advanced Filters — JSON

json
{
  "filter": {
    "includedEventTypes": ["Orders.Created"],
    "subjectBeginsWith": "/orders/",
    "advancedFilters": [
      {
        "operatorType": "StringIn",
        "key": "data.region",
        "values": ["EU-West", "EU-North"]
      },
      {
        "operatorType": "NumberGreaterThanOrEquals",
        "key": "data.amount",
        "value": 500
      },
      {
        "operatorType": "BoolEquals",
        "key": "data.isPriority",
        "value": true
      },
      {
        "operatorType": "StringNotContains",
        "key": "data.status",
        "values": ["cancelled", "refunded"]
      }
    ]
  }
}
💡
Max 25 Advanced Filters Per SubscriptionEach event subscription supports up to 25 advanced filter conditions. For complex routing needs, use a fan-out pattern: one subscription per logical consumer group, each with its own filter set. Or use a Service Bus topic with SQL subscription filters for more powerful server-side routing.

07Delivery, Retry & Dead-Letter

Event Grid guarantees at-least-once delivery with a sophisticated retry mechanism that uses exponential backoff over a configurable window (default 24 hours, up to 30 attempts). Understanding the retry and dead-letter behavior is critical for production reliability — without proper dead-letter configuration, failed events are silently dropped after retry exhaustion. The retry policy distinguishes between transient errors (retried) and permanent client errors (dropped immediately), so your handlers should return appropriate HTTP status codes to signal whether a retry would help.

Retry Schedule

Event Grid retries delivery with an exponential backoff with jitter strategy. The total retry window is configurable (default 24 hours) and the max delivery attempts are also configurable (default 30).

AttemptRetry Delay (approx)
1st retry10 seconds
2nd retry30 seconds
3rd retry1 minute
4th retry5 minutes
5th retry10 minutes
6th retry30 minutes
7th+ retry1 hour (max interval)
After max attempts or TTLDead-letter (if configured) or drop

Events Are Dropped (Not Retried) When

ConditionBehavior
Handler returns 400 Bad RequestDropped immediately — not retried (client error)
Handler returns 413 Payload Too LargeDropped immediately
Handler returns 403 ForbiddenDropped immediately
Handler returns 404 Not FoundDropped — subscription endpoint invalid
Handler returns 503 / 500Retried with backoff — transient error
Handler returns 429 Too Many RequestsRetried — respects Retry-After header
TTL expired (default 24h)Dead-lettered or dropped
Max delivery attempts reachedDead-lettered or dropped

Dead-Letter Configuration

bash
# Add dead-letter storage to an existing subscription
az eventgrid event-subscription update \
  --name my-subscription \
  --source-resource-id <topic-resource-id> \
  --deadletter-endpoint \
    /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.Storage/storageAccounts/mydeadletter/blobServices/default/containers/dead-events

# Dead-letter blob path format:
# <container>/<topic-name>/<subscription-name>/<year>/<month>/<day>/<hour>/<eventId>.json
⚠️
Configure Dead-Lettering in ProductionWithout a dead-letter destination, undeliverable events are silently dropped after retry exhaustion. Always configure a dead-letter blob container and set up a monitor alert on the DeadLetteredCount metric.

08Push Delivery vs Pull Delivery

Classic Event Grid uses push delivery — Event Grid calls your handler endpoint. The new Event Grid Namespaces resource introduces native pull delivery — your consumer requests events on its own schedule, like Service Bus. This is ideal when your consumer is behind a firewall or needs full flow control.

DimensionPush Delivery (Classic)Pull Delivery (Namespace)
ModelEvent Grid calls handler webhookConsumer polls Event Grid for events
Consumer locationMust be publicly reachableCan be behind firewall / VNet
Flow controlEvent Grid controls paceConsumer controls pace
BackpressureNot supported — can overwhelm handlerFull consumer-side control
Lock modelNo lockingLock, acknowledge, reject, release
Competing consumersNot supported (fan-in)Supported — multiple consumers share queue
Handler typesFunction, Logic App, SB, webhook, etc.Any consumer via SDK / REST
Max event size1 MB1 MB
LatencySub-secondDepends on poll interval
Resource typetopics / systemTopicsnamespaces / topics / eventSubscriptions

Pull Delivery — Receive & Acknowledge

csharp
using Azure.Messaging.EventGrid.Namespaces;

var client = new EventGridReceiverClient(
    new Uri("https://<namespace>.eastus-1.eventgrid.azure.net"),
    "<topic-name>",
    "<subscription-name>",
    new DefaultAzureCredential());

// Receive a batch of events (with lock — must acknowledge within lock duration)
ReceiveResult result = await client.ReceiveAsync(maxEvents: 10, maxWaitTime: TimeSpan.FromSeconds(30));

var lockTokens = new List<string>();
foreach (ReceiveDetails detail in result.Value)
{
    CloudEvent cloudEvent = detail.Event;
    BrokerProperties broker = detail.BrokerProperties;

    Console.WriteLine($"Event: {cloudEvent.Type}, LockToken: {broker.LockToken}");

    // Process the event...
    bool success = await ProcessEventAsync(cloudEvent);

    if (success)
        lockTokens.Add(broker.LockToken); // Acknowledge
    else
        await client.ReleaseAsync(new[] { broker.LockToken }); // Return to queue
}

// Acknowledge successfully processed events
if (lockTokens.Any())
    await client.AcknowledgeAsync(lockTokens);

09Security & Authentication

Securing Event Grid involves two sides: authenticating publishers who send events to topics, and validating that Event Grid can legitimately deliver events to your handler endpoints. In production, always use Managed Identity or Entra ID service principals instead of access keys — keys are shared secrets that are difficult to rotate and easy to leak. The webhook validation handshake ensures Event Grid only delivers to endpoints you actually own, preventing unauthorized subscriptions from exfiltrating your events to attacker-controlled URLs.

Publishing Authentication

MethodUse CaseRecommended?
Access Key (SAS)Quick dev/test publishing⚠️ Avoid in production
SAS Token (time-limited)Short-lived publishing access✓ Acceptable
Managed Identity (Entra ID)App Service, Functions, AKS publishing✅ Recommended
Service Principal (Entra ID)CI/CD pipelines✅ Recommended

Webhook Validation Handshake

When you create a subscription pointing to a webhook, Event Grid sends a validation event. Your endpoint must respond with thevalidationCode within 5 minutes or the subscription creation fails.

csharp
// ASP.NET Core webhook handler with validation
[HttpPost("webhook")]
public async Task<IActionResult> ReceiveEvent()
{
    string requestContent = await new StreamReader(Request.Body).ReadToEndAsync();

    // CloudEvents uses HTTP OPTIONS for handshake — different from Event Grid schema
    if (Request.Headers["WebHook-Request-Origin"].Count > 0)
    {
        // CloudEvents webhook validation
        Response.Headers["WebHook-Allowed-Origin"] = Request.Headers["WebHook-Request-Origin"].ToString();
        return Ok();
    }

    // Event Grid schema validation
    var events = JsonSerializer.Deserialize<JsonElement[]>(requestContent);
    foreach (var evt in events!)
    {
        if (evt.GetProperty("eventType").GetString() == "Microsoft.EventGrid.SubscriptionValidationEvent")
        {
            var validationCode = evt.GetProperty("data")
                                    .GetProperty("validationCode").GetString();
            return Ok(new { validationResponse = validationCode });
        }

        // Process real events
        await HandleEventAsync(evt);
    }
    return Ok();
}

RBAC Roles for Event Grid

RolePermissions
EventGrid Data SenderPublish events to a topic
EventGrid Data ReceiverPull events from namespace subscriptions
EventGrid ContributorManage Event Grid resources (control plane)
EventGrid EventSubscription ContributorCreate and manage event subscriptions
EventGrid EventSubscription ReaderRead event subscription properties

10Network Security & Private Endpoints

For enterprise deployments, Event Grid supports private endpoints and IP firewall rules to ensure events never traverse the public internet. Private endpoints allow publishers inside your VNet to send events through Azure's backbone network, and managed identity-based delivery ensures handlers receive events securely without exposing public webhook URLs. This is essential for compliance-sensitive workloads in healthcare, finance, and government where data must stay within your network boundary. Note that system topics have limited networking options compared to custom topics and namespaces.

FeatureCustom TopicsSystem TopicsNamespaces
IP Firewall Rules✓ Yes✗ No✓ Yes
Private Endpoints (inbound publishing)✓ Yes✗ No✓ Yes
Private Endpoints (pull delivery)N/AN/A✓ Yes
Managed Identity for delivery✓ Yes✓ Yes✓ Yes
VNet peering for webhook deliveryVia App Service VNetVia App Service VNet✓ Yes

Private Endpoint for Custom Topic (Publishing)

bash
# Create private endpoint so publishers inside VNet can publish securely
az network private-endpoint create \
  --name egrid-publish-pe \
  --resource-group myRG \
  --vnet-name myVNet \
  --subnet mySubnet \
  --private-connection-resource-id \
    /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.EventGrid/topics/order-events \
  --group-ids topic \
  --connection-name egrid-private-conn

# Private DNS zone for Event Grid
az network private-dns zone create \
  --resource-group myRG \
  --name "privatelink.eventgrid.azure.net"

11Logic Apps Integration

Logic Apps provides a low-code way to subscribe to Event Grid events and orchestrate multi-step workflows in response — no custom code required. The Event Grid trigger connector automatically handles the webhook validation handshake and subscription lifecycle, making it the fastest path from event to action for scenarios like sending Teams alerts, creating tickets, or calling external APIs. This integration shines for operations teams who need to react to infrastructure events (like Key Vault secret expiry or resource changes) without deploying custom functions. Use Logic Apps when the response involves multiple steps, conditional branching, or connectors to SaaS services.

ConnectorDirectionDescription
When an Event Grid event occursTriggerSubscribe to any system or custom topic event type
Publish eventActionPublish a custom event to an Event Grid topic

Event Grid Trigger in Logic App

json
{
  "triggers": {
    "When_a_resource_event_occurs": {
      "type": "ApiConnectionWebhook",
      "inputs": {
        "host": {
          "connection": {
            "name": "@parameters('$connections')['azureeventgrid']['connectionId']"
          }
        },
        "path": "/subscriptions/@{encodeURIComponent('<sub-id>')}/providers/@{encodeURIComponent('Microsoft.KeyVault.vaults')}/resource/eventSubscriptions",
        "body": {
          "properties": {
            "destination": {
              "endpointType": "webhook",
              "properties": { "endpointUrl": "@{listCallbackUrl()}" }
            },
            "filter": {
              "includedEventTypes": [
                "Microsoft.KeyVault.SecretNearExpiry",
                "Microsoft.KeyVault.SecretExpired"
              ]
            }
          }
        }
      }
    }
  },
  "actions": {
    "Send_Teams_Alert": {
      "type": "Http",
      "inputs": {
        "method": "POST",
        "uri": "@parameters('teamsWebhookUrl')",
        "body": {
          "text": "🔑 Key Vault Alert: @{triggerBody()?['subject']} — @{triggerBody()?['eventType']}"
        }
      }
    }
  }
}

12Function Apps Integration

Azure Functions is the most popular handler for Event Grid events — the Event Grid trigger binding gives you automatic scaling, built-in retry handling, and zero infrastructure management. Functions can both consume events (via trigger bindings) and produce events (via output bindings), making them ideal for event transformation, enrichment, and fan-out scenarios. In production, use the isolated worker model (.NET 8+) for better dependency injection, middleware support, and process isolation. The trigger supports both Event Grid schema and CloudEvents, so your function code stays clean regardless of the envelope format.

Event Grid Trigger (Isolated Worker — .NET 8)

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

public class BlobEventProcessor
{
    private readonly ILogger<BlobEventProcessor> _logger;

    public BlobEventProcessor(ILogger<BlobEventProcessor> logger) => _logger = logger;

    [Function("ProcessBlobEvent")]
    public async Task Run(
        [EventGridTrigger] EventGridEvent eventGridEvent)
    {
        _logger.LogInformation("Event: {Type}, Subject: {Subject}",
            eventGridEvent.EventType, eventGridEvent.Subject);

        if (eventGridEvent.EventType == "Microsoft.Storage.BlobCreated")
        {
            var data = eventGridEvent.Data!.ToObjectFromJson<BlobCreatedData>();
            _logger.LogInformation("Blob URL: {Url}, Size: {Size}", data.Url, data.ContentLength);
            await ProcessNewBlobAsync(data.Url);
        }
    }
}

// CloudEvents trigger
public class OrderEventProcessor
{
    [Function("ProcessOrderEvent")]
    public async Task Run(
        [EventGridTrigger] CloudEvent cloudEvent,
        ILogger log)
    {
        log.LogInformation("CloudEvent type: {Type}", cloudEvent.Type);

        var order = cloudEvent.Data!.ToObjectFromJson<OrderCreatedData>();
        await RouteOrderAsync(order);
    }
}

public record BlobCreatedData(string Url, string ContentType, long ContentLength, string BlobType);
public record OrderCreatedData(string OrderId, decimal Amount, string Region, bool IsPriority);

Publish Events from a Function

csharp
[Function("CreateOrder")]
[EventGridOutput(TopicEndpointUri = "ORDER_EVENTS_ENDPOINT", TopicKeySetting = "ORDER_EVENTS_KEY")]
public static EventGridEvent Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req)
{
    var order = JsonSerializer.Deserialize<Order>(req.Body);
    return new EventGridEvent(
        subject: $"/orders/{order.OrderId}",
        eventType: "Orders.Created",
        dataVersion: "1.0",
        data: order);
}

// CloudEvents output binding
[Function("CreateOrderCloudEvent")]
public async Task<CloudEvent> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
    var order = await req.ReadFromJsonAsync<Order>();
    return new CloudEvent(
        source: "/myapp/orders",
        type: "Orders.Created",
        jsonSerializableData: order)
    {
        Subject = $"/orders/{order!.OrderId}",
        Id = Guid.NewGuid().ToString()
    };
}

13Service Bus Integration

Routing Event Grid events into Service Bus queues or topics is one of the most powerful enterprise patterns — you get Event Grid's real-time push routing combined withService Bus's reliable delivery, DLQ, sessions, and ordered processing.

bash
# Route Event Grid events to a Service Bus topic
az eventgrid event-subscription create \
  --name order-to-servicebus \
  --source-resource-id <custom-topic-id> \
  --endpoint-type servicebustopic \
  --endpoint /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.ServiceBus/namespaces/<namespace>/topics/<topic-name> \
  --included-event-types Orders.Created Orders.Updated \
  --subject-begins-with /orders/
PatternWhen to Use
Event Grid → Service Bus QueueReliable task dispatch for competitive consumers. One processor gets each order.
Event Grid → Service Bus TopicFan-out with filtering. Billing, shipping, analytics all get independent copies.
Event Grid → Service Bus + SessionsOrdered processing per entity (e.g. all events for order-456 in sequence).
Event Grid → Function App → Service BusTransform / enrich events before queuing — add correlation IDs, normalize schema.

14Event Hubs Integration

Routing Event Grid events into Event Hubs is ideal when you need to feed events into a stream analytics pipeline or need high-throughput event aggregation from multiple sources into a single stream.

bash
# Route to Event Hub for stream analytics ingestion
az eventgrid event-subscription create \
  --name resource-changes-to-eventhub \
  --source-resource-id /subscriptions/<sub> \
  --endpoint-type eventhub \
  --endpoint /subscriptions/<sub>/resourceGroups/<rg>/providers/\
Microsoft.EventHub/namespaces/<namespace>/eventhubs/<hub-name> \
  --included-event-types Microsoft.Resources.ResourceWriteSuccess \
                          Microsoft.Resources.ResourceDeleteSuccess
💡
Event Grid + Event Hubs for Azure Resource AuditSubscribing to Azure Resource Manager events and routing to Event Hubs gives you a real-time audit log of all infrastructure changes across your subscription — feed into Sentinel or Splunk for SIEM analysis.

15Blob Storage Integration

Blob Storage system topics are the most commonly used Event Grid integration. Instead of polling Storage with Logic Apps (1-min delay), events are pushed instantly when blobs are created, deleted, or renamed.

Event TypeTrigger Condition
Microsoft.Storage.BlobCreatedA blob is uploaded (PutBlob, PutBlockList, CopyBlob, FlushWithClose)
Microsoft.Storage.BlobDeletedA blob is deleted (DeleteBlob or when soft-deleted blob is permanently removed)
Microsoft.Storage.BlobRenamedA blob in ADLS Gen2 is renamed (HierarchicalNamespace enabled)
Microsoft.Storage.DirectoryCreatedA directory in ADLS Gen2 is created
Microsoft.Storage.DirectoryDeletedA directory in ADLS Gen2 is deleted
Microsoft.Storage.BlobTierChangedA blob's access tier is changed (Hot → Cool → Archive)

Pattern: Blob Upload → Process → Store Result

csharp
// Function triggered by blob-created event from Event Grid
[Function("ProcessUploadedDocument")]
public async Task Run(
    [EventGridTrigger] EventGridEvent eventGridEvent,
    [BlobInput("{data.url}", Connection = "StorageConnection")] Stream blobStream)
{
    if (eventGridEvent.EventType != "Microsoft.Storage.BlobCreated") return;

    var data = eventGridEvent.Data!.ToObjectFromJson<StorageBlobCreatedEventData>();

    // Only process PDFs from the 'uploads' container
    if (!data.Url.Contains("/uploads/") || data.ContentType != "application/pdf")
        return;

    _logger.LogInformation("Processing uploaded PDF: {Url}", data.Url);

    // Call Azure Document Intelligence
    var result = await _documentClient.AnalyzeDocumentAsync(blobStream);

    // Store extracted data to Cosmos DB
    await _cosmosContainer.UpsertItemAsync(new
    {
        id = data.BlobUrl.Split('/').Last(),
        extractedText = result.Content,
        uploadedAt = eventGridEvent.EventTime,
        blobUrl = data.Url
    });
}

16Publishing Custom Events

Publishing custom events lets your own applications participate in the Event Grid ecosystem alongside Azure services. You send events to a custom topic endpoint using the SDK, REST API, or output bindings, and Event Grid handles routing, filtering, and delivery to all subscribers. This is the foundation of domain event patterns in microservices — each service publishes business events (OrderCreated, PaymentProcessed) and other services react without direct coupling. Always use Managed Identity for authentication in production, batch events when publishing at high volume, and prefer the CloudEvents format for portability.

Publish via SDK — .NET

csharp
using Azure.Messaging.EventGrid;
using Azure;

// With access key
var client = new EventGridPublisherClient(
    new Uri("https://order-events.<region>.eventgrid.azure.net/api/events"),
    new AzureKeyCredential("<topic-key>"));

// With Managed Identity (recommended)
var client = new EventGridPublisherClient(
    new Uri("https://order-events.<region>.eventgrid.azure.net/api/events"),
    new DefaultAzureCredential());

// Publish Event Grid schema events
var events = new List<EventGridEvent>
{
    new EventGridEvent(
        subject:     "/orders/ORD-12345",
        eventType:   "Orders.Created",
        dataVersion: "1.0",
        data: new
        {
            orderId  = "ORD-12345",
            amount   = 299.99,
            region   = "EU-West",
            customer = new { id = "C001", email = "user@example.com" }
        }),
    new EventGridEvent(
        subject:     "/orders/ORD-12346",
        eventType:   "Orders.Created",
        dataVersion: "1.0",
        data: new { orderId = "ORD-12346", amount = 149.00, region = "US-East" })
};
await client.SendEventsAsync(events);

// Publish CloudEvents (recommended)
var cloudEvents = new List<CloudEvent>
{
    new CloudEvent(
        source:               "/myapp/orders",
        type:                 "Orders.Created",
        jsonSerializableData: new { orderId = "ORD-12347", amount = 99.99 })
    {
        Subject = "/orders/ORD-12347",
        Id      = Guid.NewGuid().ToString(),
        Time    = DateTimeOffset.UtcNow
    }
};
await client.SendCloudEventsAsync(cloudEvents);

Publish via REST API

bash
# Publish a CloudEvent via curl
curl -X POST https://order-events.<region>.eventgrid.azure.net/api/events \
  -H "Content-Type: application/cloudevents+json" \
  -H "aeg-sas-key: <topic-access-key>" \
  -d '{
    "specversion": "1.0",
    "type": "Orders.Created",
    "source": "/myapp/orders",
    "id": "unique-event-id-1",
    "time": "2026-05-04T10:00:00Z",
    "subject": "/orders/ORD-12345",
    "datacontenttype": "application/json",
    "data": { "orderId": "ORD-12345", "amount": 299.99 }
  }'

17Event Domains (Multi-tenant)

An Event Domain is a single management endpoint that contains thousands of topics— one per tenant or per entity. Instead of creating individual topics for each customer, you publish to one domain endpoint with a topic name, and each customer subscribes only to their own topic. Perfect for SaaS platforms.

csharp
// Publish to a domain — topic name is per-tenant
var domainClient = new EventGridPublisherClient(
    new Uri("https://my-domain.<region>.eventgrid.azure.net/api/events"),
    new DefaultAzureCredential());

// Include topic in the event — routes to tenant's topic automatically
var events = new[]
{
    new EventGridEvent(
        subject:     "/invoices/INV-001",
        eventType:   "Invoice.Created",
        dataVersion: "1.0",
        data:        new { invoiceId = "INV-001", amount = 500.0 })
    {
        // Topic routes to tenant-a's topic within the domain
        Topic = "tenant-a"
    },
    new EventGridEvent(
        subject:     "/invoices/INV-002",
        eventType:   "Invoice.Created",
        dataVersion: "1.0",
        data:        new { invoiceId = "INV-002", amount = 750.0 })
    {
        Topic = "tenant-b"
    }
};
await domainClient.SendEventsAsync(events);
FeatureDetail
Max topics per domain100,000
Topics auto-createdYes — topic is created on first event publish
Per-topic access keysYes — each tenant gets scoped access to only their topic
Event subscriptionsCreated per topic within the domain
Use caseSaaS: one domain per product, one topic per customer/tenant

18Event Grid vs Service Bus vs Event Hubs

One of the most common architecture questions in Azure is when to use Event Grid versus Service Bus versus Event Hubs — they serve fundamentally different purposes despite all handling "events" or "messages." Event Grid is a reactive notification router (something happened), Service Bus is a reliable message broker (do this task), and Event Hubs is a high-throughput stream ingestion platform (capture everything). In practice, production systems often combine all three: Event Grid triggers the workflow, Service Bus ensures reliable task processing, and Event Hubs captures the full event stream for analytics and replay.

DimensionEvent GridService BusEvent Hubs
Primary purposeReactive event routing — notify that something happenedReliable message brokering — task dispatchHigh-throughput stream ingestion
ModelPush-based routing (pub/sub)Queue or topic (P2P or pub/sub)Partitioned log — consumers pull
Throughput~10M events/sec~1M messages/sec (Premium)Millions/sec
Retention24h (retry), then DLQUp to 14 days1–90 days
Message orderingNo guaranteeFIFO with sessionsPer-partition ordering
Replay✗ No✗ No✓ Yes
Dead-letterBlob StorageNative DLQ sub-queue✗ No
Max event/msg size1 MB256 KB / 100 MB (Premium)1 MB
Competing consumers✗ No✓ YesVia consumer groups
Fan-outMultiple subscriptionsMultiple topic subscriptionsMultiple consumer groups
Kafka compatible✗ No✗ No✓ Yes
Use whenReacting to Azure resource or app eventsReliable task queues, workflowsIoT, telemetry, clickstreams

19Monitoring & Diagnostics

Monitoring Event Grid in production means tracking the full event lifecycle — from publish success through delivery to dead-lettering. Azure Monitor exposes key metrics that tell you whether events are flowing correctly, and diagnostic logs give you per-event visibility when things go wrong. Set up alerts on DeadLetteredCount and DeliveryFailureCount as your first line of defense — these indicate events that your handlers never processed. Use KQL queries in Log Analytics to investigate delivery failures, identify problematic subscriptions, and track event latency trends over time.

MetricDescriptionAlert Condition
PublishSuccessCountSuccessfully published eventsDrop = publisher issue
PublishFailCountFailed publish attempts> 0 — alert immediately
DeliverySuccessCountSuccessfully delivered to handlersLow vs publish count = handler issue
DeliveryFailureCountDelivery failures to handlers> 0 — alert
DeadLetteredCountEvents sent to dead-letter destination> 0 — alert immediately
DroppedEventCountEvents dropped (no DLQ configured)> 0 — configure DLQ
MatchedEventCountEvents matching at least one subscriptionCompare to published count
UnmatchedEventCountEvents with no matching subscription> 0 — check filter config

KQL — Dead-Lettered Events in Last 24h

kql
AzureDiagnostics
| where ResourceType == "TOPICS" or ResourceType == "SYSTEMTOPICS"
| where OperationName == "DeadLetterEvent"
| where TimeGenerated > ago(24h)
| project TimeGenerated, Resource, subject_s, eventType_s,
          deadLetterReason_s, deliveryAttemptCount_d
| order by TimeGenerated desc

KQL — Delivery Failure Analysis

kql
AzureMetrics
| where ResourceType == "MICROSOFT.EVENTGRID/TOPICS"
| where MetricName in ("DeliveryFailureCount", "DeadLetteredCount")
| where TimeGenerated > ago(1h)
| summarize TotalFailures = sum(Total) by bin(TimeGenerated, 5m), MetricName, Resource
| where TotalFailures > 0
| order by TimeGenerated desc

20Architecture Patterns

Event Grid enables a variety of proven architecture patterns for event-driven systems — from simple file-processing triggers to complex multi-service choreography and saga orchestration. These patterns represent battle-tested approaches used in production by organizations ranging from startups to enterprises. The key insight is that Event Grid acts as the connective tissue between services, eliminating direct dependencies and enabling each component to evolve independently. Choose the pattern that matches your reliability, latency, and scalability requirements.

📁
Blob-Triggered Processing
File uploaded to Blob → Event Grid (BlobCreated) → Function App → AI processing → results to Cosmos DB. No polling, instant trigger.
🔑
Secret Rotation Automation
Key Vault SecretNearExpiry event → Event Grid → Function App rotates credential at source → updates KV version. Zero-downtime rotation.
🏗️
Infrastructure Change Audit
ARM ResourceWriteSuccess events → Event Grid → Event Hub → Stream Analytics → Log Analytics. Real-time infrastructure audit trail.
📣
Multi-Service Fan-Out
Order created event → Event Grid → 4 subscriptions: billing queue, shipping function, analytics hub, Teams notification.
🏢
SaaS Event Domain
One Event Grid domain per product. One topic per customer. Customers subscribe to their topic only. Scales to 100k tenants.
🔄
Saga Orchestration
Each microservice publishes completion events to Event Grid. Saga coordinator subscribes and drives the next step or compensating action.

21Pricing Overview

Event Grid follows a pure consumption-based pricing model with a generous free tier — the first 100,000 operations per month cost nothing, making it essentially free for development and low-volume production workloads. An "operation" includes publishing, delivery attempts, and filter evaluations, so efficient filtering directly reduces your bill. For high-volume scenarios processing millions of events daily, costs remain remarkably low compared to always-on compute alternatives. The key cost optimization lever is minimizing unnecessary filter evaluations by using specific event type filters and subject prefixes.

ResourcePrice (approx)
First 100,000 operations/monthFree
Operations (publish + delivery + filter)~$0.60 per million operations
Advanced filter evaluations~$0.60 per million filter evaluations
Dead-letter storageStandard Azure Blob Storage pricing
Event Grid Namespaces (pull)~$0.40 per million operations
MQTT messaging (Namespace)~$0.40 per million messages
💰
Cost OptimizationEach subscription filter evaluation counts as an operation — use specificincludedEventTypes filters to avoid evaluating events that will never match. Use subject filters to reduce the event volume reaching subscriptions. Batch events in custom topics where possible.

22Quick Reference Cheat Sheet

This cheat sheet consolidates the most frequently needed Event Grid endpoints, CLI commands, and service limits into a single quick-reference section. Keep this handy during development and troubleshooting — it covers the exact URL patterns for publishing, the CLI commands you'll run most often, and the hard limits that constrain your architecture decisions. Bookmark this section for those moments when you need to quickly look up a dead-letter path format or check the maximum number of subscriptions per topic.

Endpoints & URIs
text
# Custom Topic endpoint
https://<topic-name>.<region>.eventgrid.azure.net/api/events

# Event Domain endpoint
https://<domain-name>.<region>.eventgrid.azure.net/api/events

# Namespace (pull delivery)
https://<namespace>.<region-key>.eventgrid.azure.net

# Dead-letter blob path
<container>/<topic>/<subscription>/<year>/<month>/<day>/<hour>/<eventId>.json
Essential CLI Commands
bash
# Create custom topic (CloudEvents)
az eventgrid topic create -g myRG -n my-topic -l eastus \
  --input-schema cloudeventschemav1_0

# Create system topic for Storage
az eventgrid system-topic create -g myRG -n my-storage-topic \
  --topic-type Microsoft.Storage.StorageAccounts \
  --source <storage-account-resource-id>

# Create subscription to Function App
az eventgrid event-subscription create \
  --name my-sub --source-resource-id <topic-id> \
  --endpoint-type azurefunction --endpoint <function-resource-id> \
  --included-event-types MyApp.OrderCreated

# List subscriptions on a topic
az eventgrid event-subscription list --source-resource-id <topic-id>

# Test delivery (send a test event)
az eventgrid topic event-subscription show --name my-sub --topic-name my-topic -g myRG

# View dead-letter events
az storage blob list --account-name mydeadletter --container-name dead-events
LimitValue
Max event size1 MB
Max batch size1 MB total
Max events per batchUnlimited (constrained by batch size)
Max subscriptions per topic500
Max advanced filters per subscription25
Max filter values per advanced filter25
Delivery retry window24 hours (configurable)
Max delivery attempts30 (configurable)
Max topics per domain100,000
Max domains per subscription100
Throughput (events/sec)~10 million