Programatically Retrieve all Office 365 Unified Audit Logs - microsoft-graph-api

I would like to programmatically retrieve and process all logs available from the Office 365 Unified Audit Logs for the purpose of forensic investigation. From the front end, these logs are available through the Office 365 Compliance Admin Center.
I have tried the following options to access these logs from a script, with no success:
Microsoft 365 Management API - This contains the correct data, but is of limited usefulness for forensic investigations due to the short 7 day retention period.
Microsoft Graph - This does not contain all the relevant data - you cannot access the Unified Audit Logs directly through Graph, and the usage reports do not cover all items contained in the Audit Logs (e.g. Exchange actions).
Search-UnifiedAuditLog on Exchange Online PowerShell - Microsoft themselves recommend not to use this programmatically, and I've experienced extremely unreliable results and unmanageable rate-limiting when trying to do so.
So is there something I'm missing here, or is there no way to programmatically retrieve all items from the Unified Audit Logs for the entire retention period? (generally 90 days).

As far as I know the only way to do this is to use the Management API on a regular basis and output the results to some solution for long term storage (Azure Log Analytics Workspace comes to mind, or SIEM like Splunk / Graylog). I.e. write a script that retrieves logs for the last week, and run it at least weekly.
I'll explain how to retrieve logs manually and also show a tool which already exists for this at the bottom of the post.
Manually:
1: Enable Audit logging on the tenant if not already enabled
2: Create an App registration in Azure AD and for getting single tenant audit logs choose "Accounts in this organizational directory only (xyz only - Single tenant)"
3: Create a 'secret key' from within the newly created App Registration. Store it somewhere safe as it's only shown once. From the overview page of the App Registration also store the "Tenant ID" and "Application (Client) ID". You will need all three.
4: From within the new App Registration go to "API permissions" and add 'Application type' permissions for: 'ActivityFeed.Read' and 'ActivityFeed.ReadDlp'.
5: For the following steps you will need to start calling the Office API's, for which you will need a bearer token in the header. To obtain this send the following POST request:
URL: https://login.microsoftonline.com/***tenant_ID***/oauth2/token
Headers: "{'Content-Type': 'application/x-www-form-urlencoded'}"
Data: "grant_type=client_credentials&client_id=Application_ID&client_secret=Secret_Key&resource=https://manage.office.com"
You will receive a JSON response which contains 'access_token'. For all the upcoming API calls, use the following header:
"{'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'bearer access_token'}"
6: Subscribe to the audit log feeds you would like to retrieve. The following exist: 'Audit.General', 'Audit.AzureActiveDirectory', 'Audit.Exchange', 'Audit.SharePoint', 'DLP.All'. The POST for Exchange for example would look like: "https://manage.office.com/api/v1.0/tenant_ID/activity/feed/subscriptions/start?contentType=Audit.Exchange"
7: You are now ready to start retrieving actual logs. Individual logs live inside content blobs, which live inside pages, which live inside feeds (e.g. the Audit.Exchange feed). Therefore, for each feed you would like to retrieve logs from, you must collect all the content blobs (iterating through the pages of them) and then retrieve the actual content from that blob.
To retrieve a page of content blobs use the following URL (change bolded content to your situation): "https://manage.office.com/api/v1.0/tenant_ID/activity/feed/subscriptions/content?contentType=Audit.Exhange&startTime=2022-04-13T09:42:52&endTime=2022-04-14T08:42:52"
This will give you a JSON response with content blobs inside. In the response header check "NextPageUri"; if it contains a URL, call that URL for the next page of content.
Now that you have content blobs, use them to retrieve the actual logs. Each content blob is a JSON dict, which contains a "contentUri" field. Call that URL to retrieve a JSON response with the actual logs inside.
You can do this in most programming/scripting languages, but for larger amounts of logs you will want to retrieve logs in parallel, or it will take a long time.
With a tool
In case you want to use an existing tool, this one is free, works on Linux and Windows, and supports multiple outputs.

Related

Microsoft purview API call

I'm looking to integrate Microsoft's Purview DLP in my web application. My application sends email so I want to block any sensible information from being sent.
Basically a user is going to be under a Microsoft Purview policy to prevent data losses. I want to integrate his DLP policy within my web application.
My two main question were: Is there an Api call that can retrieve then sensitivity label of a certain document of a user? And is there an Api call that can scan a document or string with the regex expression of Microsoft Purview?
Example: Let's say I want to send an email with a document, I want to see through an Graph Api call if we can retrieve the sensitivity label of Microsoft Purview of a certain document linked to a specific user. Then I want to scan the email content and the document content with Microsoft's Purview regex expression to see if there's any sensible information within the email. Ideally scanning the data would be an Graph Api call with a string containing the document and email content and then getting the response from the Api if the email can be sent or not.
Is there a way to do this? All the documentation I found was pretty basic and wasn't answering my questions.
To get the sensitive label through the API:
GET /users/{usersId}/security/informationProtection/sensitivityLabels/{sensitivityLabelId}
or
GET /me/security/informationProtection/sensitivityLabels/{sensitivityLabelId}
For more info:https://learn.microsoft.com/en-us/graph/api/security-sensitivitylabel-get?view=graph-rest-beta#http-request

Trying to get detailed data dumps from new google nest e thermostat

I bought a nest thermostat as I thought it would be able to give me detailed data to showing the target temp and the actual as well as time etc. I needed this for various reasons.
However, it seems the official API "Works with Nest" was closed by Google. I've been able to get postman to ping the same location that the Google Nest Webapp hits and get back the data I need. I want to create a simple webapp to keep polling and save the data locally. However, I'm unable to find the OAuth Client Secret that the Nest Webapp uses to get the authorization code. I had to login via the webapp to get the code in one of the request and then test it out using postman.
Is there any other API that will allow my to poll this data for my Nest easier?
If there isn't another API, is there a way to get the Client ID and Client Secret form the Nest Webapp so I can drop that in mine to use? (I know its hacky, but am I think I'm out of options)

/me/activities request Microsoft Graph Explorer successful 200 response but empty "value":[ ]

I am trying to retrieve the list of entries that can be seen in the Windows 10 timeline view via the User Activities API in Microsoft Graph.
I have selected the UserActivity.ReadWrite.CreatedByApp permission and am getting an HTTP 200 Success response, but the value returned is empty. I am signed in with a Work Account (O365)
https://graph.microsoft.com/beta/me/activities/recent?$top=5
When I look in the timeline view in Windows 10, I see a comprehensive history of activity. The machine is signed in using the same Work Account.
Any help greatly appreciated.
According to the documentation, only activities created by the current AppID will be returned:
The UserActivity.ReadWrite.CreatedByApp permission will also apply extra filtering to the response, so that only activities created by your application are returned. This server-side filtering might result in empty pages if the user is particularly active and other applications have created more recent activities. To get your application's activities, use the nextLink property to paginate.
I'd suggest dropping the $top parameter and following the nextLink URIs to see if your app's activities show up deeper down the stack.

How can I download a OneDrive file with Office365 REST API into a Ruby variable?

I'm building a Ruby on Rails app, and I'd like to integrate some Office365 features.
For instance : I would like to download a file from OneDrive and then attach it to an Email in order to send it via Outlook rest API.
I found this get Item content OneDrive REST API but I dont understand how to use it.
I understand that I have to send a GET request (formated as explained in msdn.microsoft.com) with Rails, which will then provide me a "a pre-authenticated download URL" to download the file.
Then I will have to send a second GET request with this a pre-authenticated download URL to start the download, but I don't understand how to deal with the Response in order to save the file into a variable.
How can I retrieve the file into a variable of my Ruby on Rails App, so that I can attach it to an Email with an Outlook REST API to send it from my own Rail controller ?
Also this workflow is really not optimized in term of Bandwidth and Processing (3 REST API request + 1 download + 1 upload), it will work.
However if it exist a single REST API that direclty attach a OneDrive file to an email to send it, that would ease a lot my life, save energy, save money from Microsoft datacenter, and spare the planet ecology.
Any tutorial, examples, or more explanatory doc would be much appreciated.
--- EDIT ---
Adding link to the email is not wished as the email may have to be send to someone outside of Office365 users, and public link are a security issue for confidential documents.
Any help is welcome.
There isn't a single REST API call you can make currently to do what you want, although being able to easily attach a file from OneDrive to a new email message is a great scenario for Microsoft Graph API, it just isn't supported right now.
If you want to attach the file, you need to do as you mentioned, download the contents of the file, and then upload it again as an attachment to the message.
However, I'd recommend sending a link to the file instead, even though you mentioned you don't want to do that. OneDrive for Business now supports "company shareable links" which are scoped to just the user's organization instead of being available totally anonymously.
Something else to consider: The security concerns of sending an anonymous link aren't that different than sending an attached file. In fact, the anonymous link can be more secure, because access to the file can be monitored and revoked in the future (unlike the attachment, which will always be out there).

How do I get revenue reports from a YouTube CMS account using an API (for an MCN)?

I have access to a YouTube CMS account (for an MCN). On YouTube I can do lots and lots of things with it and this also includes downloading CSV reports which contain detailed information about earnings.
However I want to do some automatic processing of that data and thus access the data using an API instead of a manual CSV download. It looks like the YouTube Analytics Content Owner Reports should contain these data as well, thus I tried to get some data from this API (for now only using the API Explorer) but the only thing I was able to get was a "Forbidden" response.
The API Explorer tells me that for a CMS account I need to specify contentOwner==OWNER_NAME but there is nowhere an explanation what that OWNER_NAME would be. I tried to just insert the displayed name of my CMS account, replacing spaces with underscores, but no success. How do I find out what my owner name is?
Additionally, when I authenticate using OAuth I receive as usual the list of accounts where I can choose which one to use (e.g. all the YouTube channels I am a manager of), but the CMS account is not listed. However if I go to YouTube I can click on the top right corner and then switch to the CMS. No idea if that is important...
Then again, maybe I am totally on the wrong track, because I want to get the reports for all channels connected to my MCN but that does not mean that I own the content. So maybe I am no content owner? In this case: Which is the correct way to request the reports from the API?
First of all, the CMS account is not a separate account you can log in via Oath. It is more like a privilege and it is connected to one of your google/youtube accounts. This is in contrast to youtube's regular channel-management, where each channel has it's own login credentials.
I attached a screenshot of my youtube account-selector-view, where the CMS belongs to the account name#email.com, which is also the account you have to use for oauth authorization to access your CMS reports.
Furthermore you can see the name of the CMS, in this case it "CMSName". So, generally this is the name you would use for contentOwner==CMSName. However, your CMS Name seems to include whitespaces. Unfortunately, i cannot reconstruct this case because of missing admin-rights, but i would suggest you the _ for whitespaces too, because " " and "%20" do not map the regular expression for valid params.
But you said, that you had no success by trying it. But there are too error scenarios:
403 Forbidden: The name of the CMS could either be wrong or the selected OAth account does not have the required privileges. Do you have all required Scopes and selected the correct account?
400 Bad Request: This happens when the request is invalid per se. So if you choose contentOwner==CMSName as ids param, a filter parameter is always required, e.g. channel==[ChannelIdForWhichIHaveCMSRights]. So, a API request, that should generally work, would look like this: https://www.googleapis.com/youtube/analytics/v1/reports?ids=contentOwner%3D%3D[CONTENTOWNER_ID]&start-date=2015-01-01&end-date=2015-01-15&metrics=views&filters=channel%3D%3D[CHANNEL_ID_WITH_CMS_RIGHTS]&access_token=[OATH_TOKEN_FOR_RIGHT_ACCOUNT]
If both cases won't work for you and you're still getting 403 errors, let us do some debugging and try to fetch the content Owner Id. I will now introduce the YouTube Content ID API https://developers.google.com/youtube/partner/.
A few words in advance: You have to activate the API in your developer console, like any other API you want to use for your app. BUT:
Note: The YouTube Content ID API is intended for use by YouTube content partners and is not accessible to all developers or to all YouTube users. If you do not see the YouTube Content ID API as one of the services listed in the Google Developers Console, see www.youtube.com/partner to learn more about the YouTube Partner Program.
You don't see it in the list auf available APIs, unless your account is connected to a CMS and some time has past... It takes 7-14 days unless the Content ID API is available for your account. This is a information i got from the support, but they told me, that it is an automated step.
So, now lets assume, that you already have access to the Content ID API.
You can fetch a list of contentOwnerShips that belong to an account. You can use the API explorer https://developers.google.com/youtube/partner/docs/v1/contentOwners/list#try-it just use as param fetchMine=true and authorize with the https://www.googleapis.com/auth/youtubepartner-content-owner-readonly scope. The response looks like this:
{
"kind": "youtubePartner#contentOwnerList",
"items": [
{
"kind": "youtubePartner#contentOwner",
"id": "[CMS_ID]",
"displayName": "[DisplayName]",
"primaryNotificationEmails": [
"mail#random.xx"
],
"conflictNotificationEmail": "mail#random.xx",
"disputeNotificationEmails": [
"mail#random.xx"
],
"fingerprintReportNotificationEmails": [
"mail#random.xx"
]
}
]
}
This is where you get your CMS_ID from, you can also use it for any API Request as onBehalfOfContentOwner.
To get a list of all channels that belong to the ownership, simply make this request
"https://www.googleapis.com/youtube/v3/channels?part=contentDetails&managedByMe=true&onBehalfOfContentOwner=[CONTENTOWNER]&access_token=[ACCESS_TOKEN]"
But this request requires the granted "https://www.googleapis.com/auth/youtubepartner" scope.
Hoe this could help you, feel free to ask further questions.

Resources