Azure Alerts -> Secure Webhook -> Az Functions with Auth
I have been trying to implement Security in Azure Functions a lot recently and in the past but unfortunately, I can never accomplish it based on Microsoft documentation alone.
So, I am creating this article to help anyone trying to do the same with a nice rundown of how to set up a secure webhook between Azure Monitoring Alerts and an Azure Function HTTP endpoint.
1. Create an Azure function in your Subscription. I am calling my functions Alert-Action-Function. Any plan should work to accomplish this as we will only be creating 1 HTTP trigger function and enabling Authentication/Authorization
- Run time .NET Core 3.1
- Consumptions plan
- Windows OS
2. Enable function Authentication/Authorization
-
Open your function resource and go to the section Settings and open Authentication/Authorization. Than turn App Service Authentication to On
-
Set Action to take when the request is not authenticated to Log in with Azure Active Directory
-
Under Authentication Providers click the Azure Active Directory Not Configured
-
Under Management Mode select Express. Notice the Create App field, this will provide you with an auto-generated App Registry name (I will be adding “AD-App” on the end to make it more clear).
Now click ok and save. Now we have enabled the Authentication part of our function
3. Add a HTTP Trigger function
... to do tests on your setup. I like to write to blob storage so I can view the request body if needed, also b/c you can use this to see if the request is finished in real-time.
Why I do this: Azure Monitoring with Application Insights and diagnostic settings setup properly will take around 5 mins to let you see logs and around 2 mins for metrics... and that is on a good day for Azure.
GitHub repo page for this code here
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.Storage;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Storage.Blob;
/*
************************************************
NOTE:
make sure you have your AzureWebJobsStorage Environmental Variable is set when trying to run
AND
make sure you add this package
dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage
************************************************
*/
namespace Company.Function
{
public static class HttpTrigger_test_blob_store
{
[FunctionName("HttpTrigger_test_blob_store")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[Blob("blobverifycontainer")] CloudBlobContainer outputContainer,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
/// write to the blob storage
await outputContainer.CreateIfNotExistsAsync();
var blobName = Guid.NewGuid().ToString();
var cloudBlockBlob = outputContainer.GetBlockBlobReference(blobName);
await cloudBlockBlob.UploadTextAsync(requestBody);
// return blob name for easy lookup
return new OkObjectResult(blobName);
}
}
}
4. Configure your AD App registration.
-
In Azure Active Directory -> App registrations find and open the name from step 2.4 (the express auto-generated name if you didn’t change it)
-
Maker sure to add yourself as the Owner
-
Now go to Manifest and you will be adding to the App Roles array in the JSON editor
-
Replace with provided JSON.
"appRoles": [ { "allowedMemberTypes": [ "Application" ], "description": "This is a role for Action Groups to join", "displayName": "ActionGroupsSecureWebhook", "id": "e14442ab-8bd5-47be-90af-695c413d9761 REPLACE WITH NEW GUID", "isEnabled": true, "lang": null, "origin": "Application", "value": "ServiceClient" } ],BUT make sure to replace the id with a new GUID. If you don’t you will get this error!
Things to Note!!!
- don’t worry about the tab formatting, after save and you come back it will be done for you. Just make sure your JSON is valid)
- IMPORTANT: if you mess up the **appRoles and try to edit it later you may need to first set IsEnabled to “false”, save and then make your changes. This is something I found out the hard way.**
5. ALSO, ensure this (Integrity Check)
Make sure that the Application ID URL has been assigned to the base URL of your Azure Function.
If it has not this will an error when linking this AD App Registration to the Secure Webhook (you will get an azure notification that doesn’t give you a useful message and just says “undefined” error. (I was only able to see the real error was when I when into chrome devtools to investigate the corresponding Network call.)
6. Create Azure Function to Alert on
- I created a little NoiseMaker project to be an easy way to test Azure Monitor Alerts in general.
- Link this to Application Insights. I named mine NoiseMaker as well.
- If nothing is passed to the query string it will return a 418 (“I’m a teapot”. Look it up, it’s real)
- Github code is here
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Company.Function
{
public static class HTTP_Noise_Maker
{
[FunctionName("HTTP_Noise_Maker")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function *NoiseMaker* processed a request.");
string pass = req.Query["pass"];
string notfound = req.Query["notfound"];
if (pass == "true")
{
var result = new ObjectResult("I'M OK 😁");
result.StatusCode = StatusCodes.Status200OK;
return result;
}
else if (notfound == "true")
{
var result = new ObjectResult("I'M NOT FOUND 😲");
result.StatusCode = StatusCodes.Status404NotFound;
return result;
}
else
{
var result = new ObjectResult("I'M A TEA POT 🍵");
result.StatusCode = StatusCodes.Status418ImATeapot;
return result;
}
}
}
}
7. Create your alert
Go to Azure Monitor
Select Resource. I am using the NoiseMaker
Select the Condition. Server Requests are what we want to catch when a 418 happens. (You can do the same for other HTTP response codes as well)
Then...
Now Create the Action Group
Give it a name and add the Secure Webhook Action and Create
Now just finish the Alert Creation form with a Name, Description, Severity and make sure Enabled is checked. Should look like this.
8. Finally, test it
Trigger the NoiseMaker API endpoint a couple of times (either with a browser or Postman), wait a couple of minutes for the alert to show up on the Monitoring Alert page. Then go to the Storage Account linked to your “Alert-Action-Function” you created before and you should see a new blob in “blobverifycontainer” Container.
Let me know this tutorial worked out for you in the comments below. This is my first Dev.to article and I hope it helps but If improvements need to be made (or I just used too many pictures haha) let me know.
Happy Monitoring
















Top comments (8)
Great post, very detailed. I have just 1 quick question. What do you have the "User assignment required" option set to, Yes or No for the Alert_Action_Function-AD-App? I'm having a problem with my setup when the "User assignment required" is set to yes.
sorry, i don't quite understand the context of the question. can you give me more detail as to what you are referring to?
nvm got it. Mine if set to No.
I didn't set this myself thought.
i don't know precisely what this does but from what I gather from its tooltip it is for allowing access to this service principle to others in your Azure subscription. I think if you changed this to yes only the owner(s) will be able to see it. (i am referring to inside App registration under the All Applications and Owned Application tabs). so i would assume if you created and enabled this feature your co-worker would not be able to find it under the All Application tab.
I would recommend reading the tooltip bc this is my interpretation of it without testing it.
Thank you for looking at this. I have a user assigned in the Users and groups with Default Access but the webhook Active Directory authentication will not work. Not sure if I need a different role or permissions.
Are you trying to allow someone else access to your service principal? I'm not sure how users and groups relates to this? More context?
If this is what you are trying to do and you allowed to have this access being a a Contributor on the subscription should be more then enough (not fine tuned authorization I know but this is an option).
I'm trying to do basically what your blog post is doing. I have a Azure function with an endpoint that I need an Azure App Configuration Event to call when an update happens to a configuration key. The event webhook needs to be set up for AAD authentication. This all works except when I turn that "user Assigned Required" option on.
Thanks for the post.
I needed to achieve Secure Webhook delivery but without using the Entra Easy Auth method. My Function App needed to be able to host two HTTP triggered functions; a legacy one that must still accept function key authentication and another that only accepts Entra authentication. Both functions fundamentally do the same thing. When using Entra Easy Auth it is forced upon all functions in the Function App so it couldn't be used.
The process I followed is broadly similar apart from the my Function App validates Entra tokens and authorisation in code.
Thought I'd share some of the pain I went through.
Don't forget to assign the Microsoft first-party Azns AAD Webhook service principal the ActionGroupsSecureWebhook app role. This Microsoft article has a PowerShell script that creates the role and does this assignment.
When trying to add the Secure Webhook and saving the Action Group I would receive the error WebhookServiceUriBlocked. There is no info online about this error.
In the end I made the Azns AAD Webhook service principal an owner of my App Registration (that same Microsoft article says to do this "to be able to create, modify, or test the secure webhook action in the action group."). I also enabled the use of V2.0 tokens only (also called out as a Tip in that article). And in the end I added a custom domain for my Function App. Doing some, or all of these things made it work. I suspect it was the custom domain that actually fixed it; I'm guessing Microsoft now prevents the use of default yourapp.azurewebsites.net domains to stop subdomain takeover from dangling DNS entries.Oh, I also left the Application ID URI as the default api://guid and did enable Require User Assignment as I need to limit app access to Azns AAD Webhook and developer user accounts only.
Edit: Doh! You don't need to make the Azns AAD Webhook service principal an owner of the App Reg, just the user accounts of those who need to be able to select the App Reg when adding the Secure Webhook to the Action Group.
Set "accessTokenAcceptedVersion": null, in the manifest of your app registration to "accessTokenAcceptedVersion": 2,
after that it worked for me. took me a long time to find out.
Its documented in Azure learn:
learn.microsoft.com/en-us/azure/az...