Event Grid trigger for Azure Functions with PowerShell

August 22, 2018
devops event-grid azure-functions

I wanted to trigger Azure Function by an event sent to Event Grid, which is an easy and well documented task. It’s getting more difficult when you don’t use Azure Portal and want to set everything up using command line.

There are certain combinations of services which make automated deployment not as simple as JSON template. One of them is Bot Service with Azure Active Directory (post coming soon), but in this article I’m going to look into Event Grid + Azure Functions combo.

tl;dr

Currently, it’s not possible to use only ARM and Azure CLI to create both Event Grid and event subscription with Azure Functions as webhook endpoint. To do this, you have to include several REST calls and get something called system key.

Final PowerShell script is here.

Event Grid

My Event Grid resource is part of a larger infrastructure, so I’ve used Azure Resource Manager (ARM) to deploy the topic itself:

{
    "type": "Microsoft.EventGrid/topics",
    "name": "[parameters('gridName')]",
    "apiVersion": "2018-01-01",
    "location": "northeurope",
    "properties":{},
    "dependsOn": []
}

This Event Grid topic will be used to trigger Azure Functions function, but the event subscriptions are not part of Event Grid ARM template. Some scripting will be necessary…

Custom event subscription

Azure CLI is able to create various types of event subscriptions. To list the explanation and helpful examples, just type:

az eventgrid event-subscription create -h

In my case I needed a webhook subscription which sends events of type TextReceived to Azure Functions endpoint.

It could be a regular HTTP endpoint, but I used the Event Grid extension which provides a Functions trigger and handles the whole setup for me.

az eventgrid event-subscription create -g MyGroup --name MySubscription --topic-name MyTopic --endpoint "https://mysite.azurewebsites.net/admin/extensions/EventGridExtensionConfig?functionName=Starter&code=ccooddee==" --endpoint-type webhook --included-event-types TextReceived

So far so good. The complication comes with the endpoint URL:

https://mysite.azurewebsites.net/admin/extensions/EventGridExtensionConfig?functionName=Starter&code=ccooddee==

This endpoint is kindly provided by the Functions Event Grid extension and it’s very easy to copy/paste it from the Azure Portal. Easy and also impossible to do during automated deployment…

Endpoint URL

The only real issue with this URL is the code= part. This code is not the HTTP trigger authorization code and it’s not the master key from Function App either. According to the documentation, it’s called system key. How do we get it?

Currently, there’s no easy way (such as CLI command) to get this key. I had to put together a PowerShell script which goes through the process of:

  1. getting Kudu credentials with Azure CLI,
  2. encoding these credentials with Base64,
  3. using these credentials to get master key with PowerShell REST call,
  4. using master key to get system key with PowerShell REST call as well,
  5. finally creating Event Grid subscription with Azure CLI.

Final script

Attribution: I’m not a PowerShell guru, so I took inspiration from the following great articles:

$resourceGroupName = "MyGroup";
$functionAppName = "mysite";
$starterFuncName = "Starter"; # trigger function (entry point)
$eventGridSubscriptionName = "MySusbcription";
$eventGridTopicName = "MyTopic";
$includedEventTypes = "TextReceived"; # space delimited list of event types

# if not logged in
# az login
# az account set --subscription <sub name>

# if eventgrid extension not installed
# az extension add --name eventgrid

# use Azure CLI to get Kudu creds
Write-Host "Getting Kudu credentials..."
$dep = az webapp deployment list-publishing-profiles -n $functionAppName -g $resourceGroupName --query "[?publishMethod=='MSDeploy']" -o json | ConvertFrom-Json;
$username = $dep.userName;
$pass = $dep.userPWD;

# base 64 creds
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("${username}:${pass}"));

# use creds to get master key
Write-Host "Getting master key..."
$masterResp = Invoke-RestMethod -Uri "https://$functionAppName.scm.azurewebsites.net/api/functions/admin/masterkey" -Headers @{"Authorization" = "Basic " + $encoded};

# use master key to get system key
Write-Host "Getting system key..."
$systemKeyResp = Invoke-RestMethod -Uri "https://$functionAppName.azurewebsites.net/admin/host/systemkeys/eventgridextensionconfig_extension?code=$($masterResp.masterKey)";

# construct Function URL with system key
$functionUrl = "https://$functionAppName.azurewebsites.net/admin/extensions/EventGridExtensionConfig?functionName=$starterFuncName&code=$($systemKeyResp.value)"

# create Event Grid subscription with Function URL
Write-Host "Creating Event Grid subscription..."
Write-Host ("- for URL: {0}" -f $functionUrl)
az eventgrid event-subscription create -g $resourceGroupName --name $eventGridSubscriptionName --topic-name $eventGridTopicName --endpoint $functionUrl --endpoint-type webhook --included-event-types $includedEventTypes
comments powered by Disqus