Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 34

Consuming Custom Events from Azure Event Grid

Recommended resources

Hello! In our previous lesson, we successfully published custom events from a .NET application to an Azure Event Grid topic. We now have our application announcing important business moments, but no one is listening yet.

Today, we'll build the other half of this event-driven workflow. This lesson focuses on implementing an event handler to consume the custom events you learned to publish. We'll explore how to use Azure Functions to subscribe to an Event Grid topic and process incoming events. Your extensive experience in .NET and C# will be a great asset, as you'll be able to quickly understand the code required to process event data.

By the end of this lesson, you will be able to connect a subscriber to your custom topic and complete the end-to-end flow of publishing and consuming a custom event.

1. Handling Events with Azure Functions

Azure Functions is the most common and integrated way to handle events from Event Grid. It provides a serverless, event-driven compute experience that scales automatically. When implementing an event handler, you have two primary choices for your Azure Function trigger:

  1. Event Grid Trigger ([EventGridTrigger]): A specialized trigger designed specifically for Event Grid. It simplifies development by handling much of the boilerplate for you, including the initial security handshake. This is the recommended approach for new functions dedicated to handling Event Grid events.
  2. HTTP Trigger ([HttpTrigger]): A generic trigger that exposes your function as a standard HTTP endpoint (a webhook). You would use this if you need to integrate event handling into an existing API, if your handler is not an Azure Function, or if you require fine-grained control over the HTTP response codes to influence Event Grid's retry behavior.

We will explore both methods, as understanding their differences is important for the AZ-204 exam and for real-world architectural decisions.

This diagram shows the complete Event Grid flow. In the last lesson, we focused on the 'Event Sources' (our custom app) and 'Topics'. Today, we focus on the right side: 'Event Subscriptions' and 'Event Handlers' (our Azure Function).

2. The Direct Approach: Using the EventGridTrigger

Using the dedicated EventGridTrigger is the most straightforward way to create an event handler. The Azure Functions runtime takes care of the complex parts, allowing you to focus purely on your business logic.

The process involves:

  1. Creating an Azure Function with the EventGridTrigger template.
  2. Deploying the function to Azure.
  3. Creating an event subscription on your custom topic that points to your new function as the endpoint.

The following guide from Microsoft Learn walks you through this entire process.

Quickstart: Send custom events to an Azure function - Event Grid

This quickstart provides a complete, step-by-step tutorial for creating an Event Grid-triggered function, deploying it, and subscribing it to a topic.

As you read, please focus on these key sections: Create a function with an Event Grid trigger: Pay attention to the [EventGridTrigger] attribute in the generated C# code. This is what makes the function an event handler. Deploy the function to Azure: This section covers the standard deployment process from VS Code. Subscribe to a custom topic: This is the critical step where you connect the event source (your topic) to the event handler (your function). Note how you select Azure Function as the endpoint type and then choose the specific function you deployed.

When you use the EventGridTrigger, the function signature is simple and elegant. The runtime deserializes the incoming JSON payload into a CloudEvent or EventGridEvent object for you.

// Example of a function using the EventGridTrigger
using Azure.Messaging.EventGrid;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Functions.Worker;

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

    public EventGridHandler(ILogger<EventGridHandler> logger)
    {
        _logger = logger;
    }

    [Function(nameof(EventGridHandler))]
    public void Run([EventGridTrigger] CloudEvent cloudEvent)
    {
        _logger.LogInformation("Event received!");
        _logger.LogInformation("Event Type: {type}, Subject: {subject}", cloudEvent.Type, cloudEvent.Subject);

        // Your business logic here. For example, deserialize the data payload:
        // var orderData = cloudEvent.Data.ToObjectFromJson<Order>();
        // _logger.LogInformation($"Processing order: {orderData.OrderId}");
    }
}

The key takeaway is that the trigger binding handles the complexity, including a mandatory security handshake that we will discuss next.


3. The Flexible Approach: Using an HttpTrigger as a Webhook

What if your event handler is an existing web API, or you need to return specific HTTP status codes to control retries? In these cases, you would use a standard HttpTrigger and configure it as a webhook endpoint for your event subscription.

However, this flexibility comes with an added responsibility: endpoint validation. Before Event Grid sends any events to your endpoint, it must verify that you own and control it. This is done via a validation handshake.

The process is as follows:

  1. You create an event subscription pointing to your HTTP endpoint.
  2. Event Grid immediately sends a special event of type Microsoft.EventGrid.SubscriptionValidationEvent to your endpoint.
  3. This event contains a validationCode.
  4. Your code must receive this event, extract the validationCode, and return it in the HTTP response body.

If this handshake fails, the subscription will not be created.

The following video masterfully demonstrates this entire process, including what happens when the validation fails and how to write the C# code to handle it correctly.

AZ-204 - Connect and Consume Services - Azure Event Grid

This segment from Alan Rodrigues' video is an excellent practical guide to implementing the validation handshake for an HTTP-triggered function.

Watch the section from HTTP trigger validation. Pay close attention to these steps: The initial attempt to create a subscription for a standard HttpTrigger fails with a validation error. The presenter adds the Azure.Messaging.EventGrid NuGet package. The C# code is modified to read the request body, parse it into EventGridEvent objects, and specifically check for SubscriptionValidationEventData. When a validation event is found, its ValidationCode is extracted and returned in an OkObjectResult. With the validation logic in place, the subscription is created successfully.

Implementing the Handshake and Handling Custom Events in C#

To bring this all together, let's review the code needed to build a robust HTTP-triggered handler. The official Microsoft documentation provides a clear, consolidated example.

Receive events to an HTTP endpoint - Azure - Microsoft Learn

This article provides the specific C# code for handling both the validation handshake and subsequent custom events in a single HTTP-triggered function.

Please review two key sections: Endpoint validation: This section provides the C# code for handling the SubscriptionValidationEvent. Note the use of EventGridEvent.ParseMany and TryGetSystemEventData. Handle Custom events: This shows how to extend the function with an else if block to check for your specific custom event type and deserialize its data payload using eventGridEvent.Data.ToObjectFromJson<T>().

Here is a complete C# code example for an HttpTrigger function that correctly performs the validation handshake and then processes our custom com.mycompany.orders.placed event from the previous lesson.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Azure.Messaging.EventGrid;
using Azure.Messaging.EventGrid.SystemEvents;

// Define a class for our custom event data
public class OrderData
{
    public string OrderId { get; set; }
    public double Amount { get; set; }
    public string Currency { get; set; }
}

public static class HttpEventGridHandler
{
    [FunctionName("HttpEventGridHandler")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
        ILogger log)
    {
        log.LogInformation("HTTP trigger function processed a request.");

        BinaryData events = await BinaryData.FromStreamAsync(req.Body);
        EventGridEvent[] eventGridEvents = EventGridEvent.ParseMany(events);

        foreach (EventGridEvent eventGridEvent in eventGridEvents)
        {
            // 1. Handle the validation handshake
            if (eventGridEvent.TryGetSystemEventData(out object eventData))
            {
                if (eventData is SubscriptionValidationEventData subscriptionValidationEventData)
                {
                    log.LogInformation($"Got SubscriptionValidation event data. Validation code: {subscriptionValidationEventData.ValidationCode}");
                    
                    var responseData = new {
                        ValidationResponse = subscriptionValidationEventData.ValidationCode
                    };
                    return new OkObjectResult(responseData);
                }
            }
            // 2. Handle our custom event
            else if (eventGridEvent.EventType == "com.mycompany.orders.placed")
            {
                var orderData = eventGridEvent.Data.ToObjectFromJson<OrderData>();
                log.LogInformation($"Got custom event. Processing order: {orderData.OrderId}");
                // Add your business logic here...
            }
            else
            {
                log.LogWarning($"Unknown event type: {eventGridEvent.EventType}");
            }
        }

        return new OkObjectResult("");
    }
}

This pattern is fundamental to building custom webhooks that can securely subscribe to Event Grid.


Conclusion

In this lesson, you have completed the event-driven workflow by implementing a handler to consume custom events. You now understand the two primary methods for doing so with Azure Functions and the critical security handshake required for generic HTTP webhooks.

Here are your key takeaways:

  • Event handlers subscribe to topics to receive and process events. Azure Functions are a natural fit for this.
  • The EventGridTrigger is the simplest way to create a handler, as it automatically manages deserialization and the validation handshake.
  • The HttpTrigger offers more flexibility, allowing any HTTP endpoint to act as a handler, but it requires you to manually implement the endpoint validation handshake.
  • The validation process involves receiving a SubscriptionValidationEvent and echoing back the validationCode in the response.
  • The Azure.Messaging.EventGrid NuGet package provides the necessary classes (EventGridEvent, SubscriptionValidationEventData, etc.) to parse and handle incoming events in C#.

So far, we've focused on Event Grid, which excels at routing discrete events for reactive programming. However, Azure offers other eventing services for different scenarios. In our next lesson, we will introduce Azure Event Hubs, a service designed for high-throughput event streaming and big data telemetry ingestion. We will compare it to Event Grid to understand when to use each service.