Azure Functions can process Event Grid events directly without needing a separate queue or other intermediate messaging service.
Let’s see it in action. Imagine you have an Azure Storage account, and you want to react whenever a new blob is created.
{
"topic": "/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Storage/storageAccounts/{storage-account-name}",
"subject": "/blobServices/default/containers/{container-name}/blobs/{blob-name}",
"eventType": "Microsoft.Storage.BlobCreated",
"eventTime": "2023-10-27T10:00:00.1234567Z",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"data": {
"api": "PutBlob",
"clientRequestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"requestId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"eTag": "0x8DXXXXXX",
"contentType": "application/json",
"contentLength": 1024,
"blobType": "BlockBlob",
"url": "https://{storage-account-name}.blob.core.windows.net/{container-name}/{blob-name}"
},
"dataVersion": "1.0",
"metadataVersion": "1"
}
This is a typical Event Grid event payload. Notice the eventType and subject. Our Azure Function needs to be configured to listen for these specific events from this specific topic.
In your Azure Function project, you’ll define a function that’s triggered by an Event Grid event. The binding configuration in your function.json looks like this:
{
"scriptFile": "__init__.py",
"bindings": [
{
"name": "event",
"type": "eventGridTrigger",
"direction": "in"
}
]
}
The eventGridTrigger binding makes the entire Event Grid event object available to your function code. In Python, your function signature would look like:
import logging
import json
import azure.functions as func
def main(event: func.EventGridEvent):
logging.info(f"Event Grid Trigger processed an event: {event.id}")
logging.info(f"Event Type: {event.event_type}")
logging.info(f"Subject: {event.subject}")
logging.info(f"Data: {json.dumps(event.get_json())}")
# Your custom logic here, e.g., process the blob
if event.event_type == "Microsoft.Storage.BlobCreated":
blob_url = event.get_json()["url"]
logging.info(f"A new blob was created at: {blob_url}")
The key here is the func.EventGridEvent type. It provides methods like get_json() to parse the event data and properties to access metadata like event_type and subject.
To make this work, you need to create an Event Subscription in Azure. You’ll point this subscription to your Azure Function’s endpoint. When creating the subscription, you select the source (e.g., your Storage Account) and the event types you’re interested in (e.g., Microsoft.Storage.BlobCreated). Crucially, you specify the Azure Function as the destination. Azure Event Grid then handles the delivery of these events directly to your function.
The beauty is that Event Grid manages the reliable delivery. If your function is temporarily unavailable, Event Grid will retry. You can configure retry policies and dead-lettering for events that cannot be processed.
When you deploy this, Azure automatically creates the necessary Event Subscription for you if you deploy using certain tooling (like Azure Functions Core Tools or certain CI/CD pipelines) and configure it correctly. However, you can also manually create the subscription through the Azure portal or CLI.
The event.get_json() method is a convenience. Under the hood, the event payload is passed as a string, and this method parses it into a Python dictionary. If you’re working with a different language, you’d typically deserialize the incoming request body.
When you have multiple events delivered in a single HTTP request (which Event Grid does for efficiency), the event parameter in your function will be a list of func.EventGridEvent objects. Your code should be prepared to iterate through this list.
import logging
import json
import azure.functions as func
def main(events: list[func.EventGridEvent]):
logging.info(f"Processing {len(events)} Event Grid events.")
for event in events:
logging.info(f"Event ID: {event.id}")
logging.info(f"Event Type: {event.event_type}")
logging.info(f"Subject: {event.subject}")
# Process each event individually
if event.event_type == "Microsoft.Storage.BlobCreated":
blob_url = event.get_json()["url"]
logging.info(f"Blob created: {blob_url}")
This batching behavior is a performance optimization by Event Grid. Instead of sending one HTTP request per event, it groups several events into one request. Your function, by accepting a list, handles this efficiently.
The most surprising thing about Event Grid triggers is how seamlessly they integrate with Azure Functions. You don’t need to write HTTP endpoints yourself; the trigger binding handles the incoming webhook and deserializes the payload into a strongly-typed object. This abstracts away the complexities of receiving and validating webhook requests, allowing you to focus purely on the business logic that reacts to the event.
The next thing you’ll likely encounter is handling different event schemas from various Azure services, or perhaps implementing custom event publishers.