Targeted Offsite OneDrive Backups with PnP PowerShell
Welcome back to the terminal garden. Sometimes, amidst the complex cloud infrastructure, you just need a straightforward, targeted offsite backup of a specific user's Microsoft 365 OneDrive. No heavy third-party enterprise tools, just plain PowerShell and a bit of automation logic.
Here is a functional scribble from my digital toolbox to recursively download a user's entire OneDrive structure to a local machine.
The Prerequisites
Before running the backup, Microsoft 365 requires a few specific keys to the kingdom:
-
PnP PowerShell Module: Ensure you have the latest
PnP.PowerShellmodule installed (Install-Module PnP.PowerShell). - Permissions: You cannot back up what you cannot see. Even as a Global Admin, you must grant yourself Site Collection Administrator rights to the specific user's OneDrive via the M365 Admin Center before running the script.
- Entra ID App Registration: Microsoft retired the default PnP multi-tenant app for interactive logins in September 2024. You must register your own minimal App in Entra ID. Fortunately, we can automate that part too.
The Pre-Game: Automating the App Registration
Instead of clicking through the Entra ID portal to set up redirect URIs and API permissions, you can use this quick snippet (requires Global Admin rights). It registers the app, configures http://localhost for the login prompt, grants the necessary SharePoint permissions, and handles the admin consent all in one go.
PowerShell
# =====================================================================
# App Registration Scribble
# =====================================================================
Import-Module PnP.PowerShell $TenantName = "contoso.onmicrosoft.com"
$AppName = "PnP PowerShell Backup" Write-Host "Creating Entra ID App Registration..." -ForegroundColor Cyan
Register-PnPEntraIDAppForInteractiveLogin -ApplicationName $AppName -Tenant $TenantName # NOTE: Grab the "Client ID" from the console output once this finishes!
The Main Event: The Backup Script
Once you have your Client ID from the step above, you can run the actual backup. This script connects to the target OneDrive, checks for the standard "Documents" library, and recursively downloads all files and folders while maintaining the original directory structure.
PowerShell
# =====================================================================
# maik.ing | the terminal garden - OneDrive Offsite Backup
# =====================================================================
Import-Module PnP.PowerShell # 1. Define Variables
$UserUPN = "jane.doe@contoso.com"
$Tenant = "contoso"
$BasePath = "C:\LocalBackups\OneDrive"
$BackupDir = Join-Path -Path $BasePath -ChildPath $UserUPN
$ClientId = "YOUR-ENTRA-ID-CLIENT-ID-HERE" # Insert the ID from the pre-game script # Format URL (OneDrive URLs replace dots and @ with underscores)
$UserUrlPart = $UserUPN.Replace(".", "_").Replace("@", "_")
$OneDriveUrl = "https://$Tenant-my.sharepoint.com/personal/$UserUrlPart" # 2. Recursive Download Function
Function Backup-OneDriveFolder {
param (
[Parameter(Mandatory=$true)] $FolderUrl,
[Parameter(Mandatory=$true)] $TargetFolder
)
if (-not (Test-Path $TargetFolder)) {
New-Item -ItemType Directory -Path $TargetFolder -Force | Out-Null
} # Download files
$files = Get-PnPFolderItem -FolderSiteRelativeUrl $FolderUrl -ItemType File
foreach ($file in $files) {
Write-Host " -> Downloading: $($file.Name)" -ForegroundColor Gray
Get-PnPFile -Url $file.ServerRelativeUrl -Path $TargetFolder -FileName $file.Name -AsFile -Force
} # Process subfolders
$subFolders = Get-PnPFolderItem -FolderSiteRelativeUrl $FolderUrl -ItemType Folder
foreach ($subFolder in $subFolders) {
# Ignore hidden SharePoint system folders
if ($subFolder.Name -notin @("Forms", "Hidden", "_t", "_w")) {
$newTargetFolder = Join-Path -Path $TargetFolder -ChildPath $subFolder.Name
Backup-OneDriveFolder -FolderUrl "$FolderUrl/$($subFolder.Name)" -TargetFolder $newTargetFolder
}
}
} # 3. Connect and Execute
Write-Host "Connecting to OneDrive for $UserUPN..." -ForegroundColor Cyan
Connect-PnPOnline -Url $OneDriveUrl -Interactive -ClientId $ClientId # Access Check
$docList = Get-PnPList -Identity "Documents" -ErrorAction SilentlyContinue
if ($null -ne $docList) {
Write-Host "Success! Found $($docList.ItemCount) items on the server." -ForegroundColor Green
Write-Host "Starting backup to $BackupDir ..." -ForegroundColor Yellow
Backup-OneDriveFolder -FolderUrl "Documents" -TargetFolder $BackupDir
Write-Host "Backup completed!" -ForegroundColor Green
} else {
Write-Host "ERROR: Library is empty or you lack Site Collection Admin rights!" -ForegroundColor Red
} Disconnect-PnPOnline
The Nordic Breeze Takeaway
The built-in access check in step 3 is a lifesaver. PnP PowerShell will happily establish a connection to a OneDrive site even if your specific admin account lacks file-level access. Without the check, the script simply sees an "empty" drive and finishes instantly. Always verify your access first before trusting a "completed" backup!
☕ Support the Terminal Garden
If this scribble saved you a few hours of reading Microsoft documentation, consider fueling the next late-night PowerShell session.
BTC: bc1qp06uajaxpu2dxzqhqsvcxesp7uydgy5829nag2
ETH: 0x54644d3af213fed1cc6e2c96d2dcd2189014b6f9
SOL: Gwu9BKKYe97ukrVUYDQ3Mp96Ru3hu9HK82oe4Lg4haBp
Building, breaking, and automating runs on coffee and community. Thank you!