Super-Simple OneDrive “Auto-Rescue” 🚀
Super-Simple OneDrive “Auto-Rescue” 🚀
How I auto-find low-storage users and boost their OneDrive space—hands-free
Running out of OneDrive space is a tiny alert that can snowball into tickets, delays, and very cranky teammates. This post gives you a drop-in automation that:
-
Scans your tenant for users with < 5 GB free, and
-
Auto-bumps their OneDrive quota by +5 GB—safely, repeatably, auditable.
It’s clean, modular, and designed for beginners to succeed on Day 1 and for admins to trust in prod.
What you’ll build
-
Script 1 → Exports a CSV of users who have less than 5 GB free (name, UPN, OneDrive URL, usage %).
-
Script 2 → Reads that CSV and raises each user’s quota by 5 GB.
Pro tip: Drop these into a scheduled job and you’ve got “always-green” OneDrive capacity 🌿.
Prerequisites (no secrets in code!)
-
Microsoft 365 admin perms for OneDrive/SharePoint management
-
PowerShell (latest)
-
App-only access via Microsoft Graph + PnP.PowerShell
-
Grant admin consent for these Application permissions:
-
Microsoft Graph:
User.Read.All,Files.ReadWrite.All -
SharePoint:
Sites.FullControl.All
(Tip: Keep secrets in an Azure Key Vault or local secure store—never hardcode.)
-
Quick setup (sanitized—use your values)
-
Replace every placeholder like
<TENANT_ID>,<CLIENT_ID>,<CLIENT_SECRET>,<ADMIN_CENTER_URL>,<MYONEDRIVE_HOST>with your real values (or pull them securely at runtime).
Script 1 — Find users with < 5 GB free (exports CSV)
# Install modules (run once if needed)
# Install-Module Microsoft.Graph -Scope CurrentUser -Force -AllowClobber
# Install-Module PnP.PowerShell -Scope CurrentUser -Force -AllowClobber
# ==== SECURE INPUTS (USE KEY VAULT OR SECRET STORE IN PRODUCTION) ====
$TenantId = "<TENANT_ID>"
$ClientId = "<CLIENT_ID>"
$ClientSecret = "<CLIENT_SECRET>"
# ==== TOKEN ====
$body = @{
grant_type = "client_credentials"
scope = "https://graph.microsoft.com/.default"
client_id = $ClientId
client_secret = $ClientSecret
}
$tokenResponse = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" `
-Method Post -ContentType "application/x-www-form-urlencoded" -Body $body
$headers = @{ Authorization = "Bearer $($tokenResponse.access_token)" }
# Optional: connect to your SharePoint Admin if you plan tenant ops later
# Connect-PnPOnline -Url "<ADMIN_CENTER_URL>" -Interactive
# ==== GET USERS & DRIVE QUOTAS ====
$results = @()
$usersUrl = "https://graph.microsoft.com/v1.0/users`?$select=id,displayName,mail,userPrincipalName"
do {
$users = Invoke-RestMethod -Uri $usersUrl -Headers $headers -Method Get
foreach ($u in $users.value) {
try {
$drive = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users/$($u.id)/drive" -Headers $headers -Method Get
if ($drive -and $drive.quota -and $drive.webUrl) {
$totalGB = [math]::round($drive.quota.total / 1GB, 2)
$usedGB = [math]::round($drive.quota.used / 1GB, 2)
if ($totalGB -gt 0) {
$availableGB = [math]::round($totalGB - $usedGB, 2)
$usedPercent = [math]::round(($usedGB / $totalGB) * 100, 2)
if ($availableGB -lt 5) {
$results += [pscustomobject]@{
DisplayName = $u.displayName
UserPrincipalName = $u.userPrincipalName
UserEmail = $u.mail
SiteUrl = $drive.webUrl
UsedQuotaGB = $usedGB
TotalQuotaGB = $totalGB
AvailableQuotaGB = $availableGB
UsedPercentage = $usedPercent
}
}
}
}
} catch {
Write-Warning "Failed quota for $($u.userPrincipalName): $($_.Exception.Message)"
}
}
$usersUrl = $users.'@odata.nextLink'
} while ($usersUrl)
$csvPath = "$env:USERPROFILE\Downloads\OneDriveUsageReport.csv"
$results | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
Write-Host "Exported: $csvPath"
Why it’s safe & tidy
-
Uses Graph for drive quota (no site guessing).
-
Exports only at-risk users (< 5 GB free).
-
Paginates users so large tenants don’t get cut off.
Script 2 — Bump quota by +5 GB (reads the CSV)
# Requires PnP.PowerShell
# Install-Module PnP.PowerShell -Scope CurrentUser -Force
# Your SharePoint Admin Center URL (sanitized)
$AdminCenterUrl = "<ADMIN_CENTER_URL>" # e.g., https://contoso-admin.sharepoint.com
# Your OneDrive "personal" host (sanitized)
$MyOneDriveHost = "<MYONEDRIVE_HOST>" # e.g., contoso-my.sharepoint.com
# Input CSV from Script 1
$CsvPath = "$env:USERPROFILE\Downloads\OneDriveUsageReport.csv"
if (-not (Test-Path $CsvPath)) { throw "CSV not found at $CsvPath" }
Connect-PnPOnline -Url $AdminCenterUrl -Interactive
$rows = Import-Csv -Path $CsvPath
foreach ($row in $rows) {
$upn = $row.UserPrincipalName
if (-not $upn) { continue }
# Build personal site URL from UPN pattern: personal/<alias>_<domainTld...>
$alias = ($upn.Split('@')[0] -replace '\.', '_')
$domain = ($upn.Split('@')[1] -replace '\.', '_')
$siteUrl = "https://$MyOneDriveHost/personal/${alias}_${domain}"
try {
$site = Get-PnPTenantSite -Url $siteUrl -ErrorAction Stop
$currentQuotaMB = [int]$site.StorageQuota
$newQuotaMB = $currentQuotaMB + (5 * 1024) # +5 GB
$warnQuotaMB = [int]($newQuotaMB - 1024) # warn 1 GB under total
Set-PnPTenantSite -Url $siteUrl -StorageQuota $newQuotaMB -StorageQuotaWarningLevel $warnQuotaMB
$updated = Get-PnPTenantSite -Url $siteUrl
if ([int]$updated.StorageQuota -eq $newQuotaMB) {
Write-Host "✅ $upn → Increased to $([math]::Round($newQuotaMB/1024,2)) GB"
} else {
Write-Warning "⚠️ $upn → Quota change did not persist (check CA/permissions)."
}
} catch {
Write-Warning "❌ $upn → Could not update $siteUrl ($($_.Exception.Message))"
}
}
Why it’s reliable
-
Uses PnP tenant APIs to set StorageQuota precisely.
-
Keeps a 1 GB warning buffer to trigger admin alerts before the next crunch.
-
Works directly off your CSV—no duplicate logic.
Pro tips & guardrails
-
No secrets in scripts: load from SecretManagement, Key Vault, or environment variables.
-
Least privilege in production; scope app-only access to strictly what’s needed.
-
Pilot first: run on a small, non-critical group before rolling tenant-wide.
-
Observability: pipe output to a log file or post a summary to Teams.
TL;DR
With two tidy PowerShell scripts, you can detect low OneDrive headroom and auto-expand storage—before tickets start flying. Set it, schedule it, forget it.
If you want, I can turn this into a one-click runbook or add a Teams summary card after each run. 💬
(Based on internal implementation notes and scripts I refactored and sanitized for public sharing.)
Comments
Post a Comment