All scripts
Microsoft 365 715
Teams Meeting Attendance Report
Pulls the attendance report for a specific Teams meeting using Microsoft Graph, including join and leave times for each participant, and exports it to CSV.
Get-TeamsMeetingAttendanceReport.ps1
<#
.SYNOPSIS
Exports the attendance report for a Microsoft Teams meeting to CSV.
.DESCRIPTION
Uses Microsoft Graph to find a Teams meeting by its join URL for a given
organizer, then pulls the attendance report for that meeting, including
each participant's join time, leave time, and total duration. Results
are exported to a CSV file so you can hand them to whoever asked for
proof of attendance, a training log, or a billing record.
Requires the Microsoft.Graph module and delegated or application
permissions for OnlineMeetings.Read.All and OnlineMeetingArtifact.Read.All.
.AUTHOR
Shehryar Hassan
.EXAMPLE
.\Get-TeamsMeetingAttendanceReport.ps1 -OrganizerUserId "jane.doe@contoso.com" -JoinUrl "https://teams.microsoft.com/l/meetup-join/..." -OutputPath "C:\Reports\attendance.csv"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$OrganizerUserId,
[Parameter(Mandatory = $true)]
[string]$JoinUrl,
[Parameter(Mandatory = $false)]
[string]$OutputPath = ".\TeamsMeetingAttendance.csv"
)
if (-not (Get-MgContext)) {
Connect-MgGraph -Scopes "OnlineMeetings.Read.All", "OnlineMeetingArtifact.Read.All"
}
Write-Host "Looking up meeting for organizer $OrganizerUserId..."
$meetingUri = "https://graph.microsoft.com/v1.0/users/$OrganizerUserId/onlineMeetings?`$filter=JoinWebUrl eq '$JoinUrl'"
$meeting = Invoke-MgGraphRequest -Method GET -Uri $meetingUri
if (-not $meeting.value -or $meeting.value.Count -eq 0) {
Write-Warning "No meeting found for that organizer and join URL. Double check both values."
return
}
$meetingId = $meeting.value[0].id
Write-Host "Found meeting $meetingId. Pulling attendance reports..."
$reportsUri = "https://graph.microsoft.com/v1.0/users/$OrganizerUserId/onlineMeetings/$meetingId/attendanceReports"
$reports = Invoke-MgGraphRequest -Method GET -Uri $reportsUri
if (-not $reports.value -or $reports.value.Count -eq 0) {
Write-Warning "No attendance reports exist for this meeting yet. Reports are usually available a few minutes after the meeting ends."
return
}
$latestReportId = ($reports.value | Sort-Object -Property meetingEndDateTime -Descending)[0].id
$recordsUri = "https://graph.microsoft.com/v1.0/users/$OrganizerUserId/onlineMeetings/$meetingId/attendanceReports/$latestReportId/attendanceRecords"
$records = Invoke-MgGraphRequest -Method GET -Uri $recordsUri
$rows = foreach ($record in $records.value) {
foreach ($interval in $record.attendanceIntervals) {
[PSCustomObject]@{
DisplayName = $record.identity.displayName
Email = $record.emailAddress
Role = $record.role
JoinTime = $interval.joinDateTime
LeaveTime = $interval.leaveDateTime
DurationMin = [math]::Round($interval.durationInSeconds / 60, 1)
}
}
}
$rows | Sort-Object DisplayName, JoinTime | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Host "Attendance report saved to $OutputPath ($($rows.Count) rows)."
Read it before you run it, and test in a safe tenant first.