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:

  1. Scans your tenant for users with < 5 GB free, and

  2. 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

Popular posts from this blog

Bridging the Impossible: Connecting Jira On-Prem to Power Automate & Copilot Studio — The Solution Nobody Built Until Now"

How I Automated My Entire SharePoint Tenant with 150 MCP Tools and Claude Desktop

Azure Management MCP Server