I wanted to trigger an Azure Function based on changes in the Azure Subscription(s) we where monitoring. The incoming data can than be used to do interesting things with: keeping track of who does what, see new resources being deployed or old ones being deleted, etc. Back when I started working on this, there was no Event Grid option to use in Azure Functions, so I started with linking it to Azure Monitor events. I haven’t checked the current options, so I cannot compare them yet.
In this blog I wanted to show how you can do this, both by using the Azure Portal and the Azure CLI.
To get Azure Monitor to send in the changes that we need to see, I use this architecture:
To configure Azure Monitor to send all activities into an EventHub and then into our Function, you’ll need to execute several steps.
How to do this manually via the Azure Portal is described below.
The default setting of 1 throughput unit is enough for this setup, as mentioned by Microsoft’s documentation.
Go to Activity Log, hit the export
button:
PICK ALL REGIONS!! Most activities we want to see are GLOBAL and those would be missed otherwise.
Choose the EventHub entity you picked for the Azure Monitor to export the activities to:
You can add a consumer group to it, named ‘$default` by default.
Go to Shared Access Policies
and create a policy. We only need to have the Listen
rights so we can listen to incoming events.
Copy either on of the Connection strings
and configure the Azure Function host with it:
No need to restart the function app: the platform does that for you.
How to do this via the Azure CLI is described below.
First we need to check if there already is an Event Hub and if we could use it. Use these commands to list the available namespaces in the current subscription:
az login # login with an account that has the correct access rights to deploy resources on subscription level
az eventhubs namespace list
To search for the namespaces in a specific resource group and subscription, you can also add those parameters:
$resourceGroup = "<resourceGroupName>"
$subscription = "<subscriptionId>"
az eventhubs namespace list --resource-group $resourceGroup --subscription $subscription
You’ll get back []
as an empty JSON array indicating that there are no namespaces in that list.
If there is an existing namespace, you can use that to create the Log Profile with. Of course, you’ll need to check how this namespace is being used and where it sends the messages!
Use this command to create a new namespace:
az eventhubs namespace create --resource-group $resourceGroup --subscription $subscription --name EventHubNameSpaceCLI
This will take a couple of minutes to complete and will return the newly created namespace. You can save the JSON response in an object with this command:
$namespace = az eventhubs namespace show --resource-group $resourceGroup --subscription $subscription --name EventHubNameSpaceCLI | ConvertFrom-JSON
$eventhub = az eventhubs eventhub create --subscription $subscription --resource-group $resourceGroup --namespace-name $namespace.Name --name EventHubCLI | ConvertFrom-Json
To be able to connect to the new EventHub, we need an authorization rule. That will have an Id that will be used as a connection string.
$ruleName = "<authorization rule name>"
$rule = az eventhubs eventhub authorization-rule create --resource-group $resourceGroup --namespace-name $namespace.Name --eventhub-name $eventhub.Name --subscription $subscription --name ListenRule --rights Listen | ConvertFrom-Json
# The Id of the rule that you need:
$rule.Id
First, connect the CLI and check if there already is a profile available
az login # login with an account that has the correct access rights to deploy resources on subscription level
az monitor log-profiles list # list the current log-profiles
Note: this will run against the currently selected (or default) subscription. You could get one of two results:
[]
If there already is a profile, carefully read through the results to see if it contains everything we need. Note: there can only be one profile per subscription. If there is only one subscription with a profile and that is set to export to the correct EventHub, that is fine. From top to bottom this is the information you see in the image above:
If there already is an export configured to a Storage Account (but no ServiceBusRuleId), you can check the locations and categories, if those are sufficient, you can update the profile to also have a ServiceBusRuleId set to send the data into the EventHub you need.
If the locations or categories are not sufficient, you need to check the Storage Account that is being used if to see if it hurts if you send more information there. This depends on the setup that is used on top of that information.
If changing this profile is an issue, you are toast. You can create a new profile on a different subscription, but all activities on the default subscription will only be sent to the profile on that subscription!
If there is no profile setup, you can create a new profile with the necessary settings like this:
az monitor log-profiles create --name "default" --location null --locations "global" "eastus" "westus" "westeu" "northeu" --categories "Delete" "Write" "Action" --enabled true --days 1 --service-bus-rule-id "/subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName>/providers/Microsoft.EventHub/namespaces/<nameSpaceName>/authorizationrules/RootManageSharedAccessKey"
Important parameters for us:
global
az login # login to the account
az account set --subscription "<SubscriptionId you want to use>" # switch to the correct subscription
$resourceGroup = "<resource group name>" # name of the resourcegroup the EventHub is in
az eventhubs namespace list -g $resourceGroup # list all the eventhub namespaces in the given resourcegroup
Find the name of the namespace you want to use and use that in the next set of commands
$EventHub = az eventhubs namespace show -g $resourceGroup --name "<name of the namespace>" | ConvertFrom-Json
az eventhubs eventhub list -g $resourceGroup --namespace-name $EventHub.Name
Find the name of the event hub in the list and use that in the next set of commands:
$NameSpace = az eventhubs eventhub show -g $resourceGroup --namespace-name $EventHub.Name --name "<name of the eventhub>" | ConvertFrom-Json
# find all authorization rules
az eventhubs eventhub authorization-rule list --resource-group $resourceGroup --namespace-name $NameSpace.Name --eventhub-name $EventHub.Name
$AuthRule = az eventhubs eventhub authorization-rule show --resource-group $resourceGroup --namespace-name $EventHub.Name --eventhub-name $NameSpace.Name | ConvertFrom-Json
# search for the rule with the name you want
$ruleName = "<authorization rule name>"
$rule = az eventhubs eventhub authorization-rule show --resource-group $resourceGroup --namespace-name $NameSpace.Name --eventhub-name $EventHub.Name --name $ruleName | ConvertFrom-Json
The Azure Function needs to be told about the connection to the EventHub that it needs to listen on. For that, you need the Id of the authorization rule that we created in the previous section.
You can retrieve that Id from the rule we saved:
# The Id of the rule that you need:
$rule.Id
And then you can set that into the corresponding app setting for it:
az functionapp config appsettings set --resource-group $resourcegroup --name $functionapp --settings EventHubConnectionAppSetting=$rule.Id
The name of the setting we are changing here (EventHubConnectionAppSetting
) needs to match the name of the Connection you gave the parameter on the function:
[FunctionName("EventHubActivitiesFunction"))]
public static async Task Run(
[EventHubTrigger("insights-operational-Logs", Connection = "EventHubConnectionAppSetting")]
EventData eventHubMessage,
ILogger log)
{ ... }
For more documentation about the Event Hub binding, check docs.microsoft.com.