Setting up Webhook for O365 Audit logs

This is part one of a 2-part blog post.

  1. Walkthrough Setting up WebHook for O365 Audit Logs – (This Post)
  2. Dive into the code for O365 Audit Log webhooks to see how it works

In this blog post I’m going to show you how to get the O365 Audit logs using WebHooks. The full code can be found at my Github repo My post will show you how to set up with screenshots and the expected results. In my next blog post I will dive into the important parts of the code to get this Audit WebHook connected and working.

Set up – Walkthrough

Creating an App Only Token

Once you have downloaded a copy from my Repo you will need to set up your environment. First thing we are going to do is create an App Only Token that will be able to read the Audit Logs.

  • For your Office 365 Tenant go to
  • Select Active Directory
  • Select App Registrations
  • Click Create New Registration
    • Name: Audit Logs Retrieval
    • Supported Account types:
      Accounts in this organizational directory only
    • Click Register
  • Take a copy of the Application (client) ID
  • Take a copy of the Directory (tenant) ID
  • Click View API Permissions
  • Click Add a Permission
  • Select Office 365 Management APIs -> Application Permissions -> ActivityFeed.Read
  • Click Add permissions

  • Click Grant Admin Consent for [tenant] and accept the permissions.
  • Click on Certificates & Secrets
  • Click New Client Secret
    • Description: Audit Web Hook
    • Expires: Never
  • Take a copy of the Secret value

Setting up Azure

You will need to set up your Azure Environment, this will consist of the following:

  • Resource Group
  • Azure Function V1
  • Applications Insights
  • Storage Account

I like to automate where I can, also it saves me creating loads of screenshots which are probably all out of date after 2 months. I have written an Az CLI PowerShell script that will create the above for you in your Azure Environment. In the next blog post I will explain the code.

  • Download the latest version of Az Cli.
  • Using a PowerShell window – Sign into your Azure Environment using ‘az login’
  • If you have multiple subscriptions, ensure you are pointing to the correct subscription ‘az account set –subscription [SubscriptionName]
  • Change the directory to .\O365AuditWebhook\powershell
  • Run the following: ‘.\Install-AzureEnvironment.ps1 -Environment “[Environment]” -Name:”AuditWebHook”‘ replacing the [Environment] with your tenant name. For example, I’ve used cfcodedev.
  • Once the script has run, you will have the basic template Azure resources you need within the Resource group named [Environment]-AuditWebHook

Deploying Azure Function from Visual Studio 2019

Firstly, you don’t have to deploy this way. If you prefer to use Visual Studio code, create an AZ install script or manually deploy using Kudu, that is your choice, and all are valid. My choice of doing this is simplicity for screen shots and steps.

  • Open the solution using Visual Studio Code 2019
  • Right click on the project AuditWebHook and select Publish
  • From the Pick a publish target dialog (click Start if you are not seeing a dialog), and under Azure Functions Consumption Plan click Select Existing, and select Create Profile.
  • Sign into your account if you need to, then pick your subscription, resource group, and then you can either search, or just pick the Azure Function. Click OK.
  • This takes you back to the Summary page. Under Actions click Edit Azure App Service settings
  • The Application Settings dialog will show you the values Local and what is found within Azure Function in the cloud. You will need to update the Remote value for the following:
  • You will need to add the following Settings, by clicking on Add Setting creating the setting name, and put the value in afterwards. Repeat for each setting below.
    • Tenant: [Name of your Tenant, do not include]
    • ClientId: [Client ID created in step ‘Creating an App Only Token’ earlier]
    • AppSecret: [Secret Value created in step ‘Creating an App Only Token’ earlier]
  • Click OK
  • Back on the Publish screen, click the Publish button. This will push the code to your environment, with the correct Application Settings.
  • By going to your Azure Function at, you will see 2 Azure Functions
  • Then clicking on Configuration, it will take you to the Application settings page, click Show Values and you will see your values.

At this point you just have the Azure function as a Webhook in place. Next steps are to tie the O365 Audit log to the WebHook.

Connecting O365 Audit Logs to your webhook

The last step is tying the Audit logs to your webhook. The webhook can be used for the different Audit logs. There are 5 different types of logs.

  1. Audit.AzureActiveDirectory
  2. Audit.Exchange
  3. Audit.SharePoint
  4. Audit.General
  5. DLP.All -Note: DLP sensitive data is only available in the activity feed API to users that have been granted “Read DLP Sensitive Data” permission.

I have written a PowerShell script for you that will register the webhook for you. You will find this in the repo.

  • Open PowerShell
  • Change the directory to .\O365AuditWebhook\powershell
  • Run the following PowerShell script (Run on one line), change the parameters to match your environment. I’ve picked Audit.SharePoint, but you can use any listed above, and run the PowerShell script multiple times to connect all logs to the webhook.
.\Set-AuditLogs.ps1 -ClientId:<ClientID>
-TenantGUID:<Directory ID>

The above codes login with the ClientID and Secret and Starts a subscription to the given ContentType audit, using the WebHookUrl for the webhook.

If successful, you will receive a 200 Status Code message like below.

Your Azure Function (AuditWebHook) would have fired, and you would see something like the following within your logs.

Viewing the results

Directly from the Microsoft Page on Office 365 management api it states in this note:

When a subscription is created, it can take up to 12 hours for the first content blobs to become available for that subscription. The content blobs are created by collecting and aggregating actions and events across multiple servers and datacenters. As a result of this distributed process, the actions and events contained in the content blobs will not necessarily appear in the order in which they occurred. One content blob can contain actions and events that occurred prior to the actions and events contained in an earlier content blob. We are working to decrease the latency between the occurrence of actions and events and their availability within a content blob, but we can’t guarantee that they appear sequentially.

If you are using a Development environment – like myself – and setup the Audit.SharePoint content type then I suggest you go into SharePoint, and start using SharePoint. Just so the logs start to fill.

Please note, it can take up to 30 minutes or up to 24 hours after an event occurs for the corresponding audit log entry to be displayed in the search results, depending on the service of Office 365. See the table at the bottom of this section Search the audit log in security and compliance – Before you begin

Viewing the AuditWebHook azure function, you will see that it has fired more times since your initial setup.

If you look at your latest call, (note: logs can display out of order in azure functions) you will see that it attempts to find the validation code, which is what it needs to set up the webhook. When it is unable to find the validation code, the code assumes that content contains log information. It grabs the URI of the log that has been created and then it adds it to our Azure Storage Queue for our other azure function to process. Depending on how busy your environment is, this request could hold multiple URL’s to logs. A webhook has to respond quickly back to the calling code with a 200 status code. Therefore we are adding the URI’s of the logs directly to a Storage Queue to allow a different process to interrogate the logs.

The second Azure Function (AuditContentUri) will fire every time an item lands on the Storage Queue. This will grab the information from within the log file by calling the URI.

If we select one of the calls and view the logs of that Azure Function call, every entry within that Audit log file URI will be displayed in a JSON format. Clicking on a row in the logs, will display the full details of the line. At this point in the code, would be where you process the line and do whatever you need to do with the Audit log. I’m just printing it out to the Azure Function Logs.

Remove O365 Audit Logs from your webhook

To remove the webhook from the Audit log just run the following PowerShell script. You will find this in the repo.

  • Open PowerShell
  • Change the directory to .\O365AuditWebhook\powershell
  • Run the following PowerShell script (Run on one line), change the parameters to match your environment. I’ve picked Audit.SharePoint, but you can use any listed above, and run the PowerShell script multiple times to remove all logs to the webhook.

The below codes login with the ClientID and Secret and stops the subscription of the given ContentType audit.

.\Remove-AuditLogs.ps1 -ClientId:<ClientID>
-TenantGUID:<Directory ID>

Hopefully, if you have followed this correctly, (and I have written decent enough instructions for you), you should have a basic Audit Log Webhook working in your environment. This isn’t anywhere near production ready code, but it gives you an idea where to start. In my next blog post I will be going though parts of the code, to explain how it all fits together.