PnP PowerShell Support¶
Home → Documentation → Features → PnP PowerShell
Overview¶
Cesivi Server provides limited support for PnP PowerShell cmdlets. Since PnP PowerShell is a wrapper around CSOM (Client-Side Object Model), its compatibility is inherently limited by the underlying CSOM implementation constraints.
PnP Coverage: 21.4% (51/238 tests passing) Status: ⚠️ CSOM-Dependent - 85% of failures inherited from CSOM limitations Best For: Simple PowerShell automation and basic CRUD operations
Understanding PnP PowerShell Limitations¶
The Dependency Chain¶
PnP Cmdlet (PowerShell)
↓ (wraps)
CSOM Operation (ClientContext, Load/Query)
↓ (uses)
CSOM Binary Protocol (proprietary JSON format)
↓ (communicates with)
Mock Server Response
Key Insight: Every PnP cmdlet ultimately uses CSOM. If CSOM can't deserialize the response or initialize properties correctly, PnP cmdlets fail too.
Why Coverage is Low (21.4%)¶
PnP PowerShell's 21.4% pass rate directly reflects CSOM's 70.3% pass rate:
Failure Breakdown (187 failing tests):
- 155-165 tests (85%): CSOM-dependent failures
- 136 tests (73%): PropertyNotInitializedException from CSOM Load() lambda limitation
- 20-29 tests (27%): Binary JSON serialization errors from CSOM protocol
- 15-25 tests (10-15%): PnP-specific failures
- Parameter binding issues
- Missing cmdlet implementations
- PowerShell wrapper edge cases
Conclusion: Fixing PnP requires fixing CSOM first. The 21.4% pass rate is the architectural ceiling for CSOM-dependent operations.
What Works¶
Connection Management (✅ Reliable)¶
# Connect to mock server
Connect-PnPOnline -Url "http://localhost:5000" -UseWebLogin
# With credentials
Connect-PnPOnline -Url "http://localhost:5000" -Credentials (Get-Credential)
# Generic authentication (when AcceptAllCredentials: true)
$creds = New-Object PSCredential("testuser", (ConvertTo-SecureString "password" -AsPlainText -Force))
Connect-PnPOnline -Url "http://localhost:5000" -Credentials $creds
# Verify connection
Get-PnPConnection
# Disconnect
Disconnect-PnPOnline
Web Operations (✅ 90%+ Reliable)¶
# Get web properties (WORKS)
$web = Get-PnPWeb
Write-Host "Web Title: $($web.Title)"
Write-Host "Web URL: $($web.Url)"
# Get all webs (WORKS)
$webs = Get-PnPSubWebs
foreach ($web in $webs) {
Write-Host "Subweb: $($web.Title)"
}
# Create subweb (WORKS with limitations)
New-PnPWeb -Title "My Subweb" -Url "mysubweb" -Template "STS#0"
# Remove web (WORKS)
Remove-PnPWeb -Url "mysubweb" -Force
List Operations (✅ 85%+ Reliable for Basic)¶
# Get a list (WORKS)
$list = Get-PnPList -Identity "Documents"
Write-Host "List Title: $($list.Title)"
Write-Host "Item Count: $($list.ItemCount)"
# Get all lists (WORKS)
$lists = Get-PnPList
foreach ($list in $lists) {
Write-Host "List: $($list.Title)"
}
# Create a list (WORKS)
New-PnPList -Title "MyList" -Template GenericList
# Remove a list (WORKS)
Remove-PnPList -Identity "MyList" -Force
File Operations (✅ 85%+ Reliable)¶
# Upload a file (WORKS)
Add-PnPFile -Path "C:\temp\document.pdf" -Folder "Shared Documents"
# Download a file (WORKS)
Get-PnPFile -Url "/sites/site/Shared Documents/document.pdf" -Path "C:\temp\downloaded.pdf" -AsFile
# Get file metadata (WORKS)
$file = Get-PnPFile -Url "/sites/site/Shared Documents/document.pdf"
Write-Host "File Size: $($file.Length)"
# Remove a file (WORKS)
Remove-PnPFile -ServerRelativeUrl "/sites/site/Shared Documents/document.pdf" -Force
Folder Operations (✅ 80%+ Reliable)¶
# Create a folder (WORKS)
New-PnPFolder -Name "MyFolder" -Folder "Shared Documents"
# Get folder (WORKS with limitations)
$folder = Get-PnPFolder -Url "/sites/site/Shared Documents/MyFolder"
# Remove folder (WORKS)
Remove-PnPFolder -Name "MyFolder" -Folder "Shared Documents" -Force
What Doesn't Work (or Has Limitations)¶
List Item Operations (⚠️ 50% Reliable)¶
Problem: PnP cmdlets use selective loading internally, triggering CSOM PropertyNotInitializedException.
# ❌ PROBLEMATIC - May throw PropertyNotInitializedException
$items = Get-PnPListItem -List "Documents" -Fields "Title", "Created"
$title = $items[0]["Title"] # May fail
# ✅ WORKAROUND - Use REST API instead
$items = Invoke-PnPSPRestMethod -Url "/_api/web/lists/getbytitle('Documents')/items?`$select=Title,Created"
foreach ($item in $items.value) {
Write-Host "Title: $($item.Title), Created: $($item.Created)"
}
Field and Content Type Operations (❌ <30% Reliable)¶
Problem: Field/ContentType cmdlets depend heavily on CSOM selective loading and site column persistence.
# ❌ OFTEN FAILS - CSOM limitations
Get-PnPField -Identity "Title" # May fail with PropertyNotInitializedException
New-PnPField -DisplayName "MyField" -InternalName "MyField" -Type Text # May not persist
Set-PnPField -Identity "MyField" -Values @{Description="New description"} # May not persist
# ✅ WORKAROUND - Use REST API
Invoke-PnPSPRestMethod -Url "/_api/web/fields/getbyinternalname('Title')"
View Operations (❌ <20% Reliable)¶
Problem: View updates don't persist (CSOM persistence limitation).
# ❌ OFTEN FAILS
Get-PnPView -List "Documents" # May fail
Set-PnPView -List "Documents" -Identity "All Documents" -Values @{RowLimit=100} # Doesn't persist
# ✅ WORKAROUND - Use REST API
Invoke-PnPSPRestMethod -Url "/_api/web/lists/getbytitle('Documents')/views"
Cmdlet Compatibility Matrix¶
Legend¶
- ✅ Fully Compatible - Works reliably (>85% success rate)
- ⚠️ Partially Compatible - Works with limitations (60-85% success rate)
- ❌ Not Compatible - Fails consistently (<60% success rate)
Connection & Web Management¶
| Cmdlet | Status | Notes | Alternative |
|---|---|---|---|
Connect-PnPOnline |
✅ 95%+ | Works reliably | N/A |
Disconnect-PnPOnline |
✅ 95%+ | Works reliably | N/A |
Get-PnPContext |
✅ 95%+ | Works reliably | N/A |
Get-PnPWeb |
✅ 90%+ | Some properties may be null | REST API |
New-PnPWeb |
⚠️ 70%+ | Site creation limited | REST API |
Remove-PnPWeb |
✅ 85%+ | Works reliably | N/A |
List & List Item Operations¶
| Cmdlet | Status | Notes | Alternative |
|---|---|---|---|
Get-PnPList |
✅ 90%+ | Basic scenarios work | REST API |
New-PnPList |
✅ 85%+ | Basic list creation | REST API |
Remove-PnPList |
✅ 90%+ | Reliable deletion | N/A |
Get-PnPListItem |
⚠️ 50%+ | Fails with -Fields parameter | REST API |
Add-PnPListItem |
⚠️ 60%+ | Creation works, updates may fail | REST API |
Set-PnPListItem |
❌ <20% | CSOM serialization errors | REST API |
Remove-PnPListItem |
✅ 85%+ | Item deletion reliable | N/A |
File & Folder Operations¶
| Cmdlet | Status | Notes | Alternative |
|---|---|---|---|
Get-PnPFile |
✅ 90%+ | File metadata retrieval works | REST API |
Add-PnPFile |
✅ 85%+ | File upload reliable | REST API |
Remove-PnPFile |
✅ 85%+ | File deletion reliable | REST API |
Get-PnPFolder |
⚠️ 70%+ | Folder metadata partial | REST API |
New-PnPFolder |
✅ 80%+ | Folder creation works | REST API |
Remove-PnPFolder |
✅ 80%+ | Folder deletion works | REST API |
Copy-PnPFile |
⚠️ 40%+ | File copy unreliable | REST API |
Move-PnPFile |
⚠️ 40%+ | File move unreliable | REST API |
Field & Content Type Operations¶
| Cmdlet | Status | Notes | Alternative |
|---|---|---|---|
Get-PnPField |
⚠️ 60%+ | Selective field loading may fail | REST API |
New-PnPField |
❌ <30% | Field creation not persisting | REST API |
Set-PnPField |
❌ <20% | Field updates not persisting | REST API |
Get-PnPContentType |
⚠️ 50%+ | Collection loading may fail | REST API |
New-PnPContentType |
❌ <20% | ContentType creation fails | REST API |
Permission & User Operations¶
| Cmdlet | Status | Notes | Alternative |
|---|---|---|---|
Get-PnPUser |
✅ 85%+ | User lookup works | REST API |
New-PnPUser |
⚠️ 70%+ | User addition works | REST API |
Get-PnPGroup |
✅ 85%+ | Group enumeration works | REST API |
New-PnPGroup |
⚠️ 70%+ | Group creation works | REST API |
Grant-PnPSiteDesignRights |
⚠️ 50%+ | Permission assignment partial | REST API |
Recommended Workflow¶
Start with PnP for Simple Operations¶
# ✅ This works well
Connect-PnPOnline -Url "http://localhost:5000" -UseWebLogin
# ✅ Simple metadata retrieval
$web = Get-PnPWeb
$list = Get-PnPList "Documents"
Write-Host "List has $($list.ItemCount) items"
# ✅ File operations
Add-PnPFile -Path "C:\temp\file.pdf" -Folder "Shared Documents"
Switch to REST for Complex Operations¶
# ❌ This may fail with PnP
$items = Get-PnPListItem -List "Documents" -Fields "Title", "Created"
# ✅ Use REST API instead
$items = Invoke-PnPSPRestMethod -Url "/_api/web/lists/getbytitle('Documents')/items?`$select=Title,Created"
foreach ($item in $items.value) {
Write-Host "Title: $($item.Title)"
}
# ✅ Field operations via REST
$fields = Invoke-PnPSPRestMethod -Url "/_api/web/fields"
foreach ($field in $fields.value) {
Write-Host "Field: $($field.Title)"
}
Use CSOM Directly for Advanced Scenarios¶
# ✅ Direct CSOM access for full control
$ctx = Get-PnPContext
$list = $ctx.Web.Lists.GetByTitle("Documents")
$ctx.Load($list.Items) # No lambda expression
$ctx.ExecuteQuery()
foreach ($item in $list.Items) {
Write-Host "Item: $($item['Title'])"
}
Common Errors and Solutions¶
Error: PropertyNotInitializedException¶
Symptom:
The property or field 'FieldValuesAsText' has not been initialized
Cause: PnP cmdlet uses selective loading internally, triggering CSOM limitation.
Solution:
# ❌ Fails with PropertyNotInitializedException
$items = Get-PnPListItem -List "Documents" -Fields "Title"
# ✅ Use REST API instead
$items = Invoke-PnPSPRestMethod -Url "/_api/web/lists/getbytitle('Documents')/items?`$select=Title"
Error: Cannot contact site¶
Symptom:
Cannot contact site at the specified URL http://localhost:5000
Solutions:
1. Wait 5-10 seconds after starting server
2. Verify server health: Invoke-WebRequest http://localhost:5000/health
3. Check credentials
4. Use -RetryCount and -RetryWait parameters
Connect-PnPOnline -Url "http://localhost:5000" -UseWebLogin -RetryCount 3 -RetryWait 2
Error: List does not exist¶
Symptom:
List 'Documents' does not exist at site with URL 'http://localhost:5000'
Solutions: 1. Verify list name (case-sensitive) 2. Check if list was created 3. Use REST to verify list existence
# Verify list exists
$lists = Invoke-PnPSPRestMethod -Url "/_api/web/lists?`$filter=Title eq 'Documents'"
if ($lists.value.Count -eq 0) {
Write-Host "List 'Documents' does not exist"
New-PnPList -Title "Documents" -Template DocumentLibrary
}
Performance Characteristics¶
| Operation Type | Avg Response Time | Reliability | Notes |
|---|---|---|---|
| Connection | 100-200ms | High | Initial connection |
| Simple Cmdlets | 50-150ms | High | Get-PnPWeb, Get-PnPList |
| File Operations | 200-500ms | High | Upload/download |
| Complex Cmdlets | 100-300ms | Medium | May fail with CSOM errors |
| REST API Calls | 20-50ms | High | Invoke-PnPSPRestMethod |
Recommendation: Use REST API via Invoke-PnPSPRestMethod for best performance and reliability.
Example Scripts¶
Complete CRUD Script (Using Workarounds)¶
# Connect
Connect-PnPOnline -Url "http://localhost:5000" -UseWebLogin
# Create a list (✅ Works)
New-PnPList -Title "MyList" -Template GenericList
# Add items (⚠️ Use REST for reliability)
$itemData = @{
__metadata = @{ type = "SP.Data.MyListListItem" }
Title = "Item 1"
}
Invoke-PnPSPRestMethod -Method POST `
-Url "/_api/web/lists/getbytitle('MyList')/items" `
-Content $itemData
# Get items with specific fields (✅ Use REST)
$items = Invoke-PnPSPRestMethod -Url "/_api/web/lists/getbytitle('MyList')/items?`$select=Title,Created"
foreach ($item in $items.value) {
Write-Host "Title: $($item.Title), Created: $($item.Created)"
}
# Update item (✅ Use REST)
$updateData = @{
__metadata = @{ type = "SP.Data.MyListListItem" }
Title = "Updated Title"
}
Invoke-PnPSPRestMethod -Method POST `
-Url "/_api/web/lists/getbytitle('MyList')/items(1)" `
-Content $updateData `
-XHTTPMethod "MERGE"
# Delete item (✅ Use REST or PnP)
Remove-PnPListItem -List "MyList" -Identity 1 -Force
# Delete list (✅ Works)
Remove-PnPList -Identity "MyList" -Force
# Disconnect
Disconnect-PnPOnline
Bulk File Upload Script¶
# Connect
Connect-PnPOnline -Url "http://localhost:5000" -UseWebLogin
# Upload all files from a folder (✅ Works reliably)
$files = Get-ChildItem "C:\temp\documents" -File
foreach ($file in $files) {
Write-Host "Uploading $($file.Name)..."
Add-PnPFile -Path $file.FullName -Folder "Shared Documents"
}
Write-Host "Upload complete: $($files.Count) files"
# Disconnect
Disconnect-PnPOnline
When to Use What¶
| Scenario | Recommended | Reason |
|---|---|---|
| Simple metadata retrieval | PnP PowerShell | Easy, works well |
| Get list items with specific fields | REST API | Avoids CSOM selective loading |
| Bulk item updates | REST API | Better reliability |
| Create/delete lists | PnP PowerShell | Simple, reliable |
| File operations | PnP PowerShell | Upload/download work well |
| Field/ContentType operations | REST API | PnP has low success rate |
| Complex permission operations | REST API or CSOM direct | More control |
Related Documentation¶
Features¶
- CSOM - Underlying CSOM implementation and limitations
- REST API - Recommended alternative for complex operations
- Authentication - Auth methods and configuration
- Permissions - Permission system guide
Reference¶
- Known Limitations - Complete limitation details
- Error Catalog - Error patterns and solutions
- API Reference - Complete API documentation
Troubleshooting¶
- Troubleshooting Guide - Common issues and fixes
- Quick Start - Get started quickly
Last Updated: November 15, 2025 Version: 1.0.0
Navigation: Home | Documentation | Features | PnP PowerShell