Skip Ribbon Commands
Skip to main content

Quick Launch

Todd Klindt's home page > Todd Klindt's Office 365 Admin Blog > Posts > Bulk Undelete Files in Office 365 and SharePoint Online with PnP PowerShell
December 11
Bulk Undelete Files in Office 365 and SharePoint Online with PnP PowerShell

Quite often as a consultant, you get to work on truly fun and funny situations. The customer email that prompted this blog post is one of them.

My contact at the customer site emailed with a problem. A few weeks earlier one of their users deleted some pictures from one of their document libraries. Okay, not a few pictures, nearly 100,000 of them. And not only had that user deleted nearly 100,000 items, they hadn’t told anyone for 3 weeks, while they tried to upload the pictures they still had locally. I know what you’re saying, this is a job for the Recycle Bin! And you’re right, it is, but the other factors made it complicated. They couldn’t just restore all of the files in the Recycle Bin as there were also 3 weeks’ worth of legitimately deleted documents in there, including documents from that user. Also, since the user had re-uploaded a bunch of the deleted documents there would be “overwrite” prompts all over that would slow down the process. And let’s not forget that even if it all went smoothly, someone would have to manually restore nearly 100,000 files. No easy feat.

The customer talked to me because they wanted to share the story (it was a big funny after the fact) but also because they knew I always said that PowerShell could do anything. They were hoping PowerShell could bale them out of this mess too. I don’t mean to spoil the end of this story, but they were right, it could.

My tool of choice when it comes to making magic in Office 365 is the PnP PowerShell. I had never done this before, so I had to do a bit of spelunking. I ran Get-Command *recycle* -Module SharePointPnPPowerShellOnline to see what PowerShell cmdlets availed themselves to me.  Looky, looky, there it is, Restore-PnPRecycleBinItem. Did you just hear angels sing? I know I did.

Now we needed to weave a little PowerShell magic. We couldn’t restore all of the deleted files, since other folks had legitimately deleted files in the meantime. We also only wanted to restore .JPG files that this user had deleted. Here’s command that got the files we needed:

Get-PnPRecycleBinItem | Where-Object -Property Leafname -Like -Value "*.jpg"  | Where-Object -Property Dirname -Like -Value "Shared Documents/*"  | Where-Object -Property DeletedByEmail -EQ –Value

That returned all the files, now what to do with them? Restore them, of course. This bit of code grabs all the files, counts them as it restores them, then spits out the time and date when it’s done.

$bin = Get-PnPRecycleBinItem | Where-Object -Property Leafname -Like -Value "*.jpg"  | Where-Object -Property Dirname -Like -Value "Shared Documents/*"  | Where-Object -Property DeletedByEmail -EQ –Value

$bin | foreach  -begin { $a = 0} -Process  {Write-Host "$a - $($_.LeafName)" ; $_ | Restore-PnPRecycleBinItem -Force ; $a++ } -End { Get-Date }

Normally, that would have worked, but it was going to take some time to restore the files. A loooong time. Several days in fact. With some testing I surmised that the client PowerShell was the bottleneck. I used a little PowerShell trick to break the entire 100,000 item collection into chunks of 10,000 and run it multiple PowerShell windows and on multiple machines. I changed the second line to look something like this:

($bin[20001..30000]) | foreach  -begin { $a = 0} -Process  {Write-Host "$a - $($_.LeafName)" ; $_ | Restore-PnPRecycleBinItem -Force ; $a++ } -End { Get-Date }

The $bin[20001..30000] only sends items 20001 to 30000 of the collection down the pipeline. I changed that on each client. $bin[0..10000], $bin[10001..20000], and so on. After they finished I ran through it all one more time to make sure I didn’t miss any. The entire script looked like this:

# Make sure necessary modules are installed
# PnP PowerShell to get access to Office 365
Install-Module SharePointPnPPowerShellOnline

# Module to securely store passwords
Install-Module CredentialManager

# Saved credentials
New-StoredCredential -Target "ImportantSite" -UserName -Password 'Password goes here' -Persist LocalMachine

# Now the actual meat
# Connect to the site collection where the files were deleted
Connect-PnPOnline -Url -Credentials ImportantSite

# Filter the recycle bin for only the files we want to restore, JPGs, from the Shared document library, deleted by Shane
$bin = Get-PnPRecycleBinItem | Where-Object -Property Leafname -Like -Value "*.jpg"  | Where-Object -Property Dirname -Like -Value "Shared Documents/*"  | Where-Object -Property DeletedByEmail -EQ -Value

# See how many pictures Shane deleted.

# Walk through the collection and restore each document. Spit out the time at the end so you know how long it took. Also keep a counter to see how it’s going as it churns through the collection
$bin | foreach  -begin { $a = 0} -Process  {Write-Host "$a - $($_.LeafName)" ; $_ | Restore-PnPRecycleBinItem -Force ; $a++ } -End { Get-Date }

# Since the OM is the bottleneck you can run this on multiple machines to speed things up. Here’s how to only restore a subset of the collection
($bin[20001..30000]) | foreach  -begin { $a = 0} -Process  {Write-Host "$a - $($_.LeafName)" ; $_ | Restore-PnPRecycleBinItem -Force ; $a++ } -End { Get-Date }

I hope you never need to use this. Smile




Todd - always a wonder. Thank you!

I'm a big PnP PS fan as well. Had no idea about this hidden gem. Even the new-ish file restore wouldn't have worked here. Thank you!!!
 on 12/11/2018 6:50 PM

Re: Todd - always a wonder. Thank you!

Thanks, Brian. I'm glad you liked it.
Todd O. KlindtNo presence information on 12/12/2018 4:30 PM

Thanks Again!

Todd - thanks again for rescuing my client from this big boo-boo.

It was way over my pay-grade, and although you said the code you wrote was rather simple, it sure doesn't look like it!
 on 12/27/2018 1:37 PM

Re: Thanks Again!

I have no idea what you're talking about, but you're welcome. :)

Todd O. KlindtNo presence information on 1/2/2019 3:01 PM


I hope I never have to use this but i've added it to my knowledge base list in Sharepoint.


 on 1/8/2019 12:53 PM

but, what about the LVT?


When I execute a line like :
Get-PnPRecycleBinItem | Where-Object -Property Leafname -Like -Value "*.xml"

I get the dreaded 5,000 item list view threshold error. How are you able to get around this with your 100,000 item scenario? I've been stuck all day on this... any help is appreciated.
 on 1/23/2019 4:39 PM

Add Comment

Items on this list require content approval. Your submission will not appear in public views until approved by someone with proper rights. More information on content approval.


Body *

Today's date *

Select a date from the calendar.
Please enter today's date so I know you are a real person


Want a message when I reply to your comment? Put your Twitter handle here.