Creating an Azure EventGrid Webhook in ASP.NET Core

Azure EventGrid has first class support for Azure Functions, Azure Logic Apps, and Azure Automation. However, writing your own webhook in ASP.NET Core is a piece of cake and I'm going to show you how to do it (because I just wrote one yesterday 😬).

Install the EventGrid Azure SDK NuGet package

We're gonna use the Microsoft.Azure.EventGrid package to use the models defined in it.

Run this from your project directory:

dotnet add package Microsoft.Azure.EventGrid --version 2.0.0

Or if you're within VS, run this in the Package Manager prompt:

Install-Package Microsoft.Azure.EventGrid -Version 2.0.0

Like I mentioned earlier, we're only going to be using these for the EventSchema models. So you may also choose to just copy the relevant model classes if you don't want an external dependency.

Creating the webhook

There are essentially 4 steps to creating a functioning webhook for EventGrid:

  1. Bind the http request body to an EventGridEvent[] object.
  2. Check if the event type is what you're interested in.
  3. If it is a type of your interest, go ahead and cast its Data field to a corresponding model and do whatever you'd like to.
  4. If the event is a subscription validation event, respond with a subscription validation response to complete the event subscription handshake.

Here's an example, that reacts to the JobChanged event published by Azure Media Services when a job status is changed.

[Route("/api/webhooks"), AllowAnonymous]
public class WebhooksController : Controller
{
    // POST: /api/webhooks/handle_ams_jobchanged
    [HttpPost("handle_ams_jobchanged")] // <-- Must be an HTTP POST action
    public IActionResult ProcessAMSEvent(
        [FromBody]EventGridEvent[] ev, // 1. Bind the request
        [FromServices]ILogger<WebhooksController> logger)
    {
        var amsEvent = ev.FirstOrDefault(); // TODO: handle all of them!
        if(amsEvent == null) return BadRequest();
        
        // 2. Check the eventType field
        if (amsEvent.EventType == EventTypes.MediaJobStateChangeEvent)
        {
            // 3. Cast the data to the expected type
            var data = (amsEvent.Data as JObject).ToObject<MediaJobStateChangeEventData>();
            
            // TODO: do your thing; eg:
            logger.LogInformation(JsonConvert.SerializeObject(data, Formatting.Indented));
        }
        
        // 4. Respond with a SubscriptionValidationResponse to complete the 
        // event subscription handshake.
        if(amsEvent.EventType == EventTypes.EventGridSubscriptionValidationEvent)
        {
            var data = (amsEvent.Data as JObject).ToObject<SubscriptionValidationEventData>();
            var response = new SubscriptionValidationResponse(data.ValidationCode);
            return Ok(response);
        }
        return BadRequest();
    }
}

Testing the webhook

I found ngrok to be the easiest way to debug the webhook.

All you'll need to do is:

  1. Debug the application from VS or VS Code, and set a breakpoint in the webhook action.
  2. Generate an https url with ngrok: ngrok http {port-number}.
  3. Create a webhook using the https ngrok url (eg. https://subdomain.ngrok.io/api/webhooks/handle_ams_jobchanged) and send events to it to test them.

Next steps

  1. ⚡ Handle all the events. The above action only handles the first event in the event array.
  2. 🔒️ Check for a key in the url to see if it matches with a configuration value. If it doesn't, return a 404. This will ensure only the required parties have access to the complete webhook url.

References:

  1. Receive events to an HTTP endpoint - Microsoft Docs | Link.
  2. Event Grid security and authentication - Microsoft Docs | Link.
  3. Azure EventGrid C# SDK Source Code - GitHub | Link.
  4. Event-Driven Architecture in the Cloud with Azure Event Grid - MSDN Magazine | Link.
  5. Make Your .Net Core Http Endpoint to React to Custom Azure Event Grid Events  - Subhankar Sarkar| Link.