Live training: next batches coming soon. Join the waitlist:Waitlists open:Developer Training →Developer →Copilot Studio Training →Copilot Studio →

How to Add Items From an Excel File to a SharePoint List Using PnP PowerShell

Our IT team tracks every laptop, monitor and headset in an Excel workbook, and they asked me to move it into a SharePoint list. Pasting rows into grid view works once, but not when the import repeats every month.

In this tutorial, we’ll add items from an Excel to SharePoint list using PowerShell: read the workbook with ImportExcel, create a matching list, add the rows with Add-PnPListItem, switch to a batch, report bad rows and prove the count matches.

Here’s what we’re building: the SPGDemo Equipment list, filled from the workbook with text, choice, currency, date, yes/no and person columns.

PowerShell check that the SharePoint list item count matches the 25 Excel rows after the import

Before You Start

You need PowerShell 7.4 or later, PnP PowerShell with your own Entra ID app and edit rights on the site. New to PnP? Start with installing PnP PowerShell for SharePoint Online.

The workbook has one sheet, Equipment, with 20 rows. The keys we pass to Add-PnPListItem are the internal names:

Excel headerInternal nameType
Asset NameTitleSingle line of text
Asset TagAssetTagSingle line of text (unique key)
CategoryCategoryChoice
Purchase PricePurchasePriceCurrency
Purchase DatePurchaseDateDate only
Under WarrantyUnderWarrantyYes/No
Assigned ToAssignedToPerson (email, e.g. miller@spguides.onmicrosoft.com)

Step 1: Install ImportExcel and Read the Workbook

Goal: get the rows into PowerShell without Excel installed.

  1. Open PowerShell 7.
  2. Install the module once with -Scope CurrentUser (no admin rights needed).
  3. Read the sheet with Import-Excel.
Install-Module -Name ImportExcel -Scope CurrentUser   # one time, no admin rights
$rows = Import-Excel -Path "C:\SPGuidesDemo\SPGDemo Equipment.xlsx" -WorksheetName "Equipment"
$rows | Select-Object -First 8 | Format-Table -AutoSize
PowerShell reading an Excel file with Import-Excel showing equipment rows before the SharePoint import

Checkpoint: Rows found: 20, one object per row. Purchase Date shows 46034 instead of a date: that’s the Excel serial number for 01/12/2026, fixed in Step 3.

Step 2: Connect and Create the SharePoint List

Goal: a list whose columns match the workbook. Using an existing list? Skip this and look up its internal names with SharePoint list column details in PnP PowerShell.

$siteUrl  = "https://spguides.sharepoint.com/sites/SPGuides"
$clientId = "00000000-0000-0000-0000-000000000000"   # your Entra ID app's client ID

Connect-PnPOnline -Url $siteUrl -ClientId $clientId -Interactive
$listName = "SPGDemo Equipment"

New-PnPList -Title $listName -Template GenericList -OnQuickLaunch | Out-Null

# Title becomes "Asset Name"; the other columns match the Excel headers
Set-PnPField -List $listName -Identity "Title" -Values @{ Title = "Asset Name" }
Add-PnPField -List $listName -DisplayName "Asset Tag" -InternalName "AssetTag" -Type Text | Out-Null
Add-PnPField -List $listName -DisplayName "Category" -InternalName "Category" -Type Choice `
    -Choices "Laptop", "Monitor", "Docking Station", "Phone", "Headset" | Out-Null
Add-PnPField -List $listName -DisplayName "Purchase Price" -InternalName "PurchasePrice" -Type Currency | Out-Null
Add-PnPFieldFromXml -List $listName -FieldXml '<Field Type="DateTime" DisplayName="Purchase Date" Name="PurchaseDate" Format="DateOnly" />' | Out-Null
Add-PnPField -List $listName -DisplayName "Under Warranty" -InternalName "UnderWarranty" -Type Boolean | Out-Null
Add-PnPField -List $listName -DisplayName "Assigned To" -InternalName "AssignedTo" -Type User | Out-Null

# show the new columns in the default view
Set-PnPView -List $listName -Identity "All Items" `
    -Fields "LinkTitle", "AssetTag", "Category", "PurchasePrice", "PurchaseDate", "UnderWarranty", "AssignedTo" | Out-Null

Add-PnPFieldFromXml sets the date-only format in one line. To build columns from the headers automatically, see adding SharePoint list fields from Excel.

PnP PowerShell creating the SPGDemo Equipment list with choice, currency, date, yes/no and person columns

Checkpoint: Get-PnPField lists seven columns with types Text, Text, Choice, Currency, DateTime, Boolean and User.

Step 3: Add the Excel Rows With Add-PnPListItem

Goal: one item per row, with failures reported, not fatal.

  1. Read the sheet with -AsDate "Purchase Date" so dates arrive as DateTime values.
  2. Load the Category field’s real Choices so typos never reach the list.
  3. Wrap Add-PnPListItem in try/catch and collect skipped rows.
$listName = "SPGDemo Equipment"
$path     = "C:\SPGuidesDemo\SPGDemo Equipment.xlsx"

$rows    = Import-Excel -Path $path -WorksheetName "Equipment" -AsDate "Purchase Date"
$choices = (Get-PnPField -List $listName -Identity "Category").Choices
$added = 0; $skipped = @()
$timer = [Diagnostics.Stopwatch]::StartNew()

foreach ($row in $rows) {
    $tag = $row.'Asset Tag'
    # validate before SharePoint sees the row
    if ($row.'Purchase Date' -isnot [datetime]) { $skipped += "$tag : bad date '$($row.'Purchase Date')'"; continue }
    if ($row.Category -notin $choices)         { $skipped += "$tag : unknown category '$($row.Category)'"; continue }

    try {
        Add-PnPListItem -List $listName -Values @{
            Title         = $row.'Asset Name'
            AssetTag      = $tag
            Category      = $row.Category
            PurchasePrice = [double]$row.'Purchase Price'
            PurchaseDate  = $row.'Purchase Date'
            UnderWarranty = [bool]$row.'Under Warranty'
            AssignedTo    = $row.'Assigned To'
        } -ErrorAction Stop | Out-Null
        $added++
    }
    catch { $skipped += "$tag : $($_.Exception.Message)" }
}

"Rows in Excel : $($rows.Count)"
"Items added   : $added"
"Rows skipped  : $($skipped.Count)"
$skipped | ForEach-Object { "  - $_" }
"Time taken    : {0:N1} seconds" -f $timer.Elapsed.TotalSeconds

The person column takes an email address, Yes/No takes $true/$false, and outside a batch, numbers use US notation.

Add-PnPListItem loop adding Excel rows to a SharePoint list and reporting three skipped rows

Checkpoint: Items added: 17, Rows skipped: 3 in 14.7 seconds. EQ-1013 says Monitors, EQ-1014 says TBD, and EQ-1015’s misspelled email failed with The specified user … could not be found.

For lookup and multi-choice values, see adding SharePoint list items with PnP PowerShell.

Step 4: Bulk Upload Faster With New-PnPBatch

Goal: one batch and safe reruns. I fixed the three bad rows and added five purchases (25 rows now).

  1. Read the existing AssetTag values and skip those rows.
  2. Create a batch with New-PnPBatch and pass -Batch $batch.
  3. Send it with Invoke-PnPBatch.
# same $listName, $path, $rows and $choices as in Step 3, plus the tags already in the list
$existing = Get-PnPListItem -List $listName -PageSize 500 -Fields "AssetTag" | ForEach-Object { $_["AssetTag"] }
$batch = New-PnPBatch
$queued = 0; $already = 0

foreach ($row in $rows) {
    if ($row.'Asset Tag' -in $existing) { $already++; continue }
    if ($row.'Purchase Date' -isnot [datetime] -or $row.Category -notin $choices) { continue }

    Add-PnPListItem -List $listName -Batch $batch -Values @{
        Title = $row.'Asset Name'; AssetTag = $row.'Asset Tag'; Category = $row.Category
        PurchasePrice = [double]$row.'Purchase Price'; PurchaseDate = $row.'Purchase Date'
        UnderWarranty = [bool]$row.'Under Warranty'; AssignedTo = $row.'Assigned To'
    }
    $queued++
}

Invoke-PnPBatch -Batch $batch
"Already in the list: $already   Added in one batch: $queued"
New-PnPBatch and Invoke-PnPBatch bulk uploading new Excel rows to a SharePoint list in one batch

Checkpoint: Already in the list: 17, Added in one batch: 8, Time taken: 2.0 seconds.

Invoke-PnPBatch runs the whole batch and lists errors at the end; -StopOnException stops at the first. Inside a batch, numbers use the site’s regional separators. For thousands of rows, read adding more than 5000 items to a SharePoint list.

Test the Import

Compare the Excel row count with the list’s item count:

$excelRows = (Import-Excel -Path $path -WorksheetName "Equipment").Count
$listItems = (Get-PnPList -Identity $listName).ItemCount
"Excel rows: $excelRows   List items: $listItems   Match: $($excelRows -eq $listItems)"

Both are 25. EQ-1014 now shows 04/06/2026, prices read as currency like $1,199.99, and the person column resolved to real users. To change rows later, use list item CRUD with PnP PowerShell.

No ImportExcel? Save the Sheet as CSV

Can’t install modules? Save the sheet as CSV UTF-8 and use Import-Csv, converting the text values yourself:

$rows = Import-Csv -Path "C:\SPGuidesDemo\SPGDemo Equipment.csv"   # every value is text
$date = [datetime]::ParseExact($row.'Purchase Date', "MM/dd/yyyy", [cultureinfo]"en-US")

Make It Production-Ready

  • Unique key: the AssetTag check makes reruns safe; index it as the list grows.
  • Unattended runs: use certificate-based app-only sign-in instead of -Interactive.
  • Test on a copy: keep deleting all items from a SharePoint list handy for a clean retry.

Troubleshooting

  • Dates show as 46034: add -AsDate or convert with [datetime]::FromOADate().
  • The specified user could not be found: fix the email and rerun; existing rows are skipped.
  • Column does not exist: use the internal name (PurchaseDate), not the display name.
  • Connect-PnPOnline not recognized: see fixing Connect-PnPOnline is not recognized.

Where to Take It Next

A few more guides that pair well with an Excel-to-SharePoint import:

Frequently Asked Questions

How do I import Excel data into a SharePoint list using PnP PowerShell?

Read the sheet with Import-Excel, connect with Connect-PnPOnline, then call Add-PnPListItem for each row with internal column names as keys. The Add-PnPListItem reference shows each column’s value format.

How do I read an Excel file in PowerShell without Excel installed?

Install the ImportExcel module and run Import-Excel -Path file.xlsx; you get one object per row.

What is the fastest way to bulk upload to a SharePoint list from Excel?

Queue rows with Add-PnPListItem -Batch and send them with Invoke-PnPBatch. In my test, 8 rows took 2 seconds in one batch versus 14.7 seconds for 17 rows one by one. See New-PnPBatch.

Can Microsoft Lists import from Excel without PowerShell?

Yes, Microsoft Lists can create a new list from an Excel table, but it can’t add validated rows to an existing list.

Why does Import-Excel return numbers instead of dates?

Excel stores dates as serial numbers. Add -AsDate with the column name to get DateTime values.