All scripts
Microsoft 365 447
Microsoft 365 Copilot Usage Report
Pulls the Microsoft 365 Copilot usage user detail report from the Microsoft Graph reporting API and breaks out which Copilot enabled apps each user actually opened, so you can see who has a license but is not using it.
Get-CopilotUsageReport.ps1
<#
.SYNOPSIS
Reports on Microsoft 365 Copilot usage per user using the Microsoft Graph reporting API.
.DESCRIPTION
Connects to Microsoft Graph and pulls the Microsoft 365 Copilot usage user detail
report for a chosen reporting period. Breaks out which Copilot enabled apps each
user actually opened, Word, Excel, Outlook, Teams, PowerPoint, so you can see who
has a license but is not using it, and exports the result to CSV.
.AUTHOR
Shehryar Hassan
.EXAMPLE
.\Get-CopilotUsageReport.ps1 -Period D30 -OutputPath C:\Reports\copilot-usage.csv
Pulls the last 30 days of Copilot usage and saves it to the given CSV path.
#>
[CmdletBinding()]
param(
[ValidateSet('D7','D30','D90','D180')]
[string]$Period = 'D30',
[string]$OutputPath = ".\CopilotUsageReport_$(Get-Date -Format 'yyyyMMdd').csv"
)
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Reports)) {
Write-Host "Installing Microsoft.Graph.Reports module..." -ForegroundColor Yellow
Install-Module Microsoft.Graph.Reports -Scope CurrentUser -Force
}
Import-Module Microsoft.Graph.Reports
Import-Module Microsoft.Graph.Authentication
Connect-MgGraph -Scopes "Reports.Read.All" -NoWelcome
Write-Host "Pulling Copilot usage for period $Period..." -ForegroundColor Cyan
$uri = "https://graph.microsoft.com/beta/reports/getMicrosoft365CopilotUsageUserDetail(period='$Period')?`$format=text/csv"
try {
$rawCsv = Invoke-MgGraphRequest -Method GET -Uri $uri -OutputType String
}
catch {
Write-Error "Could not pull the Copilot usage report. Check that the account has Reports.Read.All and that Copilot reporting is enabled for the tenant. $_"
return
}
$rows = $rawCsv | ConvertFrom-Csv
if (-not $rows) {
Write-Warning "No rows came back. This usually means Copilot usage data is not available yet for this tenant, or the period has no activity."
return
}
$summary = foreach ($row in $rows) {
$lastActivity = $row.'Last Activity Date'
$daysSinceActive = if ($lastActivity) {
(New-TimeSpan -Start ([datetime]$lastActivity) -End (Get-Date)).Days
} else {
$null
}
[PSCustomObject]@{
DisplayName = $row.'Display Name'
UserPrincipalName = $row.'User Principal Name'
LastActivityDate = $lastActivity
DaysSinceActive = $daysSinceActive
UsedWord = $row.'Copilot in Word Last Activity Date' -ne ''
UsedExcel = $row.'Copilot in Excel Last Activity Date' -ne ''
UsedOutlook = $row.'Copilot in Outlook Last Activity Date' -ne ''
UsedTeams = $row.'Copilot in Teams Last Activity Date' -ne ''
UsedPowerPoint = $row.'Copilot in PowerPoint Last Activity Date' -ne ''
}
}
$summary | Sort-Object DaysSinceActive -Descending | Export-Csv -Path $OutputPath -NoTypeInformation
$neverActive = ($summary | Where-Object { -not $_.LastActivityDate }).Count
$totalUsers = $summary.Count
Write-Host ""
Write-Host "Report saved to $OutputPath" -ForegroundColor Green
Write-Host "$totalUsers licensed users found, $neverActive with no recorded Copilot activity in the $Period window." -ForegroundColor Cyan
Disconnect-MgGraph | Out-Null
Read it before you run it, and test in a safe tenant first.