GitHub Icon Image
GitHub

Restore large amount of items from SharePoint Recycle bin in bulk

Summary

Restores items from the recycle bin based on its unique ID, a GUID.
Attempts in batches of "x" items (default 10), if a failure occurs, not all items are restored, therefore will attempt to restore the items in the batch individually before grabbing the next batch. The results of the restore can be saved to a CSV file.

Script allows to restore in batches of 100 if you wish, however, if failures are found it could take a longer overall, as script falls back to restoring each item individually to ensure all are restored and report the error item(s).

Prerequisites

  • Obtained details of the items to restore from the sites recycle bin in a csv file.
        ## PnP PowerShell
        Connect-PnPOnline -url:https://contso.sharepoint.com/sites/RestoreDocs -pnpManagementShell
        $recycleBinItems = Get-PnpRecycleBinItem -FirstStage -RowLimit 999999
        $recycleBinItems | Export-Csv .\recyclebin.csv -NoTypeInformation
    
  • Open csv and remove rows you do not wish to restore. Save the csv file.

Screenshots

Screen Output

Screen Output

CSV Output

CSV Output

  • PnP PowerShell
# Input file
$Path = "$PSScriptRoot\recyclebin.csv"
# Output file
$OutputFile = "$PSScriptRoot\recyclebinresults.csv"

$NoInBatch = 10

$ErrorActionPreference = 'Stop'
$InformationPreference = 'Continue'
Connect-PnPOnline -url:"https://contso.sharepoint.com/sites/RestoreDocs" -PnPManagementShell

function Start-Processing {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $csvFilePath,
        [Parameter(Mandatory = $true)]
        [int]
        $processBatchCount
    )

    $csvItems = Get-Content -path:$csvFilePath | ConvertFrom-csv
    $recycleBinSplit = Split-Array -InputObject $csvItems -Size $processBatchCount

    $batchCount = $recycleBinSplit.Count
    $i = 0
    if($recycleBinSplit.Count -eq $csvItems.Count)
    {
        Write-Information -MessageData:"Restoring deleted items batch 1 of 1 containing $($recycleBinSplit.Count) items..."
        Restore-RecycleBinItems -Ids:$recycleBinSplit
    }
    else {
        $recycleBinSplit | ForEach-Object {
            $items = $PSItem
            $i++;
            Write-Information -MessageData:"Restoring deleted items batch $i of $batchCount containing $($items.Count)..."
            Restore-RecycleBinItems -Ids:$items
        }
    }
}

function Split-Array {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [object[]] $InputObject,
        [int] $Size = 10
    )
    $outArray = @()
    $parts = [math]::Ceiling($InputObject.Count / $Size)

    for ($i = 0; $i -le $parts - 1; $i++) {
        $start = $i * $Size
        $end = (($i + 1) * $Size) - 1
        $outArray += , @($InputObject[$start..$end])
    }

    Write-Output $outArray
}

function Restore-RecycleBinItems {
    param(
        [Parameter(Mandatory)]
        [Object[]]
        $Ids
    )


    $apiCall = "/_api/site/RecycleBin/RestoreByIds"
    $idsString = ($Ids).Id -join "','"
    $body = "{'ids':['$idsString']}"

    try {
        Invoke-PnPSPRestMethod -Method Post -Url $apiCall -Content $body | Out-Null
        Write-Information "Batch Success"
        $Ids | ForEach-Object {
            $id = $PSItem
            $id | Add-Member -MemberType NoteProperty -Name "Status" -Value "Success"
            Write-Output $id
        }
    }
    catch {
        $Exception = $_
        Write-Warning "Unable to process as batch, processing individually...."
        $Ids | ForEach-Object {
            $id = $PSItem
            try {
                $body = "{'ids':['$($id.Id)']}"
                Invoke-PnPSPRestMethod -Method Post -Url $apiCall -Content $body | Out-Null
                Write-Information "Success: $($id.Id)"
                $id | Add-Member -MemberType NoteProperty -Name "Status" -Value "Success"
                Write-Output $id
            }
            catch {
                $Exception = $_
                $odataError = $Exception.Exception.Message | ConvertFrom-Json
                $message = $odataError.'odata.error'.message.value
                if ($message.Contains("Value does not fall within the expected range.") -eq $true) {
                    $message = "No longer in recycle bin / Previously restored"
                }

                $id | Add-Member -MemberType NoteProperty -Name "Status" -Value $message
                Write-Information "Failed: $($id.Id) - $message"
                Write-Output $id
            }
        }
    }
}

Write-Information -MessageData:"Processing file $Path and restoring recycle bin items in batches of $NoInBatch..."
Start-Processing -csvFilePath:$Path -processBatchCount:$NoInBatch | Export-Csv $OutputFile -NoTypeInformation

Check out the PnP PowerShell to learn more at: https://aka.ms/pnp/powershell

The way you login into PnP PowerShell has changed please read PnP Management Shell EntraID app is deleted : what should I do ?

Contributors

Author(s)
Paul Matthews

Disclaimer

THESE SAMPLES ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

Back to top Script Samples
Generated by DocFX with Material UI