Serverless Doorbell — Ring.com and Azure Functions (Part 2)
Part 2 — Subscribing to Event Grid and pushing to Cosmos DB
This is part 2 in a series of how I have been using Azure Serverless to extend and connect the functionality of my ring.com video doorbell. Part 1 is here.
During my first afternoon I got my doorbell sending events to Azure Event Grid whenever it detected motion or was pressed. The next step was to create subscribers to those events. At the time of writing this I’ve created two subscribers — create a log entry in Cosmos DB, and analyze the recording with Azure Cognitive Services. I’ll save the Cognitive Services for part 3. This blog will go into detail of wiring-up Event Grid to Azure Functions, and how to use Cosmos DB bindings in the new v2 runtime to create a document.
Azure Functions and the Event Grid Trigger
This was my first opportunity to build a function trigger from Event Grid and I learned a lot in the process. The first is that you actually have 2 options for triggers when working with event grid.
Event Grid extension trigger
The first option is to use the Event Grid extension with the eventGridTrigger
type in your function.json
file. This is the method that is used if you select the Event Grid template in the Azure Functions portal experience. There are a few aspects worth calling out with this method:
- Automatically de-batches event grid messages — event grid subscribers can receive multiple messages in a single request (nested in a top-level array). The event grid trigger will automatically split them for you.
- Potential for HTTP optimizations — Hasn’t yet been implemented, but the event grid trigger could queue the message “behind-the-scenes” allowing for greater parallelism and distribution of work across function instances than straight HTTP.
- No current way to locally test — the trigger exposes an endpoint when using the portal template, but not an obvious way to trigger it locally while I was authoring in VS Code (even after manually installing the extension with the Azure Functions Core Tools).
HTTP trigger
You can also just use and manually register any HTTP triggered function to fire on events from Event Grid. The difference is pretty much the counter to each of the points above (requires manual debatch, wouldn’t leverage queues in the same way, but can locally test).
For purposes of easier local debugging I went with an HttpTrigger function which will create a document in Cosmos DB about the ring event.
Creating documents in Cosmos DB with bindings
Azure Functions has a concept in addition to triggers called ‘bindings.’ A binding is a small bit of standard code to allow for faster integration with different services. Bindings include things like Azure Storage (pull in or push out blobs), Azure Queues, Cosmos DB, and about 20 more.
For my ring.com Azure Functions, I have been developing them in VS Code on my MacBook, which means I’ve been using the current preview (and cross-platform) v2 Function Runtime. This is relevant because in v2 a few changes happened in relation to the Cosmos DB (previously DocumentDB) integration with Azure Functions.
Installing Binding Extensions in Azure Functions v2 runtime
The first big difference when developing my Cosmos DB ring.com functionality is I had to manually install the Cosmos DB binding extension into my project. One of the goals of the v2 runtime is keep the base image minimal, so unused extensions wouldn’t exist on each function instance. This means I need to specify which NuGet packages to include in the function app that the host will include when running.
The experience isn’t quite as seamless as we want it to be yet, but for now here’s how I enabled Cosmos DB:
- Found the function extension on nuget.org — these start with
Microsoft.Azure.WebJobs.Extensions.*
and the one for Cosmos DB is here. - At the root of the project, I used the Azure Functions Core Tools to install the extensions —
func extensions install -p Microsoft.Azure.WebJobs.Extensions.CosmosDb -v 3.0.0-beta6
- This adds a new
functions-extensions
folder to the project with anextensions.csproj
file that acts like an index of extensions being used in the project. In fact, if cloning the project you can run the commandfunc extensions sync
to download and include any extensions referenced in this file.
Once the setup was complete, I could add the Cosmos DB binding to the function.json
config file for my HttpTrigger function.
NOTE: There are some differences between this binding and the v1 DocumentDB binding, like connectionStringSetting
and the type
{
"name": "ringDocument",
"type": "cosmosDB",
"databaseName": "ring",
"collectionName": "events",
"createIfNotExists": true,
"connectionStringSetting": "CosmosDbConnectionString",
"direction": "out"
}
Once setup was complete, the function code is actually extremely simple. It will trigger on an Event Grid event, pull out the first event, and add it to the Cosmos DB collection via the binding configured above.
The final step was to deploy the function, and create an event subscription in the Event Grid topic created in part 1. Now whenever my doorbell gets pressed or motion is detected, an event is automatically stored in Cosmos DB for me.
In hindsight doing something this simple would have likely only taken me 1/10th of the time to build in Azure Logic Apps with the Event Grid trigger and Cosmos DB connector, but was worth building this way to learn some of the ins and outs of the Azure Functions v2 Cosmos DB binding.
In the next part I will be extending my serverless solution to include Logic Apps to orchestrate some powerful video AI whenever someone approaches the doorbell.
Continue to Part 3 — Facial Recognition powered by Cognitive Services and Logic Apps