A client’s operations team recently needed 300 task rows loaded into a SharePoint list, half of them updated a week later, and the finished ones cleaned out before quarter close. By hand, that’s an afternoon. With PnP PowerShell, it’s seconds.
Here’s how I create, read, update and delete SharePoint Online list items with PnP PowerShell. Every command below ran against a demo task list on my own Microsoft 365 tenant in PowerShell 7, and the screenshots show the real output.
Quick answer: Connect with Connect-PnPOnline, then use Add-PnPListItem -List "Tasks" -Values @{Title="New task"} to create, Get-PnPListItem -List "Tasks" -PageSize 500 to read, Set-PnPListItem -List "Tasks" -Identity 8 -Values @{TaskStatus="Completed"} to update, and Remove-PnPListItem -List "Tasks" -Identity 8 -Recycle -Force to delete. For many items, pass -Batch from New-PnPBatch and run Invoke-PnPBatch.
Before you start: connection and internal column names
You need PnP PowerShell 3.x in PowerShell 7.4 or later, plus your own Entra ID app registration for -ClientId. If that’s new to you, start with installing PnP PowerShell for SharePoint Online, then connect to the site that holds your list.
Also, -Values takes internal column names. In my demo list “Contoso Demo Tasks”, the Status column is TaskStatus and Due Date is DueDate. Run Get-PnPField -List "Contoso Demo Tasks" to look them up, or see how to get SharePoint list column details with PnP PowerShell.
Create list items with Add-PnPListItem
Add-PnPListItem takes the list name and a hashtable of column values, and returns the new item, so you can grab its ID right away. Below I add one task, then six more in a single batch:

Value formats that save trial and error:
- Numbers and currency: pass them as strings, like
"2500". Without a batch they use US notation; inside a batch they follow the site’s regional settings. - Dates: a string such as
"11/16/2026"or"11/16/2026 14:30". - Person or group: the user’s email (or the site user ID). Lookup: the ID of the item in the source list.
- Yes/No:
$trueor$false. Multi-choice: an array. Managed metadata: a term path like"CORPORATE|FINANCE"or the term GUID.
# Person column: email; lookup column: the ID of the item in the other list
Add-PnPListItem -List "Projects" -Values @{
Title = "Quarterly security review"
AssignedTo = "megan@contoso.com"
Department = 3
Tags = "Finance", "Audit" # multi-choice column: an array
}
Use -Folder "2026/Q4" to drop the item into a list folder (PnP creates it if it’s missing), and -ContentType when the list has more than one. If your list doesn’t exist yet, create the SharePoint list and its columns with PnP PowerShell first.
Read list items with Get-PnPListItem
Get-PnPListItem with no ID returns every item. I add -PageSize so items come back in pages, and -Fields so SharePoint only sends the columns I need. Values live in the item’s indexer, so $_["TaskStatus"] reads the Status column:

SharePoint returns dates in UTC, hence .ToLocalTime(). For a single item, use Get-PnPListItem -List $list -Id 8, which returns all of its fields.
For server-side filtering, pass a CAML query with -Query. You can’t combine it with -Fields; put a <ViewFields> element inside the CAML instead. One more limit: if you ask for more than 12 lookup-type columns (lookup, person, managed metadata, Created By, Modified By) at once, you’ll hit the lookup column threshold error.
Update list items with Set-PnPListItem
Set-PnPListItem finds the item by -Identity (its ID) and changes only the columns you pass. The -UpdateType parameter decides what happens to the version history, and this run shows the difference on a list with versioning on:

| UpdateType | New version? | Modified / Modified By | Triggers Power Automate? |
|---|---|---|---|
| Update (default) | Yes | Updated | Yes |
| SystemUpdate | No, changes go into the last version | Not updated | No |
| UpdateOverwriteVersion | No, overwrites the last version | Modified date updated | Yes |
I use SystemUpdate for data fixes so the Modified column and any Power Automate flows aren’t disturbed. Add -Force when you want to fire flows or webhooks even though no value changed.
A common request is “update the row if it exists, otherwise add it.” Add-PnPListItem always creates a new item, so look it up first:
$title = "Update vendor contracts"
$match = Get-PnPListItem -List $list -PageSize 500 | Where-Object { $_["Title"] -eq $title }
if ($match) {
Set-PnPListItem -List $list -Identity $match[0].Id -Values @{ TaskStatus = "Completed" } | Out-Null
} else {
Add-PnPListItem -List $list -Values @{ Title = $title; TaskStatus = "Not Started" } | Out-Null
}
Delete list items with Remove-PnPListItem
Remove-PnPListItem asks for confirmation unless you add -Force. Here’s the part older scripts get wrong: without -Recycle, the item is permanently deleted, with no way back from the recycle bin. I use -Recycle unless I’m sure:

-Recycle also works inside a batch, as shown above. To empty a whole list, see removing all items from a SharePoint list.
Work with hundreds or thousands of items
All three write cmdlets (Add, Set and Remove) accept -Batch. PnP queues the operations and sends them in chunks of 100 when you run Invoke-PnPBatch. That’s about 6 requests for 100 new items instead of roughly 600. Here’s a bulk status change:
$batch = New-PnPBatch
Get-PnPListItem -List $list -PageSize 500 |
Where-Object { $_["TaskStatus"] -eq "Not Started" } |
ForEach-Object { Set-PnPListItem -List $list -Identity $_.Id -Values @{ TaskStatus = "In Progress" } -Batch $batch }
Invoke-PnPBatch -Batch $batch -StopOnException
By default a batch keeps going after an error; -StopOnException stops at the first one. For a worked example at scale, see adding 5,000 items to a SharePoint list with PnP PowerShell, or bulk-loading rows from an Excel file.
Large lists need one more habit. SharePoint Online’s list view threshold is 5,000 items, so keep -PageSize well under it and filter CAML queries on indexed columns. My guide to the SharePoint list view threshold explains the indexing side.
To move items between lists instead, see how to copy items from one list to another using PnP PowerShell.
Common errors and fixes
- “Column ‘Status’ does not exist”: you used the display name. Switch to the internal name (
TaskStatusin my list). - Number or date saved wrong inside a batch: batched values follow the site’s regional settings. On a site that uses a comma as the decimal separator,
"2500.50"needs to be"2500,50". - Add-PnPListItem is not recognized: you’re in Windows PowerShell 5.1 or the ISE. PnP PowerShell 3.x runs in PowerShell 7.4 or later.
- Person column fails: the user must exist on the site. Use their email, or add them first with
New-PnPUser. - The attempted operation is prohibited because it exceeds the list view threshold: add
-PageSize, and index the column your CAML query filters on.
For more ready-to-use examples, see my PnP PowerShell scripts for SharePoint.
If you’re working with SharePoint lists from PowerShell, these guides pick up where this one ends:
Frequently Asked Questions
How do I add an item to a SharePoint list using PnP PowerShell?
Connect with Connect-PnPOnline, then run Add-PnPListItem -List “Tasks” -Values @{Title=”New task”}, using internal column names in the hashtable.
How do I update a SharePoint list item without creating a new version?
Run Set-PnPListItem with -UpdateType SystemUpdate. It keeps the version number and Modified By, and doesn’t trigger Power Automate flows. The Set-PnPListItem reference lists all three update types.
Does Remove-PnPListItem send items to the recycle bin?
Only with -Recycle. Without it, the item is permanently deleted. Add -Force to skip the confirmation prompt.
How do I get all items from a large SharePoint list with PnP PowerShell?
Use Get-PnPListItem -List “Tasks” -PageSize 500 so the items come back in pages below the 5,000-item list view threshold, and add -Fields to return only the columns you need.
How do I add or update many SharePoint list items quickly?
Create a batch with New-PnPBatch, pass -Batch to each Add, Set or Remove-PnPListItem call, then run Invoke-PnPBatch. See the PnP PowerShell batching article for details.

Hey! I’m Bijay Kumar, founder of SPGuides.com and a Microsoft Business Applications MVP (Power Automate, Power Apps). I launched this site in 2020 because I truly enjoy working with SharePoint, Power Platform, and SharePoint Framework (SPFx), and wanted to share that passion through step-by-step tutorials, guides, and training videos. My mission is to help you learn these technologies so you can utilize SharePoint, enhance productivity, and potentially build business solutions along the way.