Use Analytics Rather Than Emotion to Assign Copilot for Microsoft 365 Licenses

Last July, I wrote about the issue facing organizations considering deployment of Copilot for Microsoft 365: how to decide who gets Copilot licenses. Of course, some guidelines exist. For instance, you can only assign licenses to users with an eligible base license. Within enterprises, that means Office 365 E3 or E5, or Microsoft 365 E3 or E5. You could decide to assign Copilot licenses to every user account with an eligible base license. That’s certainly a straightforward and simple decision, but at $360/user annually, it could also be an expensive mistake if some of the assignees don’t make sufficient use of their eligible license to make Copilot worthwhile.

In this article, I show how to use Microsoft 365 usage data retrieved using Graph APIs to assess the suitability of user accounts for Copilot licenses. The basic idea is that a user is suitable if:

  • They have recently signed into the tenant.
  • They use the Outlook, Word, and Excel apps.
  • Their usage of Teams, Email, and OneDrive for Business reaches a certain level.

The Microsoft 365 admin center includes information to help tenants assess readiness for Copilot (Figure 1).

Copilot for Microsoft 365 guidance in the Microsoft 365 admin center.
Figure 1: Copilot for Microsoft 365 guidance in the Microsoft 365 admin center

Unfortunately, the data listed in the Microsoft 365 admin center includes guest accounts, accounts synchronized into the tenant through membership of a multi-tenant organization, and accounts used to support shared mailboxes. I wanted to create something more precise and flexible to meet the needs of organizations who want control over their assessment. Besides, it’s a nice example of how to access data that’s available to tenants that is sometimes overlooked.

The recommendations made by the script cannot be considered definitive. The script is intended to illustrate the principal of using Microsoft 365 usage reports to assess user activity tailored for Copilot, so it’s only one data point in what can be a complex selection process. Another example of reporting usage data on a per-user basis can be found here.

Permissions and Settings

The script uses the Microsoft Graph PowerShell SDK. The Reports.Read.All permission is necessary to access the usage data. You’ll also need the User.Read.All permission to read details of user accounts and the ReportSettings.ReadWrite.All permission to read the administrative reports setting which controls whether the Graph outputs obfuscated data (to protect privacy).

In this instance, we need to see the user display names and user principal names to know who should receive Copilot licenses, so the script needs to check what the tenant setting is and update the setting if necessary. See this article for more information. Here is the code used in the script to check if the tenant setting is to conceal (obfuscate) names. If true, the script updates the tenant setting temporarily to allow unobscured data to be reported. If necessary, the script later reverses the change.

If ($DisplaySettings['displayConcealedNames'] -eq $true) { # data is obscured, so let's reset it to allow the report to run
   $ObscureFlag = $true
   Write-Host "Setting tenant data concealment for reports to False" -foregroundcolor red
   Invoke-MgGraphRequest -Method PATCH -Uri '' `
    -Body (@{"displayConcealedNames"= $false} | ConvertTo-Json) 

Finding User Accounts with an Eligible Copilot Base License

The first step is to find the set of user accounts that have an eligible base license. This command uses a filter to check the set of assigned licenses for each account against the product identifiers (SKU identifiers) for Office 365 E3, Office 365 E5, Microsoft 365 E3, and Microsoft 365 E5. Because the filter checks multiple values in a complex property, it becomes a complex Graph query. That’s why the ConsistencyHeader and CountVariable parameters are present:

[array]$Users = Get-MgUser -Property Id, displayName, signInActivity, userPrincipalName `
-Filter "assignedLicenses/any(s:s/skuId eq 6fd2c87f-b296-42f0-b197-1e91e994b900) `
    or assignedLicenses/any(s:s/skuid eq c7df2760-2c81-4ef7-b578-5b5392b571df) `
    or assignedLicenses/any(s:s/skuid eq 05e9a617-0261-4cee-bb44-138d3ef5d965) `
    or assignedLicenses/any(s:s/skuid eq 06ebc4ee-1bb5-47dd-8120-11324bc54e06)" `
    -ConsistencyLevel Eventual -CountVariable Licenses -All -Sort 'displayName' 

It’s important to say that the values shown here are for the standard versions of the E3 and E5 licenses. Different product identifiers exist for the variations of these licenses used in academic and government tenants and for variations such as the Office 365 E5 without PSTN Conferencing license. Make sure that you filter based on the product identifiers used within your tenant. For instance, if you’re a small to medium tenant (under 300 seats), the eligible base products are Microsoft 365 Business Standard and Business Premium. This Microsoft page lists all product identifiers.

Fetching Usage Data

Graph access is available to a wide range of Microsoft 365 usage reports. The script used four types of usage reports with data extracted for the last 30 days:

  • Teams user detail.
  • Outlook user detail.
  • OneDrive user detail.
  • Apps.

I selected these usage reports because I suspect that Teams, Outlook, and OneDrive are likely to be high touchpoints for Copilot. I wanted to include SharePoint, but there’s currently a bug that means the SharePoint user detail report returns zero data. The apps report only tells you what apps someone uses in a period. It doesn’t tell you what they do in an app. The script therefore just checks if users access Outlook, Teams, and Excel during the review period. Here’s an example of fetching the Teams user activity detail and putting it into an array for later access:

$Uri = "'D30')"
Invoke-MgGraphRequest -Uri $Uri -Method GET -OutputFilePath $TempDownloadFile
[array]$TeamsUserData = Import-CSV $TempDownloadFile

Later, the script fetches the data for the current user with a command like:

$UserTeamsData = $TeamsUserData | Where-Object 'User Principal Name' -match $User.UserPrincipalName

More subtle checks, like if the user has an Exchange Online mailbox, are not done. Also, the script doesn’t report if accounts use an eligible update channel for the Microsoft 365 enterprise (desktop) apps. I don’t know of a way to do this using a Graph API. The Microsoft 365 admin center includes this information, so it can be done, but maybe not with a public API.

Assessing Suitability for Copilot

The next stage in the script is to measure each user against five tests to check the users suitability:

  • Signed in within the last 15 days.
  • Sent at least 30 Teams chat messages and attended at least 5 meetings in the last 30 days.
  • Sent at least 3 emails and received 6 emails daily.
  • Viewed or edited at least 10 files in OneDrive over the last 30 days.
  • Used Outlook, Word, and Excel over the last 30 days.

After processing all users, the script makes its recommendations:

Based on analysis of user activity and apps, there are 4 users recommended
to receive Copilot for Microsoft 365 licenses. Details are available in this file: C:\temp\CopilotUserAnalysis.CSV

User         UPN
----         ---

Ken Bowers   Ken
Kim Akers
Sean Landy
Tony Redmond

The checks are very simple PowerShell code and can be easily altered to suit your needs. Each test earns a certain number of points with the maximum being 100. If a user scores over 85, they’re ready to go with Copilot for Microsoft 365. Again, you could decide on a different threshold or vary the weighting assigned to each test.

Once again, this is an example of how to exploit the Microsoft 365 usage data accessible through the Graph to help make decisions. In this case, by looking at how accounts use some important Microsoft 365 workloads and apps, we can get a sense of how ready they are to make use of Copilot for Microsoft 365. If you want to experiment with the script, you can download the code from GitHub. Happy scripting!

TEC Talk: Advanced Hypothesis-Based Threat Hunting with Microsoft Azure Solutions

TEC Talk:

Join Dr. Mike Jankowski-Lorek’s Free Webinar on March 14th @ 11 AM EST.

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to, Tony also writes at to support the development of the eBook. He has been a Microsoft MVP since 2004.


  1. Chris

    In global orgs this can be challenging as this can be interpreted as tracking productivity and other processing of PII. North America only based companies this shouldn’t be an issue, but check with your EU Works Councils and other EU local laws.

    1. Avatar photo
      Tony Redmond

      I think it’s more likely to run into a problem with works council if you report the usage data on an ongoing basis. If it’s only used for a specific one-time purpose, it’ll probably be OK, but your point is taken.

  2. Will Fulmer

    Great post @Tony Redmond! Love it!

  3. Frank

    Painfully slow to run with 300k users 🙂

    1. Avatar photo
      Tony Redmond

      It’s kind of hard to write a script that will work (fast) for 300K users. Fetching the usage logs is probably quite fast. The problem might be fetching the user accounts. One suggestion is to break up the processing across different sets of accounts (department, country, jobtitle, first character of surname) and combine the results at the end.

Leave a Reply