Azure  

Webhooks + Azure Functions (.NET 9 Isolated Worker)

What is a Webhook?

A webhook is like a notification system between two applications.
When something happens in one system (like a new record is created), it sends a small message (HTTP POST) to another system’s URL to tell it, “Hey, something just happened!”

You don’t need to constantly check for updates — you just get notified instantly.

Example

If a new item is added to a SharePoint list, SharePoint can call your Azure Function and send the item details.
Your Azure Function can then:

  • Send a Teams message

  • Update a database

  • Or trigger an approval workflow

Why Use Azure Functions for Webhooks?

Azure Functions are perfect for receiving webhooks because they:

  • Run in the cloud (no servers to manage)

  • Automatically scale

  • Are cheap — you only pay when they run

  • Easily integrate with other Azure services

Basically, Azure Functions are the easiest way to create a public API endpoint that reacts to webhook events.

How Webhooks Work?

  1. Register: The source system (like SharePoint) is told where to send notifications (your Azure Function URL).

  2. Validation: The source might send a “challenge” request to confirm your URL works. Your function must respond correctly.

  3. Event Happens: When an event occurs, the source sends data to your URL via an HTTP POST.

  4. Process: Your Azure Function reads the data and does something useful (save to DB, trigger another service, etc.).

Creating a Webhook Receiver in .NET 9

Let’s build a webhook receiver using Azure Functions (.NET 9, isolated worker model).

Step 1. Create a New Azure Function Project

  1. Open Visual Studio

Launch Visual Studio → click Create a new project.

  1. Choose Project Template

Search for “Azure Functions” in the templates list.

Select

Azure Functions → Click Next

  1. Configure the Project

  • Project Name: WebhookReceiver

  • Location: Choose any local folder

  • Framework: Choose .NET 9.0 (Isolated)

  • Click Create

  1. Configure Function Settings

Visual Studio will now ask for more setup options:

SettingValue
Functions Worker.NET Isolated
Function TemplateHTTP trigger
Authorization levelFunction
Storage Account(Storage emulator or None for now)
  1. Click Create

Step 2. Add Required Packages

Make sure your .csproj looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.*" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.*" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.*" />
  </ItemGroup>
</Project>

Step 3. Program.cs (App Setup)

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        services.AddHttpClient();
    })
    .ConfigureLogging(logging => logging.AddConsole())
    .Build();

await host.RunAsync();

Step 4. Write the Function Code

Create a file called WebhookHandler.cs.

using System.Net;
using System.Text.Json;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;

public class WebhookHandler
{
    private readonly ILogger _logger;

    public WebhookHandler(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<WebhookHandler>();
    }

    [Function("WebhookReceiver")]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
    {
        _logger.LogInformation("Webhook request received.");

        // Step 1: Handle validation (for example, SharePoint challenge)
        if (req.Url.Query.Contains("validationtoken"))
        {
            var query = System.Web.HttpUtility.ParseQueryString(req.Url.Query);
            var token = query["validationtoken"];

            var response = req.CreateResponse(HttpStatusCode.OK);
            await response.WriteStringAsync(token ?? "");
            return response;
        }

        // Step 2: Read the POST body
        var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        _logger.LogInformation("Payload received: {body}", requestBody);

        // Step 3: Deserialize the JSON
        var data = JsonSerializer.Deserialize<WebhookPayload>(requestBody);
        if (data == null)
        {
            var badResponse = req.CreateResponse(HttpStatusCode.BadRequest);
            await badResponse.WriteStringAsync("Invalid payload");
            return badResponse;
        }

        // Step 4: Do your business logic here
        _logger.LogInformation("Processing event: {event}", data.EventName);

        // Step 5: Return success
        var okResponse = req.CreateResponse(HttpStatusCode.OK);
        await okResponse.WriteStringAsync("Webhook processed successfully");
        return okResponse;
    }
}

public class WebhookPayload
{
    public string EventName { get; set; }
    public string ResourceId { get; set; }
}

Step 5. Secure the Function

To keep it safe, use a secret header.

Add this check before processing the payload:

if (!req.Headers.TryGetValues("x-webhook-secret", out var secrets) ||
    secrets.FirstOrDefault() != Environment.GetEnvironmentVariable("WEBHOOK_SECRET"))
{
    _logger.LogWarning("Unauthorized request!");
    var unauthorized = req.CreateResponse(HttpStatusCode.Unauthorized);
    await unauthorized.WriteStringAsync("Unauthorized");
    return unauthorized;
}

Add this to your local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "WEBHOOK_SECRET": "my-super-secret-key"
  }
}

Step 6. Test Locally

Run your app:

func start

Then use Postman to send:

  • A POST request to http://localhost:7071/api/webhookreceiver

  • Add header x-webhook-secret: my-super-secret-key

  • Add JSON body:

{
  "EventName": "ItemCreated",
  "ResourceId": "12345"
}

You should see a success message in the console.

We will see the deployment to Azure in next article.

Conclusion

Webhooks and Azure Functions are a powerful combination.
They help you create fast, event-driven solutions that respond in real time — without servers or complex infrastructure.

With just a few lines of code in .NET 9, you can build a scalable webhook receiver that integrates with SharePoint, Power Apps, or any external service.