All scripts
Microsoft 365 143
Get-PlannerTaskOverdueReport
Reports all overdue tasks across Microsoft Planner plans in a Microsoft 365 group or the whole tenant, using Microsoft Graph, so you can see who is behind without opening every plan by hand.
Get-PlannerTaskOverdueReport.ps1
<#
.SYNOPSIS
Reports overdue tasks across Microsoft Planner plans using Microsoft Graph.
.DESCRIPTION
Connects to Microsoft Graph and walks every Microsoft 365 group the signed in
account can see, pulls each group's Planner plans, and lists every task that
is still open with a due date in the past. Useful for a quick sweep before a
status meeting instead of opening each plan by hand.
.AUTHOR
Shehryar Hassan
.EXAMPLE
.\Get-PlannerTaskOverdueReport.ps1
Lists every overdue Planner task the signed in account has access to, grouped by plan.
.EXAMPLE
.\Get-PlannerTaskOverdueReport.ps1 -GroupId "11111111-2222-3333-4444-555555555555" -ExportPath "C:\Reports\overdue-tasks.csv"
Scopes the report to one Microsoft 365 group and exports the results to a CSV file.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$GroupId,
[Parameter(Mandatory = $false)]
[string]$ExportPath
)
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Groups)) {
Write-Warning "Microsoft.Graph module is not installed. Run: Install-Module Microsoft.Graph -Scope CurrentUser"
return
}
Import-Module Microsoft.Graph.Groups
Import-Module Microsoft.Graph.Planner
Connect-MgGraph -Scopes "Group.Read.All", "Tasks.Read" -NoWelcome
$today = Get-Date
if ($GroupId) {
$groups = @(Get-MgGroup -GroupId $GroupId)
} else {
$groups = Get-MgGroup -All -Filter "resourceProvisioningOptions/Any(x:x eq 'Team')" -ErrorAction SilentlyContinue
if (-not $groups) {
$groups = Get-MgGroup -All
}
}
$results = foreach ($group in $groups) {
$plans = Get-MgGroupPlannerPlan -GroupId $group.Id -ErrorAction SilentlyContinue
foreach ($plan in $plans) {
$tasks = Get-MgPlannerPlanTask -PlannerPlanId $plan.Id -ErrorAction SilentlyContinue
foreach ($task in $tasks) {
if ($task.PercentComplete -lt 100 -and $task.DueDateTime -and ([datetime]$task.DueDateTime) -lt $today) {
$assignees = @()
if ($task.Assignments) {
$assignees = $task.Assignments.AdditionalProperties.Keys
}
[PSCustomObject]@{
Group = $group.DisplayName
Plan = $plan.Title
Task = $task.Title
DueDate = ([datetime]$task.DueDateTime).ToString("yyyy-MM-dd")
DaysOverdue = [math]::Round(($today - [datetime]$task.DueDateTime).TotalDays)
PercentDone = $task.PercentComplete
AssignedTo = ($assignees -join ", ")
}
}
}
}
}
if (-not $results) {
Write-Output "No overdue Planner tasks found."
return
}
$results = $results | Sort-Object DaysOverdue -Descending
if ($ExportPath) {
$results | Export-Csv -Path $ExportPath -NoTypeInformation
Write-Output "Exported $($results.Count) overdue tasks to $ExportPath"
} else {
$results | Format-Table -AutoSize
}
Read it before you run it, and test in a safe tenant first.