The Office 365 Management Activity API provides information about various user, admin, system, and policy actions and events from Office 365 and Azure Active Directory activity logs which can help in tracking all types of activity and based on that data a detailed report can be prepared and visualized.
Here I am using my personal developer tenant for this article and will create a sample .Net Core console application.
Following are the key steps to follow before starting API access implementation.
1. Register Application in Azure AD
- Login to your azure portal and make sure you are in the right directory in which you are going to register your application.
- Click on Azure Active Directory.
- From left navigation panel click on App Registration and then New Registration.
- Please enter display name in Name box and select any Supported account types.
- Click on Register.
- Once application is registered, perform following options to generate client secret.
- Click on Certificate and secrets.
- Then click New client secret and enter a certificate description and then click Add
- It displays a hashed client secret and copy it somewhere because later we cannot, and we need it when configuring App client call.
2. Add permission and grant tenant admin consent: –
- Click on API permissions below Certificates and secrets and then click Add a permission, this will open a new blade showing different API available to use.
- Click on Office 365 Management APIs, it opens another popup blade showing the type of permissions as Delegated and Application, since here we are using console as sample application then we can use Application permission and same apply for background services as well.
- After clicking Application permissions, it shows a list of permission.
- Since we are using O365 Activity API, first we need to give permission related to Activity Feed and expand the same ActivityFeed and then select checkbox as below.
- Then click Add Permissions
- Next click on Grant admin consent for {Tenant Name}, it will ask confirmation and then click on Yes.
- Click on the Overview from left navigation and copy Application ID, Tenant ID in a separate file which we need when making API call.
3. Request access tokens from Azure AD:
Please follow below steps to set up client application with App ID, Tenant ID, Client Secrets.
- Create a sample .Net Core console application.
- Install package “Microsoft.IdentityModel.Clients.ActiveDirectory”, this is the only package we need for getting Azure AD auth tokens.
- Store client Id, tenant id and client secret in a class level variable as shown below.
static string tenantId = "tenant123";
static string clientId = "client123";
static string secret = "S:9Wol6hg6ds;8d787d7asdn:das/78dn:/rDf";
- Use AuthenticationContext class to acquire an access token from Azure AD. For this, I have defined a class named OAuthMessageHandler which implements DelegatingHandler and automates the process of getting access tokens and overrides SendAsync method to send request to the server as given below.
public class OAuthMessageHandler : DelegatingHandler
{
private AuthenticationHeaderValue authHeader;
public OAuthMessageHandler(string tenantId, string clientId, string secret, HttpMessageHandler innerHandler)
: base(innerHandler)
{
var authenticationContext = new AuthenticationContext("https://login.windows.net/" + tenantId, false);
ClientCredential clientCred = new ClientCredential(clientId, secret);
AuthenticationResult authenticationResult = null;
Task runTask = Task.Run(async () => authenticationResult = await authenticationContext.AcquireTokenAsync("https://manage.office.com", clientCred));
runTask.Wait();
string token = authenticationResult.AccessToken;
authHeader = new AuthenticationHeaderValue("Bearer", token);
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Headers.Authorization = authHeader;
return base.SendAsync(request, cancellationToken);
}
}
4. Make Office 365 Management Activity APIs Call:
Before making APIs call, we need to turn office log to on/off. To do this please check this link https://docs.microsoft.com/en-us/microsoft-365/compliance/turn-audit-log-search-on-or-off. Once the audit log turned on, lets see how different activity log has been classified into following content type.
- Audit.AzureActiveDirectory
- Audit.Exchange
- Audit.SharePoint
- Audit.General
- DLP.All
Currently describing each of these content types is beyond the scope of this blog and we are going to proceed only with Audit.SharePoint. Follow the steps below.
1. We need to start the subscription related to content type for which we are going to retrieve/list contents.
var messageHandler = new OAuthMessageHandler(tenantId, clientId, secret, new HttpClientHandler());
using (HttpClient httpClient = new HttpClient(messageHandler))
{
httpClient.BaseAddress = new Uri("https://manage.office.com");
httpClient.Timeout = new TimeSpan(0, 2, 0);
string endpoint = $"/api/v1.0/{tenantId}/activity/feed/subscriptions/start?contentType=Audit.Exchange";
HttpRequestMessage message = new HttpRequestMessage(new HttpMethod("POST"), endpoint);
var resp = httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead).Result;
if (resp.Result.IsSuccessStatusCode)
{
Console.WriteLine("Processing completed");
}
}
2. Once subscription started related to content types then check whether content types got enable or not using list subscription APIs. Below APIs call can be made to get list of subscribed content types.
testResponse = httpClient.GetAsync($"https://manage.office.com/api/v1.0/{tenantId}/activity/feed/subscriptions/list");
Sample Response =>
[{"contentType":"Audit.Sharepoint","status":"enabled","webhook":null}]
3. Now let’s try to retrieve the content URL of SharePoint activity. Below is the APIs call and the sample response.
testResponse = httpClient.GetAsync($"https://manage.office.com/api/v1.0/{tenantId}/activity/feed/subscriptions/content?contentType=Audit.SharePoint&PublisherIdentifier={tenantId}");
Sample Response=>
[{"contentUri":"https://manage.office.com/api/v1.0/442343crca-df36-423a-ae01-329af93d424d27/activity/feed/audit/20191028111653970085558$20191028122221900037718$audit_azureactivedirectory$Audit_AzureActiveDirectory$apac0013",
"contentId":"20191028111653970085558$20191028122221900037718$audit_azureactivedirectory$Audit_SharePoint$apac0013",
"contentType":"Audit.AzureActiveDirectory","contentCreated":"2019-10-28T12:22:21.900Z", "contentExpiration":"2019-11-04T11:16:53.970Z"}]
4. Then make another request reading the contentUri from the previous sample JSON response and this will return the actual activity log contents.
testResponse = httpClient.GetAsync($"https://manage.office.com/api/v1.0/f34d4d34eca-df36-423a-ae01-329d4234507e7/activity/feed/audit/ 20191028111653970085558$20191028122221900037718$audit_sharepoint$Audit_SharePoint$apac0013",
"contentId":"20191028111653970085558$20191028122221900037718$audit_sharepoint$Audit_SharePoint$apac0013");
Sample Response =>
[{"CreationTime":"2019-11-03T07:32:24","Id":"sasa2s-32d3f464-43ef-e668-08d7602ff8c4","Operation":"PageViewed","OrganizationId":"2d4241eca-df36-423a-ae01-329af93507e7","RecordType":4, "UserKey":"i:0h.f|membership|100320004c287005@live.com","UserType":0,"Version":1,"Workload":"SharePoint","ClientIP":"223.237.241.166","ObjectId":"https:\/\/vttest.sharepoint.com\/sites\/tests\/Shared Documents\/Forms\/AllItems.aspx","UserId":"test@test.onmicrosoft.com","CorrelationId":"ab25149f-50e2-0032-436a-bdc5f9ce9761", "CustomUniqueId":false,"EventSource":"SharePoint","ItemType":"Page","ListId":"1b0aa9c8-8b329-4229-a5b9-43d15467ef96", "ListItemUniqueId":"ed53239b1-2afb-4a3b-a20c-b56e333f8c","Site":"8x32x949d-e59b-45df-b6ba-2e4720d8ddec","UserAgent":"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/77.0.3865.90 Safari\/537.36","WebId":"59d95fcb-9cfd-4dsd33-8899-4d54c196ccbf"}]
Complete code has been given below.
class Program
{
static string tenantId = "tenanr123";
static string clientId = "client123";
static string secret = "S:9Wol6h$%^$&#*dsjdnsdds-=H-PC:/rDf";
static OAuthMessageHandler messageHandler;
static Program()
{
messageHandler = new OAuthMessageHandler(tenantId, clientId, secret, new HttpClientHandler());
}
static void Main(string[] args)
{
StartSubscription();
ListSubscription();
RetrieveContent();
}
static void StartSubscription()
{
using (HttpClient httpClient = new HttpClient(messageHandler))
{
httpClient.BaseAddress = new Uri("https://manage.office.com");
httpClient.Timeout = new TimeSpan(0, 2, 0);
string endpoint = $"/api/v1.0/{tenantId}/activity/feed/subscriptions/start?contentType=Audit.Exchange";
HttpRequestMessage message = new HttpRequestMessage(new HttpMethod("POST"), endpoint);
var resp = httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead).Result;
if (resp.IsSuccessStatusCode)
{
Console.WriteLine("Processing completed");
}
}
}
static void ListSubscription()
{
using (HttpClient httpClient = new HttpClient(messageHandler))
{
httpClient.BaseAddress = new Uri("https://manage.office.com");
httpClient.Timeout = new TimeSpan(0, 2, 0);
var testResponse = httpClient.GetAsync($"https://manage.office.com/api/v1.0/{tenantId}/activity/feed/subscriptions/list");
if (testResponse.Result.IsSuccessStatusCode)
{
string json = testResponse.Result.Content.ReadAsStringAsync().Result;
Console.WriteLine("Processing completed");
}
}
}
static void RetrieveContent()
{
using (HttpClient httpClient = new HttpClient(messageHandler))
{
httpClient.BaseAddress = new Uri("https://manage.office.com");
httpClient.Timeout = new TimeSpan(0, 2, 0);
var testResponse = httpClient.GetAsync($"https://manage.office.com/api/v1.0/{tenantId}/activity/feed/subscriptions/content?contentType=Audit.SharePoint&PublisherIdentifier={tenantId}&startTime=2018-12-12&endTime=2019-12-12");
if (testResponse.Result.IsSuccessStatusCode)
{
string json = testResponse.Result.Content.ReadAsStringAsync().Result;
Console.WriteLine("Processing completed");
}
}
}
}
public class OAuthMessageHandler : DelegatingHandler
{
private AuthenticationHeaderValue authHeader;
public OAuthMessageHandler(string tenantId, string clientId, string secret, HttpMessageHandler innerHandler)
: base(innerHandler)
{
var authenticationContext = new AuthenticationContext("https://login.windows.net/" + tenantId, false);
ClientCredential clientCred = new ClientCredential(clientId, secret);
AuthenticationResult authenticationResult = null;
Task runTask = Task.Run(async () => authenticationResult = await authenticationContext.AcquireTokenAsync("https://manage.office.com", clientCred));
runTask.Wait();
string token = authenticationResult.AccessToken;
authHeader = new AuthenticationHeaderValue("Bearer", token);
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Headers.Authorization = authHeader;
return base.SendAsync(request, cancellationToken);
}
}
In this Article, I have shown how to access Office 365 management activity API by using a sample console application and making a call to APIs and getting request but the same process can be automated using webhook approach and Web API which I am going to explain in my next blog. By then cheers! And have a happy coding.
Found a glitch here:
var resp = httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead).Result;
if (resp.Result.IsSuccessStatusCode)
// should be if (resp.IsSuccessStatusCode)
{
Console.WriteLine(“Processing completed”);
}
I don’t see such line of code here.
Nice Article. Is there a way to get event form Teams created or teams deleted. Which activity log teams are classified into Audi.Sharepoint or General.
Hi Vikash, have you already published the article to access Office 365 management activity API by using Web API?