Find candidate Copilot for Microsoft 365 users
Use Graph usage reports to identify licensed users who may be suitable candidates for Copilot for Microsoft 365 based on Teams, Exchange, OneDrive, and app activity.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -NoWelcome -Scopes Reports.Read.All, ReportSettings.ReadWrite.All, User.Read.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-MgGraph -NoWelcome -Scopes Reports.Read.All, ReportSettings.ReadWrite.All, User.Read.All$TempDownloadFile = "c:\temp\x.csv"$ObfuscationChanged = $false$CSVOutputFile = "C:\temp\CopilotUserAnalysis.CSV"# Check if the tenant has obscured real names for reports - see https://office365itpros.com/2022/09/09/graph-usage-report-tips/If ((Get-MgAdminReportSetting).DisplayConcealedNames -eq $True) {$Parameters = @{ displayConcealedNames = $False }Update-MgAdminReportSetting -BodyParameter $Parameters$ObfuscationChanged = $true}# This command finds user accounts with an eligible Copilot base license. It specifies the SKU identifers for# Office 365 E3, Office 365 E5, Microsoft 365 E3, amd Microsoft 365 E5. There are other variants of these SKUs# for government and academic use, so it's important to pass the SKU identifiers in use within the tenantWrite-Host "Finding user accounts to check..."[array]$Users = Get-MgUser -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 3271cf8e-2be5-4a09-a549-70fd05baaa17)" `-ConsistencyLevel Eventual -CountVariable Licenses -All -Sort 'displayName' `-Property Id, displayName, signInActivity, userPrincipalName -PageSize 999Write-Host "Fetching usage data for Teams, Exchange, and OneDrive for Business..."# Get Teams user activity detail for the last 30 days$Uri = "https://graph.microsoft.com/v1.0/reports/getTeamsUserActivityUserDetail(period='D30')"Invoke-MgGraphRequest -Uri $Uri -Method GET -OutputFilePath $TempDownloadFile[array]$TeamsUserData = Import-CSV $TempDownloadFile# Get Email activity data$Uri = "https://graph.microsoft.com/v1.0/reports/getEmailActivityUserDetail(period='D30')"Invoke-MgGraphRequest -Uri $Uri -Method GET -OutputFilePath $TempDownloadFile[array]$EmailUserData = Import-CSV $TempDownloadFile# Get OneDrive data$Uri = "https://graph.microsoft.com/v1.0/reports/getOneDriveActivityUserDetail(period='D30')"Invoke-MgGraphRequest -Uri $Uri -Method GET -OutputFilePath $TempDownloadFile[array]$OneDriveUserData = Import-CSV $TempDownloadFile# Get Apps detail$Uri = "https://graph.microsoft.com/v1.0/reports/getM365AppUserDetail(period='D30')"Invoke-mgGraphRequest -Uri $Uri -Method GET -OutputFilePath $TempDownloadFile[array]$AppsUserData = Import-CSV $TempDownloadFileWrite-Host "Analyzing information..."$CopilotReport = [System.Collections.Generic.List[Object]]::new()ForEach ($User in $Users) {Write-Host ("Checking activity for {0}..." -f $User.displayName)$UserTeamsData = $TeamsUserData | Where-Object 'User Principal Name' -match $User.UserPrincipalName$UserOneDriveData = $OneDriveUserData | Where-Object 'User Principal Name' -match $User.UserPrincipalName$UserEmailData = $EmailUserData | Where-Object 'User Principal Name' -match $User.UserPrincipalName$UserAppsData = $AppsUserData | Where-Object 'User Principal Name' -match $User.UserPrincipalName$LastSignInDate = $null$DaysSinceLastSignIn = $nullIf ($User.signInActivity.LastSignInDateTime) {$LastSignInDate = Get-Date $User.signInActivity.LastSignInDateTime -format 'dd-MMM-yyyy'$DaysSinceLastSignIn = (New-TimeSpan $User.signInActivity.LastSignInDateTime).Days}[int]$Fails = 0; [int]$Points = 0# Test 1 - Has the user signed in withim the last 15 daysIf ($DaysSinceLastSignIn -le 15) {$Test1 = "Pass"$Points = 25} Else {$Test1 = "Fail"$Fails++}# Test 2 - 20 or more Teams chat and attending at least 5 Teams meetingsIf (([int]$UserTeamsData.'Team Chat Message Count' -ge 20) -and ([int]$UserTeamsData.'Meetings Attended Count' -ge 5)) {$Test2 = "Pass"$Points = $Points + 20} Else {$Test2 = "Fail"$Fails++}# Test 3 Sends at least 3 messages daily (22 work days over 30) and receives 6 messages dailyIf (([int]$UserEmailData.'Send Count' -ge 66) -and ([int]$UserEmailData.'Receive Count' -ge 132)) {$Test3 = "Pass"$Points = $Points + 20} Else {$Test3 = "Fail"$Fails++}# Test 4 - OneDrive - views or edits at least 10 filesIf ([int]$UserOneDriveData.'Viewed Or Edited File Count' -ge 10) {$Test4 = "Pass"$Points = $Points + 20} Else {$Test4 = "Fail"$Fails++}# Test 5 - Apps - user must use Outlook, Word, and ExcelIf ($UserAppsData.Outlook -eq "Yes" -and $UserAppsData.Word -eq "Yes" -and $UserAppsData.Excel -eq "Yes") {$Test5 = "Pass"$Points = $Points + 15} Else {$Test5 = "Fail"$Fails++}If ($Points -ge 85) {$CopilotApproved = "Approved"} Else {$CopilotApproved = "Not eligible"}$ReportLine = [PSCustomObject][Ordered]@{User = $User.displayNameUPN = $User.UserPrincipalName'Last Signin' = $LastSignInDate'Days since signin' = $DaysSinceLastSignInPoints = $Points'Test 1: Sign in' = $Test1'Test 2: Teams' = $Test2'Test 3: Email' = $Test3'Test 4: OneDrive' = $Test4'Test 5: Apps' = $Test5'Copilot approved' = $CopilotApproved}$CopilotReport.Add($ReportLine)}[array]$CopilotRecommendations = $CopilotReport | Where-Object {$_.'Copilot Approved' -eq 'Approved'} | Select-Object User, UPN$CopilotReport | Export-CSV -NoTypeInformation $CSVOutputFileClear-HostWrite-Host ""Write-Host ("Based on analysis of user activity and apps, there are {0} users recommended" -f $CopilotRecommendations.count)Write-Host ("to receive Copilot for Microsoft 365 licenses. Details are available in this file: {0}" -f $CSVOutputFile)Write-Host ""$CopilotRecommendations# Switch the tenant report obscure data setting back if necessaryIf ($ObfuscationChanged) {If ((Get-MgAdminReportSetting).DisplayConcealedNames -eq $False) {$Parameters = @{ displayConcealedNames = $True }Update-MgAdminReportSetting -BodyParameter $Parameters}}