Back to script library
Entra / Microsoft 365 · Compliance & audit

Find Copilot interactions with Graph

Find Copilot interactions for a user between two dates using the Graph aiInteractionHistory API with app-only authentication.

Connect & set up

Run these once per session. All scopes are read-only unless the script makes changes.

Connect-MgGraph -AppId $AppId -TenantId $TenantId -CertificateThumbprint $Thumbprint -NoWelcome

Run it

The main script. Copy it, or download the .ps1 and run it from your console.

param(
[string] $TenantId = "",
[string] $AppId = "",
[int] $LookbackDays = 30,
[string] $StartDate = (Get-Date).AddDays(-$LookbackDays).toString('yyyy-MM-ddT00:00:00Z'),
[string] $EndDate = (Get-Date).AddDays(1).toString('yyyy-MM-ddT00:00:00Z')
)
Disconnect-MgGraph | Out-Null
$Thumbprint = '32C9529B1FFD08BCD483A5D98807E47A472C5318'
Connect-MgGraph -AppId $AppId -TenantId $TenantId -CertificateThumbprint $Thumbprint -NoWelcome
$StartDateForReport = Get-Date $StartDate -format 'dd-MMM-yyyy'
$EndDateForReport = Get-Date $EndDate -format 'dd-MMM-yyyy'
$UserPrincipalName = Read-Host "Enter the user principal name for the user to search for"
$User = Get-MgUser -UserId $UserPrincipalName.trim() -ErrorAction SilentlyContinue
If (!$User) {
Write-Host ("User {0} not found" -f $UserPrincipalName)
Break
}
# Has the account got a Copilot license?
[array]$UserLicenses = Get-MgUserLicenseDetail -UserId $User.Id | Select-Object -ExpandProperty SkuId
If ("639dec6b-bb19-468b-871c-c5c441c4b0cb" -notin $UserLicenses) {
Write-Host ("User {0} does not have a Copilot license, so we can't check their Copilot interactions" -f $User.DisplayName)
Break
}
$Uri = ("https://graph.microsoft.com/beta/copilot/users/{0}/interactionHistory/getAllEnterpriseInteractions?`$top=100&`$filter=createdDateTime gt {1} and createdDateTime lt {2}" `
-f $User.Id, $StartDate, $EndDate)
Write-Host ("Searching for Copilot interactions for {0} between {1} and {2}" -f $User.DisplayName, $StartDateForReport, $EndDateForReport)
[array]$CopilotData = $null
# Get the first set of records
[array]$Data = Invoke-MgGraphRequest -Uri $Uri -Method GET
$CopilotData = $Data.Value
If (!($CopilotData)) {
Write-Host ("No Copilot interactions found for {0} between {1} and {2}" -f $User.DisplayName, $StartDateForReport, $EndDateForReport)
Break
}
$Nextlink = $Data.'@odata.nextLink'
While ($null -ne $Nextlink) {
Write-Host ("Fetching more records - currently at {0}" -f $CopilotData.count)
[array]$Data = Invoke-MgGraphRequest -Uri $Nextlink -Method Get
$CopilotData += $Data.Value
$Nextlink = $Data.'@odata.nextLink'
}
# Remove any null records
$CopilotData = $CopilotData | Where-Object { $_ -ne $null }
$CopilotData = $CopilotData | Sort-Object {$_.createdDateTime -as [datetime]}
Write-Host ("{0} Copilot interactions for {1} between {2} and {3} have been retrieved" -f $CopilotData.count, $User.DisplayName, $StartDateForReport, $EndDateForReport)
$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($Record in $CopilotData) {
If ($Record.createdDateTime) {
$Timestamp = Get-Date $Record.createdDateTime -format 'dd-MMM-yyyy HH:mm:ss'
} else {
$Timestamp = $null
}
Switch ($Record.interactionType) {
"userPrompt" {
$AppName = $User.displayname
}
"aiResponse" {
$AppName = $Record.from.application.displayname
}
Default {
$AppName = $Record.interfrom.application.displayname
}
}
If ($Record.body.content.length -gt 100) {
$Body = $Record.body.content.ToString().Substring(0,100)
} else {
$Body = $Record.body.content.ToString()
}
$AutoGeneratedFlag = $False
# This section checks for some of the fingerprints that indicate that the interaction is automatic rather than user-generated
Switch ($AppName) {
"Copilot in Outlook" {
$AppName = "Outlook"
If ($Body -like "*VisualTheming*" -or $Body -like "*data:image;base64*") {
$AutoGeneratedFlag = $True
}
}
"Copilot in Word" {
If ($Body -like "*[AutoGenerated]*") {
$AutoGeneratedFlag = $True
}
}
}
$ReportLine = [pscustomobject]@{
User = $User.UserPrincipalName
Timestamp = $Timestamp
'Copilot App' = $AppName
AppId = $AppId
Contexts = ($Record.contexts.displayName -join ", ")
InteractionType = $Record.interactionType
ThreadId = $Record.sessionid
Body = $Body
Attachments = ($Record.attachments.name -join ", ")
Mentions = ($Record.mentions.name -join ", ")
Links = ($Record.Links.LinkUrl -join ", ")
AutoGenerated = $AutoGeneratedFlag
}
$Report.Add($ReportLine)
}
$Report | Out-GridView -Title 'Copilot Interactions for $User'
# Some basic computations
$NumberOfAutomaticInteractions = $Report | Where-Object { $_.AutoGenerated -eq $True } | Measure-Object | Select-Object -ExpandProperty Count
$UserInteractions = $Report | Where-Object {$_.InteractionType -eq "userPrompt"} | Measure-Object | Select-Object -ExpandProperty Count
$CopilotResponses = $Report.Count - ($UserInteractions + $NumberOfAutomaticInteractions)
$PercentCopilotResponses = ($CopilotResponses/$Report.Count).ToString("P")
$PercentAutomaticInteractions = ($NumberOfAutomaticInteractions/$Report.Count).ToString("P")
$PerecentUserInteractions = ($UserInteractions/$Report.Count).ToString("P")
Write-Host ""
Write-Host ("Copilot interactions for {0} betweeen {1} and {2}" -f $User.DisplayName, $StartDateForReport, $EndDateForReport)
$Report | Group-Object 'Copilot App' | Select-Object Name, Count | Sort-Object Count -Descending | Format-Table
Write-Host ("{0} of the {1} interactions are automatic ({2})" -f $NumberOfAutomaticInteractions, $Report.Count, $PercentAutomaticInteractions)
Write-Host ("{0} of the interactions are user prompts ({1})" -f $UserInteractions, $PerecentUserInteractions)
Write-Host ("{0} of the interactions are Copilot responses to user prompts" -f $PercentCopilotResponses)
# Generate reports
If (Get-Module ImportExcel -ListAvailable) {
$ExcelGenerated = $True
$ExcelTitle = ("Copilot interactions for {0} between {1} and {2}" -f $User.DisplayName, $StartDateForReport, $EndDateForReport)
Import-Module ImportExcel -ErrorAction SilentlyContinue
$OutputXLSXFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\CopilotInteractions.xlsx"
If (Test-Path $OutputXLSXFile) {
Remove-Item $OutputXLSXFile -ErrorAction SilentlyContinue
}
$Report | Export-Excel -Path $OutputXLSXFile -WorksheetName "Copilot Interactions" -Title $ExcelTitle -TitleBold -TableName "CopilotInteractions"
} Else {
$OutputCSVFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\CopilotInteractions.csv"
$Report | Export-Csv -Path $OutputCSVFile -NoTypeInformation -Encoding Utf8
}
If ($ExcelGenerated) {
Write-Host ("An Excel worksheet containing the report data is available in {0}" -f $OutputXLSXFile)
} Else {
Write-Host ("A CSV file containing the report data is available in {0}" -f $OutputCSVFile)
}

Parameters

ParameterDefaultNotes
-TenantId""Microsoft Entra tenant ID for app-only Graph authentication.
-AppId""Application (client) ID for the app registration used to connect.
-LookbackDays30Number of days back to search Copilot interaction history.
-StartDate(Get-Date).AddDays(-30).toString('yyyy-MM-ddT00:00:00Z')Start of the Copilot interaction search window.
-EndDate(Get-Date).AddDays(1).toString('yyyy-MM-ddT00:00:00Z')End of the Copilot interaction search window.
Attribution