SharePoint online restoring large quantity of deleted files

Have you ever faced with a task where you are asked to restore large quantity of deleted files from SharePoint online? If not, trust me, it is going to happen sooner or later.

When files are deleted from SharePoint they are moved to first stage recycle bin and they are kept there for 93 days. After that period they are moved to second stage recycle bin for 93 days more before completely be deleted from SharePoint. In read cases you can still open case with Microsoft and ask to restore files deleted even from second stage recycle bin.

Restoring large quantity of items will take time so let me share what how can you can speed up the process.

Restoring files with GUI is ok if you don’t have lot of files to restore, here is the tip for faster restore:

Could only restore up to ten items at a time through this link: https://(customerurl)

Could restore 200 at a time through this link: https://(customerurl)

Because GUI restore is not helpful for our restore we are going to use PowerShell for this task. Before we start we need to install SharePoint Online Management Shell and PnP PowerShell with this command

Install-Module -Name Microsoft.Online.SharePoint.PowerShell

Install-Module SharePointPnPPowerShellOnline

Once installed we need to connect to site where the deleted files are with

Connect-PnPOnline -Url https://(customerurl)

We can now count the files in first stage, second stage and total count for both stage recycle bins

(Get-PnPRecycleBinItem -FirstStage).count
(Get-PnPRecycleBinItem -SecondStage).count

Please note: This count will return all files deleted in the last 93 days for first state recycle bin and 93+93 days for the second recycle bin.

We can also easily export files in recycle bin in csv format to see visually with what we are dealing with using

Get-PnPRecycleBinItem | Export-Csv c:\temp\recycle-bin.csv

Usually we need to do some filtering. Most of the times filtering will be done based on dates when files were deleted and/or by user how deleted the files. We can do both filtering by dates (from today going back 4 days) and user and run new export to see what we are restoring using

$today = (Get-Date) 
$date1 = $
$date2 = $

Get-PnPRecycleBinItem | Where-Object { ($_.DeletedDate -gt $date2 -and $_.DeletedDate -lt $date1) -and ($_.DeletedByEmail -eq '[email protected]') } | Export-Csv c:\temp\deleted-by-user.csv

If we are OK with the last export and we want to restore the files we can use Restore-PnpRecycleBinItem -Force command.

Get-PnPRecycleBinItem | Where-Object { ($_.DeletedDate -gt $date2 -and $_.DeletedDate -lt $date1) -and ($_.DeletedByEmail -eq '[email protected]') } | Restore-PnpRecycleBinItem -Force

This will work but there are 2 things that are not in our favour here:

  1. This way of export is still slow, running this we got export speed of around 350 files per hour.
  2. Files are not exported if full path is not present/restored.

So we needed to tweak a bit our recycle bin files and run the export on multiple sessions to make it faster. First, we are going to sort files we want to restore so we can restore folders first, and then we go with files. This way we will have full directory structure and there will be no error like path not found, etc. To do this we use

$items = Get-PnPRecycleBinItem | Where-Object { ($_.DeletedDate -gt $date2 -and $_.DeletedDate -lt $date1) -and ($_.DeletedByEmail -eq '[email protected]') }
$items = $items | Sort-Object -Property @{expression = ’ItemType’; descending = $true }, @{expression = “DirName”; descending = $false } , @{expression = “LeafName”; descending = $false }

We got all files we want to restore filtered by date and user and placed them in the variable $items, then we sort this variable $items and last we count the numbers of files we have to restore.

As this number was big and we know that PowerShell is also a bottleneck we can open multiple PowerShell sessions and run parts of the restore in separate session. This will look like this

($items[x..y]) | ForEach-Object  -begin { $a = 0 } -Process { Write-Host "$a - $($_.LeafName)" ; $_ | Restore-PnPRecycleBinItem -Force ; $a++ }

where x and y are numbers from our items array. So for example, in one PowerShell session we will run from x=0 to y=20000 in the other PowerShell session we will run for x=20001 and y=40000 and so on. This way we are increasing the speed by x2, x3 …

To summarize, once we install the PowerShell modules in the PC/Server this is what we need to run in one PowerShell to restore files

Connect-PnPOnline -Url https://(customerurl)
$today = (Get-Date) 
$date1 = $
$date2 = $
$items = Get-PnPRecycleBinItem | Where-Object { ($_.DeletedDate -gt $date2 -and $_.DeletedDate -lt $date1) -and ($_.DeletedByEmail -eq '[email protected]') }
$items = $items | Sort-Object -Property @{expression = ’ItemType’; descending = $true }, @{expression = “DirName”; descending = $false } , @{expression = “LeafName”; descending = $false }

($items[0..10000]) | ForEach-Object  -begin { $a = 0 } -Process { Write-Host "$a - $($_.LeafName)" ; $_ | Restore-PnPRecycleBinItem -Force ; $a++ }
  1. connect to SharePoint site
  2. get today date
  3. set $date1 to the closes date you want to restore
  4. set $date2 to the distant date you want to restore
  5. filter the results by date and email and place them in $items variable
  6. soft $items variable so directories/folders are on the top so you could restore them first and then do the files restore
  7. count the number of items so you know how many you are restoring
  8. if you want to split items in multiple sessions, open PowerShell and do all above before running command 9 in any windows so that your $items variable has the same content in all PowerShell sessions before restore starts, then run command 9
  9. run the restore, change the numbers in red ($items[0..10000]) based on your count result in line 7, or if you want to split and run this in parallel in multiple PowerShell sessions.