In the last hours of 2024, something interesting happened – I got a notification about Microsoft 365 Roadmap update. Now, I know Dec 31st is a working day for many, but the whole pre-/post-Christmas period is usually a “dead” one when it comes to software releases. Granted, the Roadmap notification only had a handful of updated items to look at, but one of them was something we’ve been waiting for a while now – new Copilot reports. Were I a cynical man, I’d remark that someone really needed to meet a deadline on this one 😀
Anyway, as you can see from the screenshot above, we now have the Microsoft Copilot with Enterprise data protection report available within the M365 Admin Center. Of course, Microsoft’s marketing team has outdone themselves with naming all the different variants of AI assisted tools and features, so if you are confused as to what this report is all about and how it differs from the Microsoft 365 Copilot usage reports we got a while back, you’re probably not the only one. In a nutshell, this report should give you an overview of any of your Microsoft 365 users that have leveraged the free Microsoft Copilot experience provided via Bing (and few other endpoints) in the past 30 to 180 days, subtly hinting how important AI is for their work life and nudging you to assign them a Microsoft 365 Copilot license.
To clarify, the user must be accessing Microsoft Copilot logged in via their Microsoft 365/Entra ID account. This is where the Enterprise data protection part comes in play, as Microsoft is providing extra layer of protections and promises not to train the models based on your data for any interactions initiated by users with Entra ID accounts. Another thing to keep in mind is that the report will only surface data for users without Microsoft 365 Copilot license assigned, as user that have such are already covered by the Microsoft 365 Copilot usage reports. Anyway, you can find additional details and screenshots in the official documentation for the Microsoft Copilot with enterprise data protection reports, no point repeating those here.
I do want to talk about something more important though – the fact that said reports are not exposed via the Graph API. This is of course hardly a surprise, as by now you’ve probably grown accustomed to Microsoft’s approach on reports. In fact, there have been quite few other Copilot-related improvements released recently that you can only access via the Admin Center, for example, reporting on Copilot agents or reporting on Business chat interactions, not to mention the whole Readiness dataset. I don’t have much hope that we will see Graph API endpoints for said data in the coming months, if ever, so I wanted to post a quick article as to what other methods we can use to get to it.
In fact, I already published an article on this a while back, but I figured a Copilot-focused example might be nice. Without further ado, here is how to get the Copilot datasets programmatically. First, we will need to handle authentication, and here is the bad news – we can only leverage delegate permissions for this method. In other words, we need to obtain a token in the context of a user, and said user must be assigned a role with sufficient permissions to read report data, such as the Reports Reader role or any of the roles listed here. The good news is that this is pretty much the only prerequisite. We do not need an application registration, as we will be leveraging the built-in Microsoft PowerQuery For Excel service principal. Here’s the code to obtain an access token:
#Load the MSAL binaries Add-Type -Path "C:\Program Files\WindowsPowerShell\Modules\MSAL\Microsoft.Identity.Client.dll" $appId = "a672d62c-fc7b-4e81-a576-e60dc46e951d" #"Microsoft PowerQuery For Excel" built-in application $tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #your tenant ID $redirectURI = "https://preview.powerbi.com/views/oauthredirect.html" #Get a token for the https://reports.office.com resource $app = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($appId).WithRedirectUri($redirectURI).WithTenantId($tenantId).WithBroker().Build() $Scopes = New-Object System.Collections.Generic.List[string] $Scope = "https://reports.office.com/.default" $Scopes.Add($Scope) $token = $app.AcquireTokenInteractive($Scopes).ExecuteAsync().Result #Set the auth header $authHeader = @{ 'Authorization'="Bearer $($token.AccessToken)" }
As we are using the delegate permissions model, the token request is an interactive one, and you might need to perform MFA depending on your tenant’s configuration. Once the process completes successfully, you will have a valid access token that we can utilize to obtain the Copilot data. But first, we need to know which endpoint to query for it, and the method(s) for the corresponding dataset. The endpoints are region-dependent, though you should be fine if you just hardcode the values. Just in case, here is the code to obtain the region for your tenant:
$res = Invoke-WebRequest -Uri "https://reports.office.com/private/intraTenantConfig/host?tenantid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -Headers $authHeader ($res.Content | ConvertFrom-Json).host #Get the metadata document $res = Invoke-WebRequest -Uri 'https://reports.office.com/internal/ux/$metadata' -Headers $authHeader [xml]$metadata = $res.Content #Get all available reports $allReports = ($metadata.Edmx.DataServices.Schema | ? {$_.Namespace -eq "Default"}).EntityContainer.EntitySet
Now the $allReports variable should contain a list of all report endpoints. We can filter out jus the Copilot-related ones:
From top to bottom, we have the Business chat dataset, the “standard” Microsoft 365 Copilot reports, the “adoption” reports, the Copilot Agents reports, the Microsoft Copilot with Enterprise data protection reports, the “recommendation” data and the Copilot readiness dataset. As you can see, every Copilot-related datapoint that’s exposed within the Microsoft 365 Admin center can be found here, so now we know which methods to use. On a side note, you can also get all other M365 reports and insights using the same method, not just the Copilot ones, as explained in our previous article.
As an example, let’s get the Copilot Readiness dataset. From the screenshot above, we can see two methods available for that task, with the getCopilotReadinessActivityUserDetailV2 one being the obvious choice. To query the data, lets use the region-dependent endpoint we obtained above, which in the case of my tenant would be reportsweu.office.com. Apart from that, we add the /internal/ux suffix and finally, the method itself. The query parameters include the tenantId, an optional pageSize value, and the Aggregate period for which to get the data for. Refer to the metadata document we obtained above for details on the parameters supported by each method/report.
#Copilot readiness $res = Invoke-WebRequest -Uri "https://reportsweu.office.com/internal/ux/getCopilotReadinessActivityUserDetailV2?pageSize=200&tenantId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&Aggregate=M30" -Headers $authHeader ($res.Content | ConvertFrom-Json).value
As illustrated on the screenshot below, there is 1:1 correspondence between the Microsoft 365 Copilot Readiness page within the Admin Center and the data we obtained via the getCopilotReadinessActivityUserDetailV2 method.
As another example, let’s also take a look at the Business chat data. To get the “adoption” metrics, we can leverage these two methods: getBusinessChatAdoptionByProducts and getBusinessChatAdoptionByDate. As for the Business chat usage data, it’s exposed via the standard Copilot user activity report, albeit you’ll need its V3 variant (getCopilotActivityUserDetailV3).
#Business chat adoption $res = Invoke-WebRequest -Uri "https://reportsweu.office.com/internal/ux/getBusinessChatAdoptionByProducts?pageSize=200&tenantId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&Aggregate=M180" -Headers $authHeader ($res.Content | ConvertFrom-Json).value #Copilot usage user detail $res = Invoke-WebRequest -Uri "https://reportsweu.office.com/internal/ux/getCopilotActivityUserDetailV3?pageSize=200&tenantId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&Aggregate=M180" -Headers $authHeader ($res.Content | ConvertFrom-Json).value
The results of the two queries are shown on the screenshot below. Of course, there’s not much too look at in a tenant with a single Copilot license, but the important part is that we have all the relevant Business chat adoption and usage details. What is even better is that those match exactly the data presented in the Microsoft 365 Admin center (second screenshot).
And yes, I haven’t forgotten about the Microsoft Copilot with Enterprise data protection data, albeit I have zero usage of it in my own tenant, so nothing to show. Anyway, you can get said data via the getCopilotEDPAdoptionByPeriod method:
#Copilot EDP $res = Invoke-WebRequest -Uri "https://reportsweu.office.com/internal/ux/getCopilotEDPAdoptionByPeriod?pageSize=200&tenantId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&Aggregate=M180" -Headers $authHeader ($res.Content | ConvertFrom-Json).value
And with that, we can wrap this article up. You should now have a better idea as to where the Microsoft 365 Admin Center fetches data for its reports, and can use the methods outlined above to fetch said data programmatically. The only downside is that you have to rely on delegate permissions for this, but as Microsoft is in no particular hurry to provide support for the new Copilot datasets within the Graph API, I though you might appreciate this “workaround”.
P.S. Another “workaround” you can use in order to generate additional/more robust Copilot reports and insights is via the recently introduced AI interactions Graph API endpoint. While its purpose is mostly to address compliance requirements, it does expose the prompts your users issued against (Microsoft 365) Copilot, so there is a plenty of data to leverage therein. If the endpoint works for you that is, as I still cannot get it to work in my tenant. Tony seems to have had better luck with them, so I’ll refer you to his article instead.
The workaround you’ve shared is incredibly helpful, but it does raise an interesting question about how Microsoft balances user privacy with programmatic accessibility for admins. Do you think the lack of Graph API endpoints for these datasets is a deliberate move to reinforce privacy, or just a case of slow rollout? Either way, it’s fascinating to see how these limitations shape admin workflows.
You cannot use this workaround without an admin role, and same admin roles allow you to access the data unscrambled. There is no true privacy in M365, for example the AiInteractionHistory endpoint allows you to get the actual Copilot prompts users have leveraged, eDiscovery gives you access to all user’s data, Audit log hides no details, etc…