SharePoint’s built-in Copy Files feature moves or duplicates files between libraries — same site or across sites — without needing a third-party tool. It’s the right call for routine content distribution; whether it’s the right call for a bulk migration is a different question, covered below alongside the automated alternatives.
In this post: What affects copy time · Best practices · Approaches to copying files · Actually copying a whole library · Checking whether the job actually succeeded · When would you actually use this? · Related reading
What affects copy time
- File size and count — a single small file is near-instant; hundreds or thousands of files run into SharePoint’s throttling and processing limits.
- Same site vs. cross-site — copying within a site collection is faster than copying across site collections or tenants, since SharePoint can’t optimize the transfer the same way.
- Version history — if versioning is enabled and you’re retaining it, SharePoint copies every version, not just the current one, which adds real time on files with a long history.
Best practices
- Batch large jobs. Copying thousands of files in one operation is more likely to hit a throttling limit than the same files split into smaller batches.
- Prefer same-site-collection copies where the destination allows it — cross-site copies are slower and have more failure modes.
- Confirm permissions on both ends first. The copy will fail partway through if the account doesn’t have write access at the destination, which is a worse failure mode than catching it up front.
- Don’t assume metadata survives. Fields like Created By and Modified By often don’t carry over with a straight copy — if that matters, handle it explicitly via Power Automate or PowerShell rather than assuming.
- Avoid peak hours for large transfers if performance is inconsistent — SharePoint Online’s shared infrastructure means busy periods can slow large jobs down.
Approaches to copying files
SharePoint UI — select the files, click Copy to, pick a destination. Fastest for a handful of files, but no bulk filtering and limited metadata retention.
Power Automate — trigger on When a file is created or modified, action Copy file, specify source and destination libraries. Good for ongoing automation (new files land in one library, automatically mirrored to another) rather than a one-time bulk job, where it can run into execution limits at high volume.
PowerShell (PnP) — the option for bulk operations and migrations:
# Install PnP PowerShell Module if not installed
Install-Module PnP.PowerShell -Force
# Connect to SharePoint
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/yoursite" -Interactive
# Copy File
Copy-PnPFile -SourceUrl "/sites/yoursite/Shared Documents/sample.docx" `
-TargetUrl "/sites/targetsite/Shared Documents" -Overwrite
Two details that trip people up: the parameter is -TargetUrl, not -DestinationUrl (that parameter doesn’t exist). And when the target is a different site, -TargetUrl needs to be a folder, not a full file path with a filename attached — SharePoint won’t let you rename the file mid-copy across sites, only within the same one.
Third-party tools (ShareGate, Metalogix, AvePoint) — worth it for large-scale migrations where full metadata and permission retention matters more than avoiding a license cost. Overkill for routine day-to-day copying.
Actually copying a whole library
The single-file example above is the whole mechanic, but “bulk operations and migrations” needs it wrapped in a loop over the source library’s items, plus the -NoWait parameter — running every copy synchronously and waiting on each one before starting the next is what actually causes the throttling problems the Best Practices section above warns about:
$items = Get-PnPListItem -List "Documents" -PageSize 500
foreach ($item in $items) {
$sourceUrl = $item.FieldValues.FileRef
if (-not $sourceUrl) { continue }
Copy-PnPFile -SourceUrl $sourceUrl -TargetUrl "/sites/targetsite/Shared Documents" `
-Overwrite -NoWait
}
# Check on progress separately once jobs have had time to process
Receive-PnPCopyMoveJobStatus -TargetSiteUrl "https://yourtenant.sharepoint.com/sites/targetsite"
-NoWait queues the copy as a background job and returns immediately instead of blocking the script until each file finishes — Receive-PnPCopyMoveJobStatus is the separate call that reports how those queued jobs are actually progressing. If version history genuinely doesn’t need to survive the copy (a one-time content seed rather than a true migration), -IgnoreVersionHistory skips copying every prior version and is a real speed difference on files with a long history, directly addressing the version-history time cost flagged earlier in this post.
Checking whether the job actually succeeded
Calling Receive-PnPCopyMoveJobStatus right after queuing the loop above, as shown, mostly just confirms the job exists — the real answer to “did it work” is in the object it returns, not in the fact that the call didn’t error. It reports progress, not a live stream of updates, so it needs to actually be inspected rather than treated as a fire-and-forget status check:
$jobStatus = Receive-PnPCopyMoveJobStatus -TargetSiteUrl "https://yourtenant.sharepoint.com/sites/targetsite"
if ($jobStatus.JobState -eq 0) {
Write-Host "Copy job completed successfully"
} elseif ($jobStatus.JobError) {
Write-Host "Copy job failed: $($jobStatus.JobError)"
} else {
Write-Host "Copy job still in progress -- check again shortly"
}
JobState -eq 0 is what actually means “finished” — any other value means it’s either still running or ended in an error, and JobError is where that error detail shows up if it did. Worth building this check into anything scripted rather than assuming the loop’s completion means every file landed — -NoWait means the loop finishes as soon as every job is queued, well before any of them are necessarily done.
The UI copy is fine for a handful of files. Past that, PowerShell or Power Automate — not because the UI breaks, but because neither retries nor batches for you.
When would you actually use this?
- A handful of files need to move between libraries once — the UI, no script needed.
- New files should automatically mirror into a second library going forward — Power Automate.
- You’re migrating a whole library or doing a bulk one-time copy with hundreds of files — PnP PowerShell, or a third-party tool if metadata fidelity is critical.
Related reading
- How to Copy Files from One Site to Another Using Power Automate — a deeper look at the Power Automate approach summarized here.
- How to Migrate SharePoint Online Content to Another Tenant — when the copy needs to cross tenant boundaries, not just sites.
That’s the rundown on copying files. Comment below if something doesn’t match your setup.
App Catalog Authentication Automation Backup Compliance Content Type CSS Flows Google Javascript Limitations List Metadata MFA Microsoft Node NodeJs O365 OneDrive Permissions PnP PnPJS Policy PowerApps Power Automate PowerAutomate PowerPlatform PowerShell React ReactJs Rest API Rest Endpoint Security Send an HTTP Request to SharePoint SharePoint SharePoint List SharePoint Modern SharePoint Online SPFX SPO Sync Tags Teams Termstore Versioning


