How to Delete All Items from a SharePoint List Using PnP PowerShell

A few months ago, I worked with a client who had a SharePoint Online list named Project Tracker that was being fed by a Power Automate flow. Something went wrong with the flow logic one weekend, and by Monday morning, the list had 48,000 junk items sitting in it.

The client’s ask was simple: “Can you just empty the list? But don’t delete the list itself — we have views, formatting, and a Power App connected to it.”

If you’ve ever tried to delete thousands of items from the SharePoint UI, you already know how painful it is. The browser lets you select only what’s loaded on the page, it times out, and after 15 minutes of clicking, you realize you’ve deleted about 300 items.

That’s when I switched to PnP PowerShell, and I cleaned out the whole list in under 4 minutes.

In this tutorial, I’ll show you every method I use to delete all items from a SharePoint list using PnP PowerShell — from the simple one-liner (good for small lists) to the high-performance batch method that can handle 100,000+ items without hitting the list view threshold.

I’ll also cover CSOM PowerShell as an alternative, how to handle the recycle bin, and the mistakes I’ve made so you don’t have to.

Let’s get into it.

What You Need Before You Start

Before running any of the scripts below, make sure you have these things ready:

  1. PnP PowerShell module installed — This is the modern, cross-platform module. If you haven’t installed it yet, I’ve written a detailed walkthrough on how to install PnP PowerShell.
  2. Permissions — You need at least Contribute permission on the list, but I’d recommend Site Owner or Site Collection Administrator to avoid surprises.
  3. PowerShell 7.x (recommended) or Windows PowerShell 5.1.
  4. Azure AD App registration — If this is your first time using PnP PowerShell after September 2024, you’ll need your own Entra ID app registration since the multi-tenant PnP Management Shell app was retired.

Here’s the quick install command:

Install-Module PnP.PowerShell -Scope CurrentUser -Force

To register your own app for interactive login (one-time setup):

Register-PnPEntraIDAppForInteractiveLogin `
    -ApplicationName "PnP-PowerShell-Admin" `
    -Tenant "contoso.onmicrosoft.com" `
    -Interactive

Copy the Client ID it returns — you’ll need it in every connection string below.

How to Connect to a SharePoint Online Site with PnP PowerShell

Every method in this article starts with a connection. Here’s the standard interactive connection:

$SiteURL = "https://contoso.sharepoint.com/sites/ProjectHub"
$ClientId = "a1b2c3d4-1234-5678-9abc-def012345678"

Connect-PnPOnline -Url $SiteURL -ClientId $ClientId -Interactive

If you’re running this as an unattended job (Azure Automation, scheduled task), use certificate-based authentication instead:

Connect-PnPOnline -Url $SiteURL `
    -ClientId $ClientId `
    -Tenant "contoso.onmicrosoft.com" `
    -CertificatePath "C:\Certs\PnPCert.pfx" `
    -CertificatePassword (ConvertTo-SecureString "YourPassword" -AsPlainText -Force)

⚠️ A quick word of caution: Everything below deletes data. Always test on a dev site first, and consider taking a backup by exporting the list to Excel before you run anything.

Method 1: Delete All Items Using a Simple ForEach Loop (Best for Small Lists)

This is the simplest approach, and it’s what I reach for when a SharePoint list has fewer than about 500 items.

Here you can see the SharePoint list below has more than 100 items.

Delete All Items from a SharePoint List
# Configuration
$SiteURL  = "https://tsinfotechnologies.sharepoint.com/sites/SPGuides/"
$ListName = "Project Tracker"
$ClientId = "a1b2c3d4-1234-5678-9abc-def012345678"

# Connect
Connect-PnPOnline -Url $SiteURL -ClientId $ClientId -Interactive

# Get all items
$ListItems = Get-PnPListItem -List $ListName -PageSize 500

Write-Host "Found $($ListItems.Count) items in '$ListName'" -ForegroundColor Cyan

# Loop and delete
$Counter = 0
ForEach ($Item in $ListItems) {
    Remove-PnPListItem -List $ListName -Identity $Item.Id -Force
    $Counter++
    Write-Progress -Activity "Deleting Items" `
        -Status "Deleted $Counter of $($ListItems.Count)" `
        -PercentComplete (($Counter / $ListItems.Count) * 100)
}

Write-Host "Deleted $Counter items successfully." -ForegroundColor Green
Disconnect-PnPOnline

What’s Happening Here

Part of the scriptWhat it does
Get-PnPListItem -PageSize 500Retrieves items in batches of 500 so you don’t hit the 5,000 list view threshold
-ForceSuppresses the confirmation prompt for each item
Write-ProgressGives you a live progress bar (very handy on long runs)
Disconnect-PnPOnlineCleanly closes the connection

I executed the above PnP PowerShell script using VS Code, and you can see the exact output in the screenshot below:

Delete All Items from a SharePoint List pnp powershell

The Big Downside

This method sends one HTTP request per item. On a list with 10,000 items, that’s 10,000 round trips to SharePoint. In my testing, this averaged around 1.5 to 2 items per second — which means 10,000 items would take roughly 90 minutes.

That’s why I almost never use this for big lists. Which brings me to my favorite method.

Check out Get SharePoint Folder Permissions Using PowerShell

Method 2: Delete All Items Using PnP Batching (Fastest Method — My Recommendation)

This is the method I used for that 48,000-item client list. Batching bundles up to 100 operations into a single HTTP request, which is dramatically faster.

# Configuration
$SiteURL  = "https://contoso.sharepoint.com/sites/ProjectHub"
$ListName = "Project Tracker"
$ClientId = "a1b2c3d4-1234-5678-9abc-def012345678"

Connect-PnPOnline -Url $SiteURL -ClientId $ClientId -Interactive

# Get all items (only the ID field for speed)
$ListItems = Get-PnPListItem -List $ListName -Fields "ID" -PageSize 100
$Total = $ListItems.Count
Write-Host "Found $Total items. Starting batch delete..." -ForegroundColor Cyan

$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

# Create a batch
$Batch = New-PnPBatch

ForEach ($Item in $ListItems) {
    Remove-PnPListItem -List $ListName -Identity $Item.Id -Batch $Batch
}

# Execute the batch
Invoke-PnPBatch -Batch $Batch

$Stopwatch.Stop()
Write-Host "Deleted $Total items in $($Stopwatch.Elapsed.TotalSeconds) seconds." -ForegroundColor Green

You can see the exact output in the screenshot below:

Delete All SharePoint list Items Using PnP Batching

Why This Is So Much Faster

Instead of 10,000 individual calls, PnP automatically chunks your batch into groups of 100 and sends 100 requests total. In my real-world test on that 48,000-item list, this finished in about 3 minutes 40 seconds.

Here’s a rough comparison from my own testing:

Item CountForEach LoopBatch Method
500~4 minutes~8 seconds
5,000~45 minutes~50 seconds
25,000~3.5 hours~2 minutes
100,000Don’t even try~9 minutes

💡 Pro tip: Notice I used -Fields "ID" in the Get-PnPListItem call. By default, PnP pulls back every field for every item, which is a massive waste of bandwidth when all you need is the ID. This one change alone cut my retrieval time by more than half.

Read PowerShell Find All Files With Extension

Method 3: Delete Items in Chunks with Progress and Error Handling (Production-Ready)

For very large lists, throwing 100,000 delete operations into a single batch object can eat up a lot of memory. Here’s the production script I keep in my toolkit—it processes items in chunks, handles throttling, and logs everything.

<#
.SYNOPSIS
    Deletes all items from a SharePoint Online list using PnP PowerShell batching.
#>

# ---------- Configuration ----------
$SiteURL   = "https://contoso.sharepoint.com/sites/ProjectHub"
$ListName  = "Project Tracker"
$ClientId  = "a1b2c3d4-1234-5678-9abc-def012345678"
$ChunkSize = 1000
$LogFile   = "C:\Logs\ListCleanup_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
# -----------------------------------

Function Write-Log {
    param([string]$Message, [string]$Level = "INFO")
    $Entry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') [$Level] $Message"
    Write-Host $Entry
    Add-Content -Path $LogFile -Value $Entry
}

Try {
    Connect-PnPOnline -Url $SiteURL -ClientId $ClientId -Interactive -ErrorAction Stop
    Write-Log "Connected to $SiteURL"

    # Verify the list exists
    $List = Get-PnPList -Identity $ListName -ErrorAction Stop
    Write-Log "List '$ListName' found with $($List.ItemCount) items."

    If ($List.ItemCount -eq 0) {
        Write-Log "List is already empty. Nothing to do."
        Return
    }

    # Confirm before proceeding
    $Confirm = Read-Host "Delete ALL $($List.ItemCount) items from '$ListName'? Type YES to continue"
    If ($Confirm -ne "YES") {
        Write-Log "Operation cancelled by user." "WARN"
        Return
    }

    $Deleted = 0
    Do {
        $Items = Get-PnPListItem -List $ListName -Fields "ID" -PageSize $ChunkSize |
                 Select-Object -First $ChunkSize

        If ($Items.Count -eq 0) { Break }

        $Batch = New-PnPBatch
        ForEach ($Item in $Items) {
            Remove-PnPListItem -List $ListName -Identity $Item.Id -Batch $Batch
        }
        Invoke-PnPBatch -Batch $Batch

        $Deleted += $Items.Count
        Write-Log "Deleted $Deleted of $($List.ItemCount) items."

        Start-Sleep -Milliseconds 500   # Gentle pause to avoid throttling
    }
    While ($Items.Count -eq $ChunkSize)

    Write-Log "COMPLETE. Total items deleted: $Deleted" 
}
Catch {
    Write-Log "ERROR: $($_.Exception.Message)" "ERROR"
}
Finally {
    Disconnect-PnPOnline -ErrorAction SilentlyContinue
    Write-Log "Disconnected."
}

Why I Like This Version

  • It asks for confirmation. I once ran a cleanup script against the wrong site URL. Never again.
  • It logs everything to a timestamped file, so you have an audit trail.
  • It processes in chunks, keeping memory usage flat regardless of list size.
  • It pauses briefly between chunks, which helps avoid HTTP 429 throttling responses from SharePoint.

If you want to build more robust scripts like this, my guide on PowerShell try catch covers error handling patterns in depth.

Method 4: Delete Items Using a CAML Query (Filter Before Deleting)

Sometimes you don’t want to delete everything — you want to delete everything that matches a condition. For example, my client eventually wanted to purge only items older than 90 days.

$SiteURL  = "https://contoso.sharepoint.com/sites/ProjectHub"
$ListName = "Project Tracker"
$ClientId = "a1b2c3d4-1234-5678-9abc-def012345678"

Connect-PnPOnline -Url $SiteURL -ClientId $ClientId -Interactive

$CutoffDate = (Get-Date).AddDays(-90).ToString("yyyy-MM-ddTHH:mm:ssZ")

$Query = @"
<View Scope='RecursiveAll'>
  <Query>
    <Where>
      <Lt>
        <FieldRef Name='Created' />
        <Value Type='DateTime' IncludeTimeValue='TRUE'>$CutoffDate</Value>
      </Lt>
    </Where>
  </Query>
  <ViewFields><FieldRef Name='ID' /></ViewFields>
  <RowLimit Paged='TRUE'>2000</RowLimit>
</View>
"@

$Items = Get-PnPListItem -List $ListName -Query $Query
Write-Host "Found $($Items.Count) items older than 90 days."

$Batch = New-PnPBatch
ForEach ($Item in $Items) {
    Remove-PnPListItem -List $ListName -Identity $Item.Id -Batch $Batch
}
Invoke-PnPBatch -Batch $Batch

Write-Host "Cleanup complete." -ForegroundColor Green

Other Useful CAML Filters

Delete items where Status equals “Rejected”:

<Where>
  <Eq>
    <FieldRef Name='Status' />
    <Value Type='Text'>Rejected</Value>
  </Eq>
</Where>

Delete items created by a specific user:

<Where>
  <Eq>
    <FieldRef Name='Author' LookupId='TRUE' />
    <Value Type='Integer'>15</Value>
  </Eq>
</Where>

If you’re not comfortable hand-writing CAML, take a look at my post on the CAML Query Builder — it makes constructing these queries much less painful.

Method 5: Delete All Items Using the CSOM Batch Approach (Ultra-Fast for Massive Lists)

For truly enormous lists — think 500,000+ items — there’s a lower-level trick using raw CSOM inside PnP PowerShell. This is the fastest method available, though it’s also the most advanced.

$SiteURL  = "https://contoso.sharepoint.com/sites/ProjectHub"
$ListName = "Project Tracker"
$ClientId = "a1b2c3d4-1234-5678-9abc-def012345678"
$BatchSize = 100

Connect-PnPOnline -Url $SiteURL -ClientId $ClientId -Interactive

$Ctx  = Get-PnPContext
$List = Get-PnPList -Identity $ListName
$Ctx.Load($List)
$Ctx.ExecuteQuery()

Write-Host "Starting deletion of $($List.ItemCount) items..." -ForegroundColor Cyan

$Query = New-Object Microsoft.SharePoint.Client.CamlQuery
$Query.ViewXml = "<View><Query></Query><RowLimit>$BatchSize</RowLimit></View>"

$TotalDeleted = 0

Do {
    $Items = $List.GetItems($Query)
    $Ctx.Load($Items)
    $Ctx.ExecuteQuery()

    $Count = $Items.Count
    If ($Count -eq 0) { Break }

    # Delete in reverse order to avoid index shifting
    For ($i = $Count - 1; $i -ge 0; $i--) {
        $Items[$i].DeleteObject()
    }
    $Ctx.ExecuteQuery()

    $TotalDeleted += $Count
    Write-Host "Deleted $TotalDeleted items so far..." -ForegroundColor Yellow
}
While ($true)

Write-Host "Finished. Total deleted: $TotalDeleted" -ForegroundColor Green

Notice the reverse loop (For ($i = $Count - 1; $i -ge 0; $i--)). This is essential. If you delete items forward through the collection, the index shifts after each deletion and you’ll skip items or throw index-out-of-range errors. This tripped me up the first time I wrote a CSOM deletion script.

Frequently Asked Questions

Will deleting and recreating the SharePoint list break my Power Automate flows?

Yes, every time. Flows bind to the list’s internal GUID, not its display name or URL, so even recreating the list at the identical URL will not reconnect them. You must open each flow and re-select the list in every SharePoint action. Test each flow manually before you consider the migration complete.

Can I keep the original item IDs?

Not through normal means. SharePoint assigns IDs sequentially, and you can’t set them explicitly through the UI, PowerShell, or the REST API. The standard workaround is a LegacyID number column populated during import, which you then reference anywhere the old ID mattered.

Does the Recycle Bin restore a corrupted list in a working state?

No. Restoring from the Recycle Bin brings back the list exactly as it was, corruption included. The Recycle Bin is your rollback safety net, not a repair tool.

How long do I have before the deleted SharePoint list is gone for good?

The clock starts the moment you delete the list. It sits in the first-stage (site) Recycle Bin for up to 30 days, then moves to the second-stage (site collection) Recycle Bin for the remainder — but the combined window is capped at 93 days from the original deletion. Moving between stages does not reset the timer. After day 93, the list is permanently purged. At that point your only option is a Microsoft support ticket requesting a site collection restore — and there’s no guarantee they can help, since it’s typically only viable within a short additional window and usually restores the entire site collection to a prior state, not a single list.

Conclusion

You can delete all items from a SharePoint list using PnP PowerShell in several ways, depending on the list size and your performance requirements. For small lists, a simple ForEach loop is easy to understand and use. For larger lists, PnP batching is usually the best choice because it significantly improves deletion speed.

If you need more control, such as progress tracking, error handling, filtering, or handling massive lists, the chunked, CAML query, and CSOM batch approaches provide more flexibility.

Choose the method that best fits your list size and requirements, and always have a backup or recovery plan before performing bulk deletions in SharePoint.

You may also like the following tutorials:

Leave a Comment

⏰ LIMITED-TIME OFFER

Join the SharePoint & Power Platform Developer Live Training

📅 Live training starts October 5, 2026
SharePoint Development
Power Apps & Power Automate
Copilot Studio
🎁
FREE 1-Year Access to SPGuides.Academy

Enroll now and get access to all 9 academy courses at no extra cost.

Secure your seat before the batch fills up.
Get the live training plus the complete SPGuides.Academy learning library.

Enroll Now & Get Your FREE 1-Year Academy Access → View live training details and schedule
Power Apps functions free pdf

30 Power Apps Functions

This free guide walks you through the 30 most-used Power Apps functions with real business examples, exact syntax, and results you can see.

Live Webinar

SharePoint Integration Power Apps Form With Repeating Table [Invoice Management System]

Learn how to build an invoice management system using SharePoint integration and a repeating table.

📅 2nd September 2026 – 10:00 AM EST | 7:30 PM IST

Download User registration canvas app

DOWNLOAD USER REGISTRATION POWER APPS CANVAS APP

Download a fully functional Power Apps Canvas App (with Power Automate): User Registration App