All scripts
Microsoft 365 95
Microsoft To Do Task List Report
Reports on Microsoft To Do task lists and task counts for each user in your tenant, using Microsoft Graph. Handy before offboarding someone or when you just want a picture of how To Do is actually being used.
Get-MicrosoftToDoTaskListReport.ps1
<#
.SYNOPSIS
Reports on Microsoft To Do task lists and task counts across your tenant's users.
.DESCRIPTION
Loops through a list of users (or every licensed user in the tenant) and uses
Microsoft Graph to pull each person's To Do task lists, along with open and
completed task counts per list. Useful before offboarding someone, or when
you just want a picture of how To Do is actually being used instead of
guessing. Requires the Microsoft.Graph.Users and Microsoft.Graph.Tasks
modules, and delegated or application permissions for Tasks.Read.All and
User.Read.All.
.AUTHOR
Shehryar Hassan
.EXAMPLE
Connect-MgGraph -Scopes "Tasks.Read.All","User.Read.All"
.\Get-MicrosoftToDoTaskListReport.ps1 -OutputPath "C:\Reports\ToDoReport.csv"
.EXAMPLE
.\Get-MicrosoftToDoTaskListReport.ps1 -UserPrincipalName "jane.doe@contoso.com" -OutputPath ".\jane-todo.csv"
#>
param(
[Parameter(Mandatory = $false)]
[string[]]$UserPrincipalName,
[Parameter(Mandatory = $false)]
[string]$OutputPath = ".\ToDoTaskListReport.csv"
)
if (-not (Get-MgContext)) {
Write-Error "Not connected to Microsoft Graph. Run Connect-MgGraph -Scopes 'Tasks.Read.All','User.Read.All' first."
return
}
if ($UserPrincipalName) {
$users = foreach ($upn in $UserPrincipalName) {
Get-MgUser -UserId $upn -Property Id, DisplayName, UserPrincipalName
}
}
else {
Write-Host "No UserPrincipalName supplied, pulling all enabled users. This can take a while in a large tenant."
$users = Get-MgUser -All -Filter "accountEnabled eq true" -Property Id, DisplayName, UserPrincipalName
}
$results = New-Object System.Collections.Generic.List[object]
$total = $users.Count
$counter = 0
foreach ($user in $users) {
$counter++
Write-Progress -Activity "Reading To Do task lists" -Status "$($user.UserPrincipalName)" -PercentComplete (($counter / $total) * 100)
try {
$lists = Get-MgUserTodoList -UserId $user.Id -All -ErrorAction Stop
}
catch {
Write-Warning "Could not read task lists for $($user.UserPrincipalName): $($_.Exception.Message)"
continue
}
if (-not $lists -or $lists.Count -eq 0) {
$results.Add([PSCustomObject]@{
DisplayName = $user.DisplayName
UserPrincipalName = $user.UserPrincipalName
TaskListName = "(none)"
OpenTaskCount = 0
CompletedTaskCount = 0
})
continue
}
foreach ($list in $lists) {
try {
$tasks = Get-MgUserTodoListTask -UserId $user.Id -TodoTaskListId $list.Id -All -ErrorAction Stop
}
catch {
Write-Warning "Could not read tasks in list '$($list.DisplayName)' for $($user.UserPrincipalName): $($_.Exception.Message)"
continue
}
$openCount = ($tasks | Where-Object { $_.Status -ne "completed" }).Count
$completedCount = ($tasks | Where-Object { $_.Status -eq "completed" }).Count
$results.Add([PSCustomObject]@{
DisplayName = $user.DisplayName
UserPrincipalName = $user.UserPrincipalName
TaskListName = $list.DisplayName
OpenTaskCount = $openCount
CompletedTaskCount = $completedCount
})
}
}
Write-Progress -Activity "Reading To Do task lists" -Completed
$results | Sort-Object DisplayName, TaskListName | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Report written to $OutputPath ($($results.Count) rows across $($users.Count) users)"
Read it before you run it, and test in a safe tenant first.