All scripts
Microsoft 365 501
OneDrive Recycle Bin Storage Report
Scans every user's OneDrive recycle bin and reports how much space deleted items are taking up, so you can see who is quietly hoarding deleted files instead of guessing why OneDrive storage keeps climbing.
Get-OneDriveRecycleBinReport.ps1
<#
.SYNOPSIS
Reports how much space is used in each user's OneDrive recycle bin.
.DESCRIPTION
Connects to SharePoint Online as a tenant admin, finds every OneDrive
site collection, then checks the first and second stage recycle bins
for each one. Useful when OneDrive storage keeps climbing and nobody
can say why, since deleted files sit in the recycle bin for up to 93
days before they actually free up space.
.AUTHOR
Shehryar Hassan
.EXAMPLE
.\Get-OneDriveRecycleBinReport.ps1 -AdminUrl "https://contoso-admin.sharepoint.com" -MinimumSizeMB 500
Lists every OneDrive whose combined recycle bin size is 500 MB or more.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$AdminUrl,
[Parameter(Mandatory = $false)]
[double]$MinimumSizeMB = 0,
[Parameter(Mandatory = $false)]
[string]$ExportPath = ".\OneDriveRecycleBinReport.csv"
)
if (-not (Get-Module -ListAvailable -Name PnP.PowerShell)) {
Write-Error "PnP.PowerShell module not found. Install it first with: Install-Module PnP.PowerShell -Scope CurrentUser"
return
}
Import-Module PnP.PowerShell
Write-Host "Connecting to $AdminUrl ..."
Connect-PnPOnline -Url $AdminUrl -Interactive
$oneDriveSites = Get-PnPTenantSite -IncludePersonalSite $true -Filter "Url -like '-my.sharepoint.com/personal/'"
Write-Host "Found $($oneDriveSites.Count) OneDrive sites. Checking recycle bins, this can take a while."
$results = @()
$counter = 0
foreach ($site in $oneDriveSites) {
$counter++
Write-Progress -Activity "Checking OneDrive recycle bins" -Status $site.Url -PercentComplete (($counter / $oneDriveSites.Count) * 100)
try {
Connect-PnPOnline -Url $site.Url -Interactive -WarningAction SilentlyContinue
$firstStage = Get-PnPRecycleBinItem -RowLimit 5000
$secondStage = Get-PnPRecycleBinItem -SecondStage -RowLimit 5000
$firstStageSizeMB = [math]::Round((($firstStage | Measure-Object -Property Size -Sum).Sum / 1MB), 2)
$secondStageSizeMB = [math]::Round((($secondStage | Measure-Object -Property Size -Sum).Sum / 1MB), 2)
$totalSizeMB = [math]::Round(($firstStageSizeMB + $secondStageSizeMB), 2)
if ($totalSizeMB -ge $MinimumSizeMB) {
$results += [PSCustomObject]@{
OneDriveUrl = $site.Url
Owner = $site.Owner
FirstStageItems = $firstStage.Count
FirstStageSizeMB = $firstStageSizeMB
SecondStageItems = $secondStage.Count
SecondStageSizeMB = $secondStageSizeMB
TotalRecycleBinMB = $totalSizeMB
}
}
}
catch {
Write-Warning "Could not check $($site.Url): $($_.Exception.Message)"
}
}
$results = $results | Sort-Object TotalRecycleBinMB -Descending
$results | Export-Csv -Path $ExportPath -NoTypeInformation
Write-Host "Done. $($results.Count) OneDrive sites met the $MinimumSizeMB MB threshold."
Write-Host "Report saved to $ExportPath"
$results | Format-Table -AutoSize
Read it before you run it, and test in a safe tenant first.