<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/" >

<channel>
	<title>PowerShell &#8211; SPGuides</title>
	<atom:link href="https://www.spguides.com/category/powershell/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.spguides.com</link>
	<description>SharePoint &#38; Power Platform Tutorials &#38; Training</description>
	<lastBuildDate>Wed, 11 Nov 2026 13:21:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.12</generator>

<image>
	<url>https://www.spguides.com/wp-content/uploads/2026/05/SPguides-Favicon-new-348x215.avif</url>
	<title>PowerShell &#8211; SPGuides</title>
	<link>https://www.spguides.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to Delete a Folder in SharePoint Online with PowerShell (PnP)</title>
		<link>https://www.spguides.com/delete-folder-sharepoint-powershell/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Tue, 10 Nov 2026 14:00:00 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Delete Folder SharePoint PowerShell]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=139951</guid>

					<description><![CDATA[A project lead asked me to clean up the SPGuides Projects library: last year&#8217;s archive folder, an old projects tree, and a pile of empty upload folders. Clicking through them in the browser is slow, and one wrong click takes the wrong folder with it. A script is faster and repeatable, but deleting folders blindly ... <a title="How to Delete a Folder in SharePoint Online with PowerShell (PnP)" class="read-more" href="https://www.spguides.com/delete-folder-sharepoint-powershell/" aria-label="Read more about How to Delete a Folder in SharePoint Online with PowerShell (PnP)">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>A project lead asked me to clean up the SPGuides Projects library: last year&#8217;s archive folder, an old projects tree, and a pile of empty upload folders. Clicking through them in the browser is slow, and one wrong click takes the wrong folder with it.</p>



<p>A script is faster and repeatable, but deleting folders blindly is risky too. A folder takes every file and subfolder inside it, so I want each delete to land in the recycle bin where I can undo it.</p>



<p>In this tutorial, I&#8217;ll show you how I handle <strong>delete folder sharepoint powershell</strong> jobs with PnP PowerShell: delete one folder, delete a subfolder, clear out only empty folders, handle a missing folder, restore a folder from the recycle bin, and run it in Windows PowerShell 5.1.</p>



<p class="spg-quick" style="background-color:#EEF4FF;border-left:4px solid #1D5BD8;padding:14px 18px;border-radius:6px"><strong>Quick answer:</strong> Connect with <code>Connect-PnPOnline</code>, then run <code>Remove-PnPFolder -Name "Archive 2025" -Folder "SPGuidesProjects" -Recycle -Force</code>. <code>-Name</code> is the folder to delete, <code>-Folder</code> is its parent&#8217;s site-relative URL, <code>-Recycle</code> sends it to the site recycle bin instead of deleting it outright, and <code>-Force</code> skips the confirmation prompt.</p>



<h2 class="wp-block-heading">What you need before you start</h2>



<p>You need the PnP.PowerShell module, your own Entra ID app registration for sign-in, and at least Edit permission on the library. I covered the install and the app registration step by step in my post on <a href="https://www.spguides.com/create-folder-sharepoint-powershell/">creating a folder in SharePoint with PowerShell</a>, so here&#8217;s only the short version:</p>



<pre class="wp-block-code"><code>Install-Module PnP.PowerShell -Scope CurrentUser

Connect-PnPOnline -Url "https://spguides.sharepoint.com/sites/SPGuides" -ClientId "&lt;your-app-client-id&gt;" -DeviceLogin -PersistLogin</code></pre>



<p>Replace the client ID with your app&#8217;s Application (client) ID. If the install itself gives you trouble, this guide to <a href="https://www.spguides.com/install-pnp-powershell/">installing PnP PowerShell for SharePoint Online</a> walks through the usual fixes.</p>



<p>I ran every script below on 10/25/2026 against my /sites/SPGuides site, in PowerShell 7.6.7 with PnP.PowerShell 3.3.0. The library shows as SPGuides Projects, but its URL name is SPGuidesProjects.</p>



<h2 class="wp-block-heading">How do I delete a folder with Remove-PnPFolder?</h2>



<p>Remove-PnPFolder takes the folder name and its parent path as two separate values. That trips people up, because you don&#8217;t pass the full path of the folder you&#8217;re deleting:</p>



<figure class="wp-block-table"><table><thead><tr><th>Parameter</th><th>Required</th><th>What it does</th></tr></thead><tbody><tr><td>-Name</td><td>Yes</td><td>The name of the folder to delete, for example <code>"Archive 2025"</code></td></tr><tr><td>-Folder</td><td>Yes</td><td>The parent folder&#8217;s site-relative URL: <code>"SPGuidesProjects"</code> or <code>"SPGuidesProjects/Old Projects"</code></td></tr><tr><td>-Recycle</td><td>No</td><td>Moves the folder to the recycle bin instead of deleting it directly</td></tr><tr><td>-Force</td><td>No</td><td>Skips the &#8220;are you sure&#8221; confirmation prompt</td></tr></tbody></table></figure>



<p>Here&#8217;s the script I ran on the Archive 2025 folder. It counts the files first, deletes the folder to the recycle bin, then checks whether it&#8217;s still there:</p>



<pre class="wp-block-code"><code>$folder = Get-PnPFolder -Url "SPGuidesProjects/Archive 2025" -Includes ItemCount
"Deleting $($folder.ServerRelativeUrl) ($($folder.ItemCount) files inside)"

# -Recycle sends the folder and everything in it to the site recycle bin; -Force skips the prompt
Remove-PnPFolder -Name "Archive 2025" -Folder "SPGuidesProjects" -Recycle -Force

$stillThere = Get-PnPFolder -Url "SPGuidesProjects/Archive 2025" -ErrorAction SilentlyContinue
"Archive 2025 still exists: $([bool]$stillThere)"</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="2a2b2b" data-has-transparency="false" style="--dominant-color: #2a2b2b;" fetchpriority="high" decoding="async" width="1013" height="451" sizes="(max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-remove-pnpfolder-recycle.avif" alt="Remove-PnPFolder with -Recycle deleting the Archive 2025 folder and its 3 files in PowerShell 7" class="wp-image-139963 not-transparent" title="delete folder sharepoint powershell remove pnpfolder recycle" srcset="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-remove-pnpfolder-recycle.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-remove-pnpfolder-recycle-300x134.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-remove-pnpfolder-recycle-768x342.avif 768w" /></figure></div>


<p>With <code>-Recycle</code>, the cmdlet returns a RecycleBinItemId, the ID of the new recycle bin entry. The final check confirms the folder is gone:</p>



<pre class="wp-block-code"><code>Deleting /sites/SPGuides/SPGuidesProjects/Archive 2025 (3 files inside)

RecycleBinItemId
----------------
c928b896-ec82-4f73-a7d3-4f0cf5a272bf
Archive 2025 still exists: False</code></pre>



<p>The <code>-Folder</code> value is the library&#8217;s URL name, not its display name. The default Documents library is <code>Shared Documents</code>, for example. To look up URL names, <a href="https://www.spguides.com/get-all-lists-and-libraries-from-sharepoint-online-site-using-pnp-powershell/">list all lists and libraries with PnP PowerShell</a>.</p>



<h3 class="wp-block-heading">What happens without -Recycle?</h3>



<p>According to the PnP documentation, leaving out <code>-Recycle</code> deletes the folder directly instead of moving it to the recycle bin, so you can&#8217;t restore it from there. I never run it that way on client sites, and I didn&#8217;t run it for this post either.</p>



<p>Leaving out <code>-Force</code> is harmless: PowerShell just asks you to confirm each delete. I keep the prompt while I test a script and add <code>-Force</code> once the paths are right.</p>



<h2 class="wp-block-heading">Where does the deleted folder go?</h2>



<p>With <code>-Recycle</code>, the folder and everything in it land in the site recycle bin, exactly as if you&#8217;d deleted it in the browser. Open the site, go to <strong>Site contents</strong>, and select <strong>Recycle bin</strong>.</p>



<p>Here&#8217;s my site recycle bin after the test runs. Each entry shows the original location and who deleted it, and Q1 2027 Planning appears twice because I deleted it in two separate runs:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f9f5f5" data-has-transparency="false" style="--dominant-color: #f9f5f5;" decoding="async" width="1100" height="550" sizes="(max-width: 1100px) 100vw, 1100px" src="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-deleted-folders-in-site-recycle-bin.avif" alt="SharePoint site recycle bin listing the Archive 2025, 2024 and Batch folders deleted with Remove-PnPFolder" class="wp-image-139964 not-transparent" title="delete folder sharepoint powershell deleted folders in site recycle bin" srcset="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-deleted-folders-in-site-recycle-bin.avif 1100w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-deleted-folders-in-site-recycle-bin-300x150.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-deleted-folders-in-site-recycle-bin-1024x512.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-deleted-folders-in-site-recycle-bin-768x384.avif 768w" /></figure></div>


<p>Microsoft keeps deleted items for 93 days from the original delete. That window covers both the site recycle bin and the second-stage site collection recycle bin. My <a href="https://www.spguides.com/sharepoint-online-recycle-bin/">SharePoint recycle bin tutorial</a> explains both stages.</p>



<h2 class="wp-block-heading">Delete a subfolder and everything inside it</h2>



<p>To delete a folder deeper in the tree, put the full parent path in <code>-Folder</code>. My Old Projects folder had a 2024 subfolder with Kickoff and Status Reports inside it. Get-PnPFolderItem lists what&#8217;s about to go before the delete runs:</p>



<pre class="wp-block-code"><code># -Folder is the parent path; -Name is the folder to delete (its subfolders go with it)
Get-PnPFolderItem -FolderSiteRelativeUrl "SPGuidesProjects/Old Projects/2024" -ItemType Folder |
    Select-Object Name

Remove-PnPFolder -Name "2024" -Folder "SPGuidesProjects/Old Projects" -Recycle -Force | Out-Null
"Deleted: SPGuidesProjects/Old Projects/2024 and its subfolders"</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="2b2b2c" data-has-transparency="false" style="--dominant-color: #2b2b2c;" decoding="async" width="1013" height="411" sizes="(max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-delete-subfolder-and-children.avif" alt="Get-PnPFolderItem listing the Kickoff and Status Reports subfolders before Remove-PnPFolder deletes the 2024 folder" class="wp-image-139965 not-transparent" title="delete folder sharepoint powershell delete subfolder and children" srcset="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-delete-subfolder-and-children.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-delete-subfolder-and-children-300x122.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-delete-subfolder-and-children-768x312.avif 768w" /></figure></div>


<p>Both subfolders went with 2024; there&#8217;s no separate recursive switch. If you only want to relocate a branch, not delete it, <a href="https://www.spguides.com/move-folders-in-sharepoint-online/">move the folder in SharePoint Online</a> instead.</p>



<h2 class="wp-block-heading">Delete only empty folders</h2>



<p>Upload jobs and migrations leave behind empty folders. This loop checks each subfolder&#8217;s ItemCount and deletes only the ones with nothing in them:</p>



<pre class="wp-block-code"><code>$parent = "SPGuidesProjects/Temp Uploads"

$folders = Get-PnPFolderItem -FolderSiteRelativeUrl $parent -ItemType Folder
foreach ($f in $folders) {
    if ($f.ItemCount -eq 0) {
        Remove-PnPFolder -Name $f.Name -Folder $parent -Recycle -Force | Out-Null
        "Deleted empty folder: $($f.Name)"
    } else {
        "Kept: $($f.Name) ($($f.ItemCount) item)"
    }
}</code></pre>



<p>My Temp Uploads folder had Batch 01 through Batch 05, and only Batch 05 held a file (an upload log). The script deleted the four empty ones and kept the fifth:</p>



<pre class="wp-block-code"><code>Deleted empty folder: Batch 01
Deleted empty folder: Batch 02
Deleted empty folder: Batch 03
Deleted empty folder: Batch 04
Kept: Batch 05 (1 item)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="282829" data-has-transparency="false" style="--dominant-color: #282829;" loading="lazy" decoding="async" width="1013" height="471" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-delete-empty-folders-loop.avif" alt="PowerShell loop deleting empty Batch folders in Temp Uploads and keeping Batch 05 with one item" class="wp-image-139966 not-transparent" title="delete folder sharepoint powershell delete empty folders loop" srcset="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-delete-empty-folders-loop.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-delete-empty-folders-loop-300x139.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-delete-empty-folders-loop-768x357.avif 768w" /></figure></div>


<p>ItemCount counts subfolders too, so a folder that holds only an empty subfolder is kept. Run the loop again to catch the next level. If empty folders keep coming back, revisit your <a href="https://www.spguides.com/sharepoint-folder-structure/">SharePoint folder structure</a>.</p>



<h2 class="wp-block-heading">Restore a deleted folder with PnP PowerShell</h2>



<p>When someone deletes the wrong folder, you can bring it back without opening the browser. Get-PnPRecycleBinItem reads the recycle bin, and Restore-PnPRecycleBinItem puts the item back in its original location:</p>



<pre class="wp-block-code"><code>$item = Get-PnPRecycleBinItem -FirstStage |
    Where-Object { $_.LeafName -eq "Archive 2025" -and $_.ItemType -eq "Folder" }
$item | Select-Object LeafName, DirName, ItemType, DeletedByName

$item | Restore-PnPRecycleBinItem -Force
$restored = Get-PnPFolder -Url "SPGuidesProjects/Archive 2025" -ErrorAction SilentlyContinue
"Archive 2025 restored: $([bool]$restored)"</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="2b2c2c" data-has-transparency="false" style="--dominant-color: #2b2c2c;" loading="lazy" decoding="async" width="1013" height="411" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-restore-folder-from-recycle-bin.avif" alt="Get-PnPRecycleBinItem finding the Archive 2025 folder and Restore-PnPRecycleBinItem restoring it" class="wp-image-139968 not-transparent" title="delete folder sharepoint powershell restore folder from recycle bin" srcset="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-restore-folder-from-recycle-bin.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-restore-folder-from-recycle-bin-300x122.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-restore-folder-from-recycle-bin-768x312.avif 768w" /></figure></div>


<p>The output showed Archive 2025 from sites/SPGuides/SPGuidesProjects with ItemType Folder, then <strong>Archive 2025 restored: True</strong>. Filter on ItemType, because a file and a folder can share a name.</p>



<p>Two catches. Get-PnPRecycleBinItem needs Site Collection Administrator rights, so a site member gets nothing back. And restore the folder entry itself: restoring a single file from a deleted folder recreates the folder with only that file in it.</p>



<h2 class="wp-block-heading">Delete a folder in Windows PowerShell 5.1</h2>



<p>PnP.PowerShell 3.x needs PowerShell 7.4 or later. On a server that only has Windows PowerShell 5.1, the last version that loads is PnP.PowerShell 1.12.0, and its <code>-DeviceLogin</code> also needs <code>-Tenant</code>:</p>



<pre class="wp-block-code"><code>Install-Module PnP.PowerShell -RequiredVersion 1.12.0 -Scope CurrentUser
Connect-PnPOnline -Url "https://spguides.sharepoint.com/sites/SPGuides" -ClientId "&lt;your-app-client-id&gt;" -Tenant "spguides.onmicrosoft.com" -DeviceLogin</code></pre>



<p>Remove-PnPFolder takes the same parameters in 1.12.0. This is the script I ran there:</p>



<pre class="wp-block-code"><code>"PowerShell $($PSVersionTable.PSVersion) / PnP.PowerShell $((Get-Module PnP.PowerShell).Version)"

Remove-PnPFolder -Name "Q1 2027 Planning" -Folder "SPGuidesProjects" -Recycle -Force

$check = Get-PnPFolder -Url "SPGuidesProjects/Q1 2027 Planning" -ErrorAction SilentlyContinue
"Q1 2027 Planning still exists: $([bool]$check)"</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="2a2b2b" data-has-transparency="false" style="--dominant-color: #2a2b2b;" loading="lazy" decoding="async" width="1013" height="429" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-windows-powershell-5-1-pnp-1-12.avif" alt="Windows PowerShell 5.1 with PnP.PowerShell 1.12.0 deleting the Q1 2027 Planning folder with Remove-PnPFolder" class="wp-image-139969 not-transparent" title="delete folder sharepoint powershell windows powershell 5 1 pnp 1 12" srcset="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-windows-powershell-5-1-pnp-1-12.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-windows-powershell-5-1-pnp-1-12-300x127.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-windows-powershell-5-1-pnp-1-12-768x325.avif 768w" /></figure></div>


<p>It printed PowerShell 5.1.26100.9444 with PnP.PowerShell 1.12.0, a RecycleBinItemId, and <strong>still exists: False</strong>. If 5.1 says the cmdlet isn&#8217;t recognized, see this fix for <a href="https://www.spguides.com/connect-pnponline-is-not-recognized/">Connect-PnPOnline is not recognized</a>.</p>



<h2 class="wp-block-heading">What to check before you delete a folder</h2>



<ul class="wp-block-list">
<li><strong>What&#8217;s inside:</strong> run Get-PnPFolderItem with <code>-Recursive</code> first. A folder delete takes every file and subfolder with it.</li>


<li><strong>Retention labels and policies:</strong> retention settings don&#8217;t apply to folders themselves, but they do apply to the files inside. A file under a retention policy can be deleted, and SharePoint keeps a copy in the hidden Preservation Hold library.</li>


<li><strong>Records:</strong> a file with a locked record label or a regulatory record label can&#8217;t be deleted, so the folder delete fails with an error.</li>


<li><strong>Flows and links:</strong> anything that writes to the folder path, such as a Power Automate flow, breaks once the folder is gone.</li>
</ul>



<p>If you only want the files gone and the folder kept, delete the files instead. This flow shows how to <a href="https://www.spguides.com/microsoft-flow-delete-all-files-in-a-folder/">delete all files in a SharePoint folder with Power Automate</a>.</p>



<h2 class="wp-block-heading">Common errors and how to fix them</h2>



<p>The one I see most is <strong>File Not Found.</strong> It means the folder name or the parent path is wrong. This test deleted a folder that doesn&#8217;t exist, with <code>-ErrorAction Stop</code> so the catch block runs:</p>



<pre class="wp-block-code"><code>try {
    Remove-PnPFolder -Name "Archive 2026" -Folder "SPGuidesProjects" -Recycle -Force -ErrorAction Stop
} catch {
    "Remove failed: $($_.Exception.Message)"
}</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="343536" data-has-transparency="false" style="--dominant-color: #343536;" loading="lazy" decoding="async" width="1013" height="271" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-remove-pnpfolder-file-not-found.avif" alt="Remove-PnPFolder returning File Not Found for the missing Archive 2026 folder" class="wp-image-139967 not-transparent" title="delete folder sharepoint powershell remove pnpfolder file not found" srcset="https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-remove-pnpfolder-file-not-found.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-remove-pnpfolder-file-not-found-300x80.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/delete-folder-sharepoint-powershell-remove-pnpfolder-file-not-found-768x205.avif 768w" /></figure></div>


<ul class="wp-block-list">
<li><strong>&#8220;File Not Found.&#8221;:</strong> check the folder&#8217;s spelling and that <code>-Folder</code> is the parent path with the library&#8217;s URL name, not its display name.</li>


<li><strong>Access denied:</strong> your account needs at least Edit permission on the library to delete folders.</li>


<li><strong>Get-PnPRecycleBinItem returns nothing:</strong> you need to be a Site Collection Administrator, or the item already moved to the second stage (use <code>-SecondStage</code>).</li>


<li><strong>Delete blocked by a label:</strong> a file inside has a record label. Ask your compliance admin before you touch it.</li>
</ul>



<p>For more ready-made admin scripts, browse my <a href="https://www.spguides.com/pnp-powershell-scripts/">PnP PowerShell scripts for SharePoint developers</a> or the <a href="https://www.spguides.com/sharepoint-tutorials/">SharePoint tutorials hub</a>.</p>



<p>If you&#8217;re cleaning up more than folders, these PnP PowerShell tutorials cover the next jobs:</p>



<ul class="wp-block-list spg-related">
<li><a href="https://www.spguides.com/download-all-files-from-sharepoint-using-powershell/">Download All Files from a SharePoint Document Library Using PnP PowerShell</a></li>


<li><a href="https://www.spguides.com/upload-files-to-a-sharepoint-document-library-using-pnp-powershell/">How to Upload Files to a SharePoint Document Library Using PnP PowerShell</a></li>


<li><a href="https://www.spguides.com/get-sharepoint-folder-permissions-using-powershell/">Get SharePoint Folder Permissions Using PnP PowerShell</a></li>


<li><a href="https://www.spguides.com/delete-a-sharepoint-online-site-using-powershell/">How to Delete a SharePoint Online Site Using PowerShell (PnP and SPO)</a></li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-139951000000001" class="rank-math-list-item">
<h3 class="rank-math-question ">Does Remove-PnPFolder delete the files inside the folder?</h3>
<div class="rank-math-answer ">

<p>Yes. It deletes the folder with every file and subfolder in it. In my test, the Archive 2025 folder and its three files went to the site recycle bin together.</p>

</div>
</div>
<div id="faq-question-139951000000002" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I delete a SharePoint folder permanently with PowerShell?</h3>
<div class="rank-math-answer ">

<p>Run Remove-PnPFolder without -Recycle. The <a href="https://pnp.github.io/powershell/cmdlets/Remove-PnPFolder.html" target="_blank" rel="noreferrer noopener">Remove-PnPFolder reference</a> says the folder is then deleted directly, so it can&#8217;t be restored from the recycle bin. Use -Recycle unless you&#8217;re sure.</p>

</div>
</div>
<div id="faq-question-139951000000003" class="rank-math-list-item">
<h3 class="rank-math-question ">How long can I restore a deleted SharePoint folder?</h3>
<div class="rank-math-answer ">

<p>For 93 days from the original delete, across the site recycle bin and the second-stage site collection recycle bin. Microsoft&#8217;s <a href="https://support.microsoft.com/en-us/office/restore-items-in-the-recycle-bin-of-a-sharepoint-site-6df466b6-55f2-4898-8d6e-c0dff851a0be" target="_blank" rel="noreferrer noopener">recycle bin restore guide</a> covers both stages.</p>

</div>
</div>
<div id="faq-question-139951000000004" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I delete a folder with the SharePoint Online Management Shell?</h3>
<div class="rank-math-answer ">

<p>No. The SharePoint Online Management Shell has no folder cmdlets, so folder work needs PnP PowerShell&#8217;s Remove-PnPFolder.</p>

</div>
</div>
<div id="faq-question-139951000000005" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I delete all empty folders in a SharePoint library?</h3>
<div class="rank-math-answer ">

<p>Get the subfolders with Get-PnPFolderItem -ItemType Folder, then call Remove-PnPFolder -Recycle -Force only for folders whose ItemCount is 0. Rerun it to catch folders that become empty.</p>

</div>
</div>
</div>
</div><div class="saboxplugin-wrap" itemtype="http://schema.org/Person" itemscope itemprop="author"><div class="saboxplugin-tab"><div class="saboxplugin-gravatar"><img loading="lazy" decoding="async" src="https://www.spguides.com/wp-content/uploads/2026/05/Bijay-Kumar-Microsoft-MVP-Copy.avif" width="100" height="100" alt="Bijay Kumar Microsoft MVP" itemprop="image" title="Bijay Kumar Microsoft MVP Copy"></div><div class="saboxplugin-authorname"><a href="https://www.spguides.com/author/fewlines4biju/" class="vcard author" rel="author"><span class="fn">Bijay Kumar</span></a></div><div class="saboxplugin-desc"><div itemprop="description"><p>Hey! I’m Bijay Kumar, founder of SPGuides.com and a <a href="https://mvp.microsoft.com/en-US/mvp/profile/b59207f9-3c9a-e411-93f2-9cb65495d3c4" target="_blank" rel="nofollow noopener">Microsoft Business Applications MVP</a> (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 <a href="https://www.youtube.com/@SPGuides?sub_confirmation=1" target="_blank" rel="noopener">training videos</a>. My mission is to help you learn these technologies so you can utilize SharePoint, enhance productivity, and potentially build business solutions along the way.</p>
</div></div><div class="saboxplugin-web "><a href="https://www.enjoysharepoint.com" target="_self" rel="noopener">www.enjoysharepoint.com</a></div><div class="clearfix"></div><div class="saboxplugin-socials sabox-colored"><a title="Linkedin" target="_self" href="https://www.linkedin.com/in/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-linkedin" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x=".3" y=".6" width="500" height="500" fill="#0077b5" /><polygon class="st1" points="500.3 374.1 500.3 500.6 278.2 500.6 141.1 363.6 176.3 220.6 144.3 183 182.4 144.4 250.3 212.7 262.2 212.7 271.7 222 342.2 218.1" /><path class="st2" d="m187.9 363.6h-46.9v-150.9h46.9v150.9zm-23.4-171.5c-15 0-27.1-12.4-27.1-27.4s12.2-27.1 27.1-27.1c15 0 27.1 12.2 27.1 27.1 0 15-12.1 27.4-27.1 27.4zm198.8 171.5h-46.8v-73.4c0-17.5-0.4-39.9-24.4-39.9-24.4 0-28.1 19-28.1 38.7v74.7h-46.8v-151h44.9v20.6h0.7c6.3-11.9 21.5-24.4 44.3-24.4 47.4 0 56.1 31.2 56.1 71.8l0.1 82.9z" /></svg></span></a><a title="Behance" target="_self" href="http://Fewlines4Biju" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-behance" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x=".2" y=".3" width="500" height="500" fill="#1769ff" /><polygon class="st1" points="500.2 297.6 500.2 500.3 280.8 500.3 108.6 327.8 135.7 171.3 233 164.4 285 215.9 303.7 219.9 335 224 293.5 179.9 364.1 162.7" /><path class="st2" d="m222.7 233.1c15.6-7.5 23.8-18.8 23.8-36.4 0-34.7-25.9-43.2-55.7-43.2h-82.2v174.2h84.5c31.7 0 61.4-15.2 61.4-50.6 0-21.8-10.4-37.9-31.8-44zm-75.8-49.8h35.9c13.8 0 26.3 3.9 26.3 19.9 0 14.8-9.7 20.7-23.4 20.7h-38.8v-40.6zm41 114.9h-41v-48h41.7c16.9 0 27.5 7 27.5 24.9 0.1 17.6-12.7 23.1-28.2 23.1zm176.2-118.3h-70.7v-17.2h70.7v17.2zm27.7 86.6c0-37.3-21.8-68.4-61.4-68.4-38.4 0-64.6 28.9-64.6 66.8 0 39.3 24.7 66.2 64.6 66.2 30.1 0 49.7-13.6 59-42.4h-30.6c-3.3 10.8-16.9 16.5-27.4 16.5-20.3 0-31-11.9-31-32.1h91c0.3-2.1 0.4-4.3 0.4-6.6zm-91.3-15.3c1.1-16.6 12.1-26.9 28.8-26.9 17.4 0 26.2 10.2 27.6 26.9h-56.4z" /></svg></span></a><a title="Pinterest" target="_self" href="https://in.pinterest.com/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-pinterest" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x=".3" y=".6" width="500" height="500" fill="#bd081c" /><path class="st1" d="m500.3 310.4v190.2h-227.8l-87.7-88.2 17.2-85.2-43-45s-9-64-7-70 21-49 21-49 40-30 44-30 115.7 9.1 115.7 9.1l167.6 168.1z" /><path class="st2" d="m257.5 115.4c-61.4 0-122.1 40.9-122.1 107.2 0 42.1 23.7 66.1 38.1 66.1 5.9 0 9.3-16.5 9.3-21.2 0-5.6-14.2-17.4-14.2-40.6 0-48.1 36.6-82.3 84-82.3 40.8 0 70.9 23.2 70.9 65.7 0 31.8-12.8 91.4-54.1 91.4-14.9 0-27.7-10.8-27.7-26.2 0-22.6 15.8-44.5 15.8-67.9 0-39.6-56.2-32.4-56.2 15.4 0 10.1 1.3 21.2 5.7 30.4-8.3 35.6-25.1 88.5-25.1 125.2 0 11.3 1.6 22.4 2.7 33.8 2 2.3 1 2 4.1 0.9 30.2-41.3 29.1-49.4 42.7-103.4 7.4 14 26.4 21.6 41.5 21.6 63.6 0 92.1-62 92.1-117.8 0.2-59.5-51.1-98.3-107.5-98.3z" /></svg></span></a><a title="Twitter" target="_self" href="https://twitter.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-twitter" id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M 9.398 6.639 L 16.922 17.361 L 14.922 17.361 L 7.412 6.639 L 9.398 6.639 Z M 24.026 24.026 L -0.026 24.026 L -0.026 -0.026 L 24.026 -0.026 L 24.026 24.026 Z M 19.4 18.681 L 13.807 10.677 L 18.379 5.319 L 16.627 5.319 L 13.014 9.541 L 10.065 5.319 L 4.921 5.319 L 10.187 12.846 L 5.193 18.681 L 6.975 18.681 L 10.985 13.983 L 14.269 18.681 L 19.4 18.681 Z" />
</svg></span></a><a title="Whatsapp" target="_self" href="https://wa.me/+919916854253" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-whatsapp" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500.7" xml:space="preserve"><rect x="-0.9" y="0.2" class="st0" width="500" height="500" fill="#25d366" />
<path class="st1" d="M499.1,304.9v195.3H225.9L118.6,393.4l0.1-0.2l13-35.5l11.2-27.7l9.4-2L138,308.2l-5-15l-4-30l-3-18l7-28  l13-26.1l18-25.9l23-20l37-15h76l41.9,17.1l22.6,22.7c0,0,0,0,0,0L499.1,304.9z" /><path fill="#25d366" class="st2" d="M325.3,286.7c-0.8-1.5-3.1-2.4-6.5-4.1c-3.4-1.7-20.2-10-23.3-11.1c-3.1-1.2-5.4-1.7-7.7,1.7   c-2.3,3.5-8.8,11.1-10.8,13.4c-2,2.3-4,2.6-7.4,0.9c-20.1-10-33.3-17.9-46.5-40.7c-3.5-6,3.5-5.6,10-18.7c1.1-2.3,0.6-4.3-0.3-6   c-0.9-1.7-7.7-18.5-10.5-25.4c-2.8-6.7-5.6-5.7-7.7-5.9c-2-0.1-4.2-0.1-6.5-0.1c-2.3,0-6,0.9-9.1,4.2c-3.1,3.5-12,11.7-12,28.5   c0,16.8,12.3,33.1,13.9,35.4c1.7,2.3,24.1,36.8,58.4,51.6c21.7,9.4,30.2,10.2,41,8.6c6.6-1,20.2-8.3,23-16.3   C326.2,294.9,326.2,288.1,325.3,286.7z M325.3,286.7c-0.8-1.5-3.1-2.4-6.5-4.1c-3.4-1.7-20.2-10-23.3-11.1   c-3.1-1.2-5.4-1.7-7.7,1.7c-2.3,3.5-8.8,11.1-10.8,13.4c-2,2.3-4,2.6-7.4,0.9c-20.1-10-33.3-17.9-46.5-40.7c-3.5-6,3.5-5.6,10-18.7   c1.1-2.3,0.6-4.3-0.3-6c-0.9-1.7-7.7-18.5-10.5-25.4c-2.8-6.7-5.6-5.7-7.7-5.9c-2-0.1-4.2-0.1-6.5-0.1c-2.3,0-6,0.9-9.1,4.2   c-3.1,3.5-12,11.7-12,28.5c0,16.8,12.3,33.1,13.9,35.4c1.7,2.3,24.1,36.8,58.4,51.6c21.7,9.4,30.2,10.2,41,8.6   c6.6-1,20.2-8.3,23-16.3C326.2,294.9,326.2,288.1,325.3,286.7z M364.6,170C364.6,170,364.6,170,364.6,170   c-3.5-4.5-7.3-8.7-11.3-12.7c-25.8-25.9-60.2-40.1-96.7-40.1c-75.4,0-136.8,61.4-136.8,136.8c0,24.1,6.3,47.6,18.2,68.4l-19.4,70.9   l0.1,0l72.4-19c20,10.9,42.4,16.6,65.4,16.6h0.1c75.3,0,138.1-61.4,138.1-136.8C394.6,223.2,383.5,194,364.6,170z M256.5,367.8   c-20.5,0-40.5-5.5-57.9-15.8l-4.1-2.5l-43,11.3l11.5-41.9l-2.7-4.3c-11.4-18.1-17.4-39-17.4-60.5c0-62.7,51-113.7,113.7-113.7   c30.4,0,58.9,11.8,80.3,33.3s34.6,50,34.6,80.4C371.5,316.7,319.2,367.8,256.5,367.8z M318.9,282.6c-3.4-1.7-20.2-10-23.3-11.1   c-3.1-1.2-5.4-1.7-7.7,1.7c-2.3,3.5-8.8,11.1-10.8,13.4c-2,2.3-4,2.6-7.4,0.9c-20.1-10-33.3-17.9-46.5-40.7c-3.5-6,3.5-5.6,10-18.7   c1.1-2.3,0.6-4.3-0.3-6c-0.9-1.7-7.7-18.5-10.5-25.4c-2.8-6.7-5.6-5.7-7.7-5.9c-2-0.1-4.2-0.1-6.5-0.1c-2.3,0-6,0.9-9.1,4.2   c-3.1,3.5-12,11.7-12,28.5c0,16.8,12.3,33.1,13.9,35.4c1.7,2.3,24.1,36.8,58.4,51.6c21.7,9.4,30.2,10.2,41,8.6   c6.6-1,20.2-8.3,23-16.3c2.8-8,2.8-14.8,2-16.3C324.5,285.1,322.3,284.3,318.9,282.6z" /></svg></span></a><a title="Facebook" target="_self" href="https://www.facebook.com/Fewlines4Biju" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-facebook" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x="-.3" y=".3" width="500" height="500" fill="#3b5998" /><polygon class="st1" points="499.7 292.6 499.7 500.3 331.4 500.3 219.8 388.7 221.6 385.3 223.7 308.6 178.3 264.9 219.7 233.9 249.7 138.6 321.1 113.9" /><path class="st2" d="M219.8,388.7V264.9h-41.5v-49.2h41.5V177c0-42.1,25.7-65,63.3-65c18,0,33.5,1.4,38,1.9v44H295  c-20.4,0-24.4,9.7-24.4,24v33.9h46.1l-6.3,49.2h-39.8v123.8" /></svg></span></a><a title="Youtube" target="_self" href="https://www.youtube.com/@SPGuides?sub_confirmation=1" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-youtube" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x=".4" y="-.3" width="500" height="500" fill="#ff0000" /><polygon class="st1" points="500.4 311.3 500.4 499.7 311.8 499.7 139.5 326.7 205 196.6 360.9 172.5" /><path class="st2" d="m371.3 188.8c-2.9-10.9-11.4-19.5-22.3-22.4-19.7-5.3-98.6-5.3-98.6-5.3s-78.9 0-98.6 5.3c-10.9 2.9-19.4 11.5-22.3 22.4-5.3 19.8-5.3 61.1-5.3 61.1s0 41.3 5.3 61.1c2.9 10.9 11.4 19.2 22.3 22.1 19.7 5.3 98.6 5.3 98.6 5.3s78.9 0 98.6-5.3c10.9-2.9 19.4-11.2 22.3-22.1 5.3-19.8 5.3-61.1 5.3-61.1s0-41.3-5.3-61.1zm-146.7 98.6v-75l65.9 37.5-65.9 37.5z" /></svg></span></a></div></div></div>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Create a Folder in SharePoint Online with PowerShell (PnP)</title>
		<link>https://www.spguides.com/create-folder-sharepoint-powershell/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Sun, 08 Nov 2026 14:00:00 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Create Folder SharePoint PowerShell]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=139949</guid>

					<description><![CDATA[To create a folder in SharePoint Online with PowerShell, connect with PnP PowerShell and run Add-PnPFolder with the new folder&#8217;s name and the library&#8217;s URL name. It takes one line, and it scales to hundreds of folders without a single click. In this tutorial, I&#8217;ll walk you through the create folder sharepoint powershell steps I ... <a title="How to Create a Folder in SharePoint Online with PowerShell (PnP)" class="read-more" href="https://www.spguides.com/create-folder-sharepoint-powershell/" aria-label="Read more about How to Create a Folder in SharePoint Online with PowerShell (PnP)">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>To create a folder in SharePoint Online with PowerShell, connect with PnP PowerShell and run <code>Add-PnPFolder</code> with the new folder&#8217;s name and the library&#8217;s URL name. It takes one line, and it scales to hundreds of folders without a single click.</p>



<p>In this tutorial, I&#8217;ll walk you through the <strong>create folder sharepoint powershell</strong> steps I use: install PnP PowerShell, register an Entra app, connect, add a folder, skip existing ones, build nested folders, load a CSV, and handle Windows PowerShell 5.1.</p>



<p>Here&#8217;s the shortest version that works once the module is installed:</p>



<pre class="wp-block-code"><code>Connect-PnPOnline -Url "https://spguides.sharepoint.com/sites/SPGuides" -ClientId "&lt;your-app-client-id&gt;" -DeviceLogin -PersistLogin

Add-PnPFolder -Name "Q4 2026 Reports" -Folder "SPGuidesProjects"</code></pre>



<p>It creates a <strong>Q4 2026 Reports</strong> folder in the root of the <strong>SPGuides Projects</strong> library and returns the new folder object.</p>



<h2 class="wp-block-heading">What you need before you start</h2>



<p>You need the PnP.PowerShell module, an Entra ID app registration for sign-in, and at least Edit (Contribute) rights on the library. If folders are new to you, my guide on <a href="https://www.spguides.com/sharepoint-create-folder/">creating a folder in SharePoint from the browser</a> shows the same result by hand.</p>



<p>I ran every script in this post on 10/25/2026 with PowerShell 7.6.7 and PnP.PowerShell 3.3.0, and again in Windows PowerShell 5.1 with PnP.PowerShell 1.12.0.</p>



<h3 class="wp-block-heading">Step 1: Install PnP PowerShell</h3>



<p>Current PnP PowerShell needs PowerShell 7.4.0 or later. Open PowerShell 7 (pwsh, not the blue Windows PowerShell window) and install the module for your account:</p>



<pre class="wp-block-code"><code>Install-Module PnP.PowerShell -Scope CurrentUser</code></pre>



<p>No admin rights are needed with <code>-Scope CurrentUser</code>. If anything goes wrong, follow this walkthrough to <a href="https://www.spguides.com/install-pnp-powershell/">install PnP PowerShell for SharePoint Online</a>.</p>



<h3 class="wp-block-heading">Step 2: Register your own Entra ID app</h3>



<p>Since September 9, 2024, PnP PowerShell no longer ships a shared sign-in app, so every tenant needs its own app registration. The module can create one for you:</p>



<pre class="wp-block-code"><code>Register-PnPEntraIDAppForInteractiveLogin -ApplicationName "PnP.PowerShell" -Tenant spguides.onmicrosoft.com</code></pre>



<p>Replace the tenant with yours. Creating the app needs the Application Developer role, and a Global Administrator may have to grant admin consent. Copy the Application (client) ID it prints; that&#8217;s your <code>-ClientId</code>.</p>



<h3 class="wp-block-heading">Step 3: Connect to the site</h3>



<p>Connect to the site that holds the library. I use <code>-DeviceLogin</code>, which shows a code to enter at the Microsoft device login page, and <code>-PersistLogin</code> so the next session reuses the token. More sign-in options are in <a href="https://www.spguides.com/connect-to-sharepoint-online-using-powershell/">connecting to SharePoint Online with PowerShell</a>:</p>



<pre class="wp-block-code"><code>$siteUrl  = "https://spguides.sharepoint.com/sites/SPGuides"
$clientId = "&lt;your-app-client-id&gt;"   # your own Entra app's client ID
Connect-PnPOnline -Url $siteUrl -ClientId $clientId -DeviceLogin -PersistLogin

$web = Get-PnPWeb
"Connected to: $($web.Title) ($($web.Url))"
$lib = Get-PnPList -Identity "SPGuides Projects" -Includes RootFolder.Name
"Library display name: $($lib.Title) / URL name: $($lib.RootFolder.Name)</code></pre>



<p>The last two lines confirm the connection: the site title and URL, then the library&#8217;s display name (SPGuides Projects) next to its URL name (SPGuidesProjects).</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="333434" data-has-transparency="false" style="--dominant-color: #333434;" loading="lazy" decoding="async" width="1013" height="330" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-connect-pnponline-device-login-powershell-7.avif" alt="Connect-PnPOnline with -DeviceLogin in PowerShell 7, then the site title and the SPGuides Projects library URL name" class="wp-image-139970 not-transparent" title="create folder sharepoint powershell connect pnponline device login powershell 7" srcset="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-connect-pnponline-device-login-powershell-7.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-connect-pnponline-device-login-powershell-7-300x98.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-connect-pnponline-device-login-powershell-7-768x250.avif 768w" /></figure></div>


<p>If PowerShell says the cmdlet isn&#8217;t recognized, the module didn&#8217;t load in that window; this fix for <a href="https://www.spguides.com/connect-pnponline-is-not-recognized/">Connect-PnPOnline is not recognized</a> covers the usual causes.</p>



<h2 class="wp-block-heading">How do I create a folder with Add-PnPFolder?</h2>



<p>Add-PnPFolder has only two working parameters, which is why it&#8217;s my default for a single folder:</p>



<figure class="wp-block-table"><table><thead><tr><th>Parameter</th><th>Required</th><th>What it takes</th></tr></thead><tbody><tr><td>-Name</td><td>Yes</td><td>The new folder&#8217;s name, for example <code>"Q4 2026 Reports"</code></td></tr><tr><td>-Folder</td><td>Yes</td><td>The parent, as a site-relative URL: <code>"SPGuidesProjects"</code> or <code>"SPGuidesProjects/Clients"</code></td></tr><tr><td>-Connection</td><td>No</td><td>A saved connection from <code>Connect-PnPOnline -ReturnConnection</code></td></tr></tbody></table></figure>



<p>Here&#8217;s the script I ran. The pipe to Select-Object just keeps the output short:</p>



<pre class="wp-block-code"><code># -Folder is the site-relative URL of the parent: the library's URL name, not its display name
Add-PnPFolder -Name "Q4 2026 Reports" -Folder "SPGuidesProjects" |
    Select-Object Name, ServerRelativeUrl, ItemCount</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="313232" data-has-transparency="false" style="--dominant-color: #313232;" loading="lazy" decoding="async" width="1013" height="311" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-add-pnpfolder-create-folder.avif" alt="Add-PnPFolder creating the Q4 2026 Reports folder in the SPGuidesProjects library in PowerShell 7" class="wp-image-139955 not-transparent" title="create folder sharepoint powershell add pnpfolder create folder" srcset="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-add-pnpfolder-create-folder.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-add-pnpfolder-create-folder-300x92.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-add-pnpfolder-create-folder-768x236.avif 768w" /></figure></div>


<p>The output shows the folder&#8217;s server-relative URL and an ItemCount of 0, so it&#8217;s a new, empty folder:</p>



<pre class="wp-block-code"><code>Name            ServerRelativeUrl                                ItemCount
----            -----------------                                ---------
Q4 2026 Reports /sites/SPGuides/SPGuidesProjects/Q4 2026 Reports         0</code></pre>



<p>Refresh the library in the browser and the folder is there. This view also shows the folders the later examples in this post create:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f9f8f7" data-has-transparency="false" style="--dominant-color: #f9f8f7;" loading="lazy" decoding="async" width="1095" height="575" sizes="auto, (max-width: 1095px) 100vw, 1095px" src="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-new-folders-in-document-library.avif" alt="SPGuides Projects document library showing the Clients, Departments, Q1 2027 Planning and Q4 2026 Reports folders created with PowerShell" class="wp-image-139956 not-transparent" title="create folder sharepoint powershell new folders in document library" srcset="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-new-folders-in-document-library.avif 1095w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-new-folders-in-document-library-300x158.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-new-folders-in-document-library-1024x538.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-new-folders-in-document-library-768x403.avif 768w" /></figure></div>


<p>To create a subfolder, point <code>-Folder</code> at the parent folder&#8217;s path. This adds Invoices inside Q4 2026 Reports:</p>



<pre class="wp-block-code"><code>Add-PnPFolder -Name "Invoices" -Folder "SPGuidesProjects/Q4 2026 Reports"</code></pre>



<p>The parent must already exist for Add-PnPFolder. For deeper paths, use Resolve-PnPFolder, covered below. If the library itself is missing, first <a href="https://www.spguides.com/create-document-library-sharepoint-pnp-powershell/">create the document library with PnP PowerShell</a>.</p>



<h2 class="wp-block-heading">Library display name vs URL name: the gotcha</h2>



<p>The <code>-Folder</code> parameter wants a URL path, not the name you see in the left navigation. My library shows as <strong>SPGuides Projects</strong>, but its URL name is <strong>SPGuidesProjects</strong>, with no space.</p>



<p>The classic example is the default library. It&#8217;s called Documents on screen, but its URL name is Shared Documents, so the parent there is <code>-Folder "Shared Documents"</code>.</p>



<p>To find the URL name, run the Get-PnPList line from the connect script, or read it from the library address after the site path. To list every library at once, use this guide to <a href="https://www.spguides.com/get-all-lists-and-libraries-from-sharepoint-online-site-using-pnp-powershell/">get all lists and libraries with PnP PowerShell</a>.</p>



<h2 class="wp-block-heading">Create a folder only if it doesn&#8217;t already exist</h2>



<p>Add-PnPFolder doesn&#8217;t skip existing folders. When I ran it a second time, it threw this error:</p>



<pre class="wp-block-code"><code>A file or folder with the name https://spguides.sharepoint.com/sites/SPGuides/SPGuidesProjects/Q4 2026 Reports already exists.</code></pre>



<p>For scripts that run more than once, check with Get-PnPFolder first. This script shows both behaviors, the failed second Add-PnPFolder and the safe check:</p>



<pre class="wp-block-code"><code>$parent = "SPGuidesProjects"
$name   = "Q4 2026 Reports"

# Running Add-PnPFolder twice throws an error, so check first
try {
    Add-PnPFolder -Name $name -Folder $parent -ErrorAction Stop | Out-Null
} catch {
    "Add-PnPFolder failed: $($_.Exception.Message)"
}

$existing = Get-PnPFolder -Url "$parent/$name" -ErrorAction SilentlyContinue
if ($existing) {
    "Skipped: $($existing.ServerRelativeUrl) already exists"
} else {
    Add-PnPFolder -Name $name -Folder $parent | Out-Null
    "Created: $parent/$name"
}</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="272727" data-has-transparency="false" style="--dominant-color: #272727;" loading="lazy" decoding="async" width="1013" height="551" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-check-folder-exists-before-create.avif" alt="Add-PnPFolder already exists error and a Get-PnPFolder check that skips the existing Q4 2026 Reports folder" class="wp-image-139957 not-transparent" title="create folder sharepoint powershell check folder exists before create" srcset="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-check-folder-exists-before-create.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-check-folder-exists-before-create-300x163.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-check-folder-exists-before-create-768x418.avif 768w" /></figure></div>


<p>The try block printed the error, and the check printed <strong>Skipped: /sites/SPGuides/SPGuidesProjects/Q4 2026 Reports already exists</strong>. The same pattern works for files, lists and sites; see <a href="https://www.spguides.com/check-if-a-folder-exists-in-sharepoint-online-using-pnp-powershell/">checking if a folder, file or list exists in SharePoint</a>.</p>



<h2 class="wp-block-heading">Create nested folders and subfolders with Resolve-PnPFolder</h2>



<p>Resolve-PnPFolder takes a full site-relative path, creates every level that&#8217;s missing, and returns the last folder. If the folder already exists, it just returns it, so there&#8217;s no error to handle:</p>



<pre class="wp-block-code"><code># Resolve-PnPFolder creates every missing level and returns the last folder
Resolve-PnPFolder -SiteRelativePath "SPGuidesProjects/Clients/2026/Website Redesign" |
    Select-Object Name, ServerRelativeUrl

# Run it again: nothing is created, the existing folder comes back (no error)
$again = Resolve-PnPFolder -SiteRelativePath "SPGuidesProjects/Clients/2026/Website Redesign"
"Second run returned: $($again.ServerRelativeUrl)"</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="2d2e2e" data-has-transparency="false" style="--dominant-color: #2d2e2e;" loading="lazy" decoding="async" width="1013" height="410" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-resolve-pnpfolder-nested-folders.avif" alt="Resolve-PnPFolder creating the nested Clients 2026 Website Redesign folders and returning the same folder on a second run" class="wp-image-139958 not-transparent" title="create folder sharepoint powershell resolve pnpfolder nested folders" srcset="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-resolve-pnpfolder-nested-folders.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-resolve-pnpfolder-nested-folders-300x121.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-resolve-pnpfolder-nested-folders-768x311.avif 768w" /></figure></div>


<p>One call created Clients, 2026 and Website Redesign. The second run returned <strong>/sites/SPGuides/SPGuidesProjects/Clients/2026/Website Redesign</strong> without creating anything.</p>



<p>That makes Resolve-PnPFolder the better choice for any script you&#8217;ll rerun. Before you build deep trees, though, read my <a href="https://www.spguides.com/sharepoint-folder-structure/">SharePoint folder structure best practices</a>; metadata columns often beat a fifth folder level.</p>



<h3 class="wp-block-heading">Add-PnPFolder or Resolve-PnPFolder: which one to use?</h3>



<p>Both cmdlets return the same folder object, so the rest of your script doesn&#8217;t care which one made it. The difference is how they treat paths and existing folders:</p>



<figure class="wp-block-table"><table><thead><tr><th>Question</th><th>Add-PnPFolder</th><th>Resolve-PnPFolder</th></tr></thead><tbody><tr><td>Parameters</td><td>-Name and -Folder (parent)</td><td>One -SiteRelativePath</td></tr><tr><td>Parent folder missing</td><td>Fails</td><td>Creates every missing level</td></tr><tr><td>Folder already exists</td><td>Throws an already exists error</td><td>Returns the existing folder</td></tr><tr><td>Best for</td><td>One folder where a duplicate should stop the script</td><td>Reruns, nested paths and bulk loops</td></tr></tbody></table></figure>



<p>I use Add-PnPFolder when a duplicate means something went wrong and I want the script to stop. For everything else, especially scheduled scripts, Resolve-PnPFolder saves the extra Get-PnPFolder check.</p>



<h2 class="wp-block-heading">Create multiple folders from a CSV file</h2>



<p>For bulk folders, keep the names in a CSV so a business user can maintain the list. Mine, saved as folders.csv, has a Department and a Year column:</p>



<pre class="wp-block-code"><code>Department,Year
Finance,2026
Human Resources,2026
Marketing,2026
Sales,2026
IT,2026</code></pre>



<p>This loop builds a path per row and lets Resolve-PnPFolder create each one:</p>



<pre class="wp-block-code"><code>$rows = Import-Csv -Path ".\folders.csv"

foreach ($row in $rows) {
    $path   = "SPGuidesProjects/Departments/$($row.Department)/$($row.Year)"
    $folder = Resolve-PnPFolder -SiteRelativePath $path
    "Ready: $($folder.ServerRelativeUrl)"
}

"{0} folder paths processed" -f $rows.Count</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="2b2b2c" data-has-transparency="false" style="--dominant-color: #2b2b2c;" loading="lazy" decoding="async" width="1013" height="451" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-create-folders-from-csv.avif" alt="PowerShell loop reading folders.csv and creating five department folders with Resolve-PnPFolder" class="wp-image-139959 not-transparent" title="create folder sharepoint powershell create folders from csv" srcset="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-create-folders-from-csv.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-create-folders-from-csv-300x134.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-create-folders-from-csv-768x342.avif 768w" /></figure></div>


<p>It printed five Ready lines, one per department, and <strong>5 folder paths processed</strong>. Because Resolve-PnPFolder skips existing folders, you can add rows later and rerun the same script.</p>



<p>In the browser, the Departments folder now holds Finance, Human Resources, IT, Marketing and Sales, each with a 2026 subfolder:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f9f7f7" data-has-transparency="false" style="--dominant-color: #f9f7f7;" loading="lazy" decoding="async" width="1095" height="625" sizes="auto, (max-width: 1095px) 100vw, 1095px" src="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-department-folders-created-from-csv.avif" alt="Departments folder in the SPGuides Projects library showing the Finance, Human Resources, IT, Marketing and Sales folders created from a CSV" class="wp-image-139960 not-transparent" title="create folder sharepoint powershell department folders created from csv" srcset="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-department-folders-created-from-csv.avif 1095w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-department-folders-created-from-csv-300x171.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-department-folders-created-from-csv-1024x584.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-department-folders-created-from-csv-768x438.avif 768w" /></figure></div>


<p>Once the folders exist, you can <a href="https://www.spguides.com/upload-files-to-a-sharepoint-document-library-using-pnp-powershell/">upload files to the library with PnP PowerShell</a> into each path, or <a href="https://www.spguides.com/get-sharepoint-folder-permissions-using-powershell/">report on folder permissions with PnP PowerShell</a> if departments need different access.</p>



<h2 class="wp-block-heading">Create a folder in Windows PowerShell 5.1</h2>



<p>PnP.PowerShell 3.x doesn&#8217;t load in Windows PowerShell 5.1, because it needs PowerShell 7.4 or later. If a server only has 5.1, the last version that runs there is PnP.PowerShell 1.12.0.</p>



<p>Install that version for your account, then connect. With 1.12.0, <code>-DeviceLogin</code> also needs <code>-Tenant</code>:</p>



<pre class="wp-block-code"><code>Install-Module PnP.PowerShell -RequiredVersion 1.12.0 -Scope CurrentUser
Connect-PnPOnline -Url "https://spguides.sharepoint.com/sites/SPGuides" -ClientId "&lt;your-app-client-id&gt;" -Tenant "spguides.onmicrosoft.com" -DeviceLogin</code></pre>



<p>Without <code>-Tenant</code>, my 5.1 test failed with <strong>Please specify -Tenant with either the tenant id or hostname.</strong> Adding the tenant hostname fixed it on the next try.</p>



<p>After that, both folder cmdlets behave the same as in PowerShell 7:</p>



<pre class="wp-block-code"><code>"PowerShell $($PSVersionTable.PSVersion) / PnP.PowerShell $((Get-Module PnP.PowerShell).Version)"

Add-PnPFolder -Name "Q1 2027 Planning" -Folder "SPGuidesProjects" |
    Select-Object Name, ServerRelativeUrl

Resolve-PnPFolder -SiteRelativePath "SPGuidesProjects/Clients/2027/Intranet Refresh" |
    Select-Object Name, ServerRelativeUrl</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="2a2a2b" data-has-transparency="false" style="--dominant-color: #2a2a2b;" loading="lazy" decoding="async" width="1013" height="451" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-windows-powershell-5-1-pnp-1-12.avif" alt="Windows PowerShell 5.1 with PnP.PowerShell 1.12.0 running Add-PnPFolder and Resolve-PnPFolder" class="wp-image-139961 not-transparent" title="create folder sharepoint powershell windows powershell 5 1 pnp 1 12" srcset="https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-windows-powershell-5-1-pnp-1-12.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-windows-powershell-5-1-pnp-1-12-300x134.avif 300w, https://www.spguides.com/wp-content/uploads/2026/11/create-folder-sharepoint-powershell-windows-powershell-5-1-pnp-1-12-768x342.avif 768w" /></figure></div>


<p>The output confirms PowerShell 5.1.26100.9444 with PnP.PowerShell 1.12.0, then the new Q1 2027 Planning folder and the nested Intranet Refresh folder. Version 1.12.0 doesn&#8217;t get updates anymore, so move to PowerShell 7 when you can.</p>



<h2 class="wp-block-heading">Common errors and fixes</h2>



<ul class="wp-block-list">
<li><strong>&#8220;A file or folder with the name &#8230; already exists&#8221;:</strong> Add-PnPFolder found the folder. Check with Get-PnPFolder first, or use Resolve-PnPFolder.</li>


<li><strong>The parent path isn&#8217;t found:</strong> you probably passed the library&#8217;s display name. Use its URL name (Shared Documents for Documents, SPGuidesProjects here).</li>


<li><strong>Invalid folder names:</strong> SharePoint rejects &#8221; * : &lt; &gt; ? / \ | and leading or trailing spaces. Resolve-PnPFolder names also can&#8217;t end with a period.</li>


<li><strong>Access denied:</strong> your account needs at least Edit (Contribute) permission on the library.</li>


<li><strong>Connect-PnPOnline asks for a ClientId:</strong> register your own Entra app (Step 2) and pass its client ID.</li>
</ul>



<p>What about the SharePoint Online Management Shell? It has no folder cmdlets at all (I checked version 16.0.27424), so folder work is a PnP PowerShell job.</p>



<p>To clean up a test folder, send it to the recycle bin instead of deleting it permanently:</p>



<pre class="wp-block-code"><code>Remove-PnPFolder -Name "Q4 2026 Reports" -Folder "SPGuidesProjects" -Recycle -Force</code></pre>



<p>That keeps a 93-day safety net in the site recycle bin. For more ready-made scripts, browse my <a href="https://www.spguides.com/pnp-powershell-scripts/">PnP PowerShell scripts for SharePoint developers</a> or the <a href="https://www.spguides.com/sharepoint-tutorials/">SharePoint tutorials hub</a>.</p>



<p>If you&#8217;re automating more document library work, these tutorials pick up where this one ends:</p>



<ul class="wp-block-list spg-related">
<li><a href="https://www.spguides.com/download-all-files-from-sharepoint-using-powershell/">Download All Files from a SharePoint Document Library Using PnP PowerShell</a></li>


<li><a href="https://www.spguides.com/move-folders-in-sharepoint-online/">How to Move Folders in SharePoint Online?</a></li>


<li><a href="https://www.spguides.com/create-a-folder-in-sharepoint-from-power-apps/">2 Best Ways to Create a Folder in SharePoint From Power Apps</a></li>


<li><a href="https://www.spguides.com/sharepoint-document-sets-vs-folder/">SharePoint Document Set vs Folder: Which One Should You Use?</a></li>
</ul>



<p>Need to clean up test folders afterwards? My companion post shows how to <a href="https://www.spguides.com/delete-folder-sharepoint-powershell/">delete a folder in SharePoint Online with PnP PowerShell</a>.</p>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-139949000000001" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I create a folder in a SharePoint document library using PowerShell?</h3>
<div class="rank-math-answer ">

<p>Connect with Connect-PnPOnline, then run Add-PnPFolder -Name &#8220;Folder name&#8221; -Folder &#8220;LibraryUrlName&#8221;. The -Folder value is the library&#8217;s URL name, such as Shared Documents for the default Documents library.</p>

</div>
</div>
<div id="faq-question-139949000000002" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I create a folder and subfolders in one command?</h3>
<div class="rank-math-answer ">

<p>Use Resolve-PnPFolder -SiteRelativePath &#8220;Library/Parent/Child&#8221;. It creates every missing level and returns the last folder, and it doesn&#8217;t fail when the folders already exist.</p>

</div>
</div>
<div id="faq-question-139949000000003" class="rank-math-list-item">
<h3 class="rank-math-question ">Why does Add-PnPFolder say the folder already exists?</h3>
<div class="rank-math-answer ">

<p>Add-PnPFolder doesn&#8217;t skip existing folders. Check with Get-PnPFolder -Url &#8220;Library/Folder&#8221; -ErrorAction SilentlyContinue before creating it, or switch to Resolve-PnPFolder.</p>

</div>
</div>
<div id="faq-question-139949000000004" class="rank-math-list-item">
<h3 class="rank-math-question ">Do I need my own app registration to use PnP PowerShell?</h3>
<div class="rank-math-answer ">

<p>Yes. Since September 9, 2024, every tenant needs its own Entra ID app for PnP PowerShell sign-in. The <a href="https://pnp.github.io/powershell/articles/registerapplication.html" target="_blank" rel="noreferrer noopener">PnP app registration guide</a> shows Register-PnPEntraIDAppForInteractiveLogin and the roles it needs.</p>

</div>
</div>
<div id="faq-question-139949000000005" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I create SharePoint folders with the SharePoint Online Management Shell?</h3>
<div class="rank-math-answer ">

<p>No. The SPO Management Shell has no folder cmdlets. Use PnP PowerShell; the <a href="https://pnp.github.io/powershell/cmdlets/Add-PnPFolder.html" target="_blank" rel="noreferrer noopener">Add-PnPFolder reference</a> lists its parameters and examples.</p>

</div>
</div>
</div>
</div><div class="saboxplugin-wrap" itemtype="http://schema.org/Person" itemscope itemprop="author"><div class="saboxplugin-tab"><div class="saboxplugin-gravatar"><img loading="lazy" decoding="async" src="https://www.spguides.com/wp-content/uploads/2026/05/Bijay-Kumar-Microsoft-MVP-Copy.avif" width="100" height="100" alt="Bijay Kumar Microsoft MVP" itemprop="image" title="Bijay Kumar Microsoft MVP Copy"></div><div class="saboxplugin-authorname"><a href="https://www.spguides.com/author/fewlines4biju/" class="vcard author" rel="author"><span class="fn">Bijay Kumar</span></a></div><div class="saboxplugin-desc"><div itemprop="description"><p>Hey! I’m Bijay Kumar, founder of SPGuides.com and a <a href="https://mvp.microsoft.com/en-US/mvp/profile/b59207f9-3c9a-e411-93f2-9cb65495d3c4" target="_blank" rel="nofollow noopener">Microsoft Business Applications MVP</a> (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 <a href="https://www.youtube.com/@SPGuides?sub_confirmation=1" target="_blank" rel="noopener">training videos</a>. My mission is to help you learn these technologies so you can utilize SharePoint, enhance productivity, and potentially build business solutions along the way.</p>
</div></div><div class="saboxplugin-web "><a href="https://www.enjoysharepoint.com" target="_self" rel="noopener">www.enjoysharepoint.com</a></div><div class="clearfix"></div><div class="saboxplugin-socials sabox-colored"><a title="Linkedin" target="_self" href="https://www.linkedin.com/in/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-linkedin" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x=".3" y=".6" width="500" height="500" fill="#0077b5" /><polygon class="st1" points="500.3 374.1 500.3 500.6 278.2 500.6 141.1 363.6 176.3 220.6 144.3 183 182.4 144.4 250.3 212.7 262.2 212.7 271.7 222 342.2 218.1" /><path class="st2" d="m187.9 363.6h-46.9v-150.9h46.9v150.9zm-23.4-171.5c-15 0-27.1-12.4-27.1-27.4s12.2-27.1 27.1-27.1c15 0 27.1 12.2 27.1 27.1 0 15-12.1 27.4-27.1 27.4zm198.8 171.5h-46.8v-73.4c0-17.5-0.4-39.9-24.4-39.9-24.4 0-28.1 19-28.1 38.7v74.7h-46.8v-151h44.9v20.6h0.7c6.3-11.9 21.5-24.4 44.3-24.4 47.4 0 56.1 31.2 56.1 71.8l0.1 82.9z" /></svg></span></a><a title="Behance" target="_self" href="http://Fewlines4Biju" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-behance" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x=".2" y=".3" width="500" height="500" fill="#1769ff" /><polygon class="st1" points="500.2 297.6 500.2 500.3 280.8 500.3 108.6 327.8 135.7 171.3 233 164.4 285 215.9 303.7 219.9 335 224 293.5 179.9 364.1 162.7" /><path class="st2" d="m222.7 233.1c15.6-7.5 23.8-18.8 23.8-36.4 0-34.7-25.9-43.2-55.7-43.2h-82.2v174.2h84.5c31.7 0 61.4-15.2 61.4-50.6 0-21.8-10.4-37.9-31.8-44zm-75.8-49.8h35.9c13.8 0 26.3 3.9 26.3 19.9 0 14.8-9.7 20.7-23.4 20.7h-38.8v-40.6zm41 114.9h-41v-48h41.7c16.9 0 27.5 7 27.5 24.9 0.1 17.6-12.7 23.1-28.2 23.1zm176.2-118.3h-70.7v-17.2h70.7v17.2zm27.7 86.6c0-37.3-21.8-68.4-61.4-68.4-38.4 0-64.6 28.9-64.6 66.8 0 39.3 24.7 66.2 64.6 66.2 30.1 0 49.7-13.6 59-42.4h-30.6c-3.3 10.8-16.9 16.5-27.4 16.5-20.3 0-31-11.9-31-32.1h91c0.3-2.1 0.4-4.3 0.4-6.6zm-91.3-15.3c1.1-16.6 12.1-26.9 28.8-26.9 17.4 0 26.2 10.2 27.6 26.9h-56.4z" /></svg></span></a><a title="Pinterest" target="_self" href="https://in.pinterest.com/fewlines4biju/" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-pinterest" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x=".3" y=".6" width="500" height="500" fill="#bd081c" /><path class="st1" d="m500.3 310.4v190.2h-227.8l-87.7-88.2 17.2-85.2-43-45s-9-64-7-70 21-49 21-49 40-30 44-30 115.7 9.1 115.7 9.1l167.6 168.1z" /><path class="st2" d="m257.5 115.4c-61.4 0-122.1 40.9-122.1 107.2 0 42.1 23.7 66.1 38.1 66.1 5.9 0 9.3-16.5 9.3-21.2 0-5.6-14.2-17.4-14.2-40.6 0-48.1 36.6-82.3 84-82.3 40.8 0 70.9 23.2 70.9 65.7 0 31.8-12.8 91.4-54.1 91.4-14.9 0-27.7-10.8-27.7-26.2 0-22.6 15.8-44.5 15.8-67.9 0-39.6-56.2-32.4-56.2 15.4 0 10.1 1.3 21.2 5.7 30.4-8.3 35.6-25.1 88.5-25.1 125.2 0 11.3 1.6 22.4 2.7 33.8 2 2.3 1 2 4.1 0.9 30.2-41.3 29.1-49.4 42.7-103.4 7.4 14 26.4 21.6 41.5 21.6 63.6 0 92.1-62 92.1-117.8 0.2-59.5-51.1-98.3-107.5-98.3z" /></svg></span></a><a title="Twitter" target="_self" href="https://twitter.com/fewlines4biju" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-twitter" id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  <path d="M 9.398 6.639 L 16.922 17.361 L 14.922 17.361 L 7.412 6.639 L 9.398 6.639 Z M 24.026 24.026 L -0.026 24.026 L -0.026 -0.026 L 24.026 -0.026 L 24.026 24.026 Z M 19.4 18.681 L 13.807 10.677 L 18.379 5.319 L 16.627 5.319 L 13.014 9.541 L 10.065 5.319 L 4.921 5.319 L 10.187 12.846 L 5.193 18.681 L 6.975 18.681 L 10.985 13.983 L 14.269 18.681 L 19.4 18.681 Z" />
</svg></span></a><a title="Whatsapp" target="_self" href="https://wa.me/+919916854253" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-whatsapp" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500.7" xml:space="preserve"><rect x="-0.9" y="0.2" class="st0" width="500" height="500" fill="#25d366" />
<path class="st1" d="M499.1,304.9v195.3H225.9L118.6,393.4l0.1-0.2l13-35.5l11.2-27.7l9.4-2L138,308.2l-5-15l-4-30l-3-18l7-28  l13-26.1l18-25.9l23-20l37-15h76l41.9,17.1l22.6,22.7c0,0,0,0,0,0L499.1,304.9z" /><path fill="#25d366" class="st2" d="M325.3,286.7c-0.8-1.5-3.1-2.4-6.5-4.1c-3.4-1.7-20.2-10-23.3-11.1c-3.1-1.2-5.4-1.7-7.7,1.7   c-2.3,3.5-8.8,11.1-10.8,13.4c-2,2.3-4,2.6-7.4,0.9c-20.1-10-33.3-17.9-46.5-40.7c-3.5-6,3.5-5.6,10-18.7c1.1-2.3,0.6-4.3-0.3-6   c-0.9-1.7-7.7-18.5-10.5-25.4c-2.8-6.7-5.6-5.7-7.7-5.9c-2-0.1-4.2-0.1-6.5-0.1c-2.3,0-6,0.9-9.1,4.2c-3.1,3.5-12,11.7-12,28.5   c0,16.8,12.3,33.1,13.9,35.4c1.7,2.3,24.1,36.8,58.4,51.6c21.7,9.4,30.2,10.2,41,8.6c6.6-1,20.2-8.3,23-16.3   C326.2,294.9,326.2,288.1,325.3,286.7z M325.3,286.7c-0.8-1.5-3.1-2.4-6.5-4.1c-3.4-1.7-20.2-10-23.3-11.1   c-3.1-1.2-5.4-1.7-7.7,1.7c-2.3,3.5-8.8,11.1-10.8,13.4c-2,2.3-4,2.6-7.4,0.9c-20.1-10-33.3-17.9-46.5-40.7c-3.5-6,3.5-5.6,10-18.7   c1.1-2.3,0.6-4.3-0.3-6c-0.9-1.7-7.7-18.5-10.5-25.4c-2.8-6.7-5.6-5.7-7.7-5.9c-2-0.1-4.2-0.1-6.5-0.1c-2.3,0-6,0.9-9.1,4.2   c-3.1,3.5-12,11.7-12,28.5c0,16.8,12.3,33.1,13.9,35.4c1.7,2.3,24.1,36.8,58.4,51.6c21.7,9.4,30.2,10.2,41,8.6   c6.6-1,20.2-8.3,23-16.3C326.2,294.9,326.2,288.1,325.3,286.7z M364.6,170C364.6,170,364.6,170,364.6,170   c-3.5-4.5-7.3-8.7-11.3-12.7c-25.8-25.9-60.2-40.1-96.7-40.1c-75.4,0-136.8,61.4-136.8,136.8c0,24.1,6.3,47.6,18.2,68.4l-19.4,70.9   l0.1,0l72.4-19c20,10.9,42.4,16.6,65.4,16.6h0.1c75.3,0,138.1-61.4,138.1-136.8C394.6,223.2,383.5,194,364.6,170z M256.5,367.8   c-20.5,0-40.5-5.5-57.9-15.8l-4.1-2.5l-43,11.3l11.5-41.9l-2.7-4.3c-11.4-18.1-17.4-39-17.4-60.5c0-62.7,51-113.7,113.7-113.7   c30.4,0,58.9,11.8,80.3,33.3s34.6,50,34.6,80.4C371.5,316.7,319.2,367.8,256.5,367.8z M318.9,282.6c-3.4-1.7-20.2-10-23.3-11.1   c-3.1-1.2-5.4-1.7-7.7,1.7c-2.3,3.5-8.8,11.1-10.8,13.4c-2,2.3-4,2.6-7.4,0.9c-20.1-10-33.3-17.9-46.5-40.7c-3.5-6,3.5-5.6,10-18.7   c1.1-2.3,0.6-4.3-0.3-6c-0.9-1.7-7.7-18.5-10.5-25.4c-2.8-6.7-5.6-5.7-7.7-5.9c-2-0.1-4.2-0.1-6.5-0.1c-2.3,0-6,0.9-9.1,4.2   c-3.1,3.5-12,11.7-12,28.5c0,16.8,12.3,33.1,13.9,35.4c1.7,2.3,24.1,36.8,58.4,51.6c21.7,9.4,30.2,10.2,41,8.6   c6.6-1,20.2-8.3,23-16.3c2.8-8,2.8-14.8,2-16.3C324.5,285.1,322.3,284.3,318.9,282.6z" /></svg></span></a><a title="Facebook" target="_self" href="https://www.facebook.com/Fewlines4Biju" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-facebook" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x="-.3" y=".3" width="500" height="500" fill="#3b5998" /><polygon class="st1" points="499.7 292.6 499.7 500.3 331.4 500.3 219.8 388.7 221.6 385.3 223.7 308.6 178.3 264.9 219.7 233.9 249.7 138.6 321.1 113.9" /><path class="st2" d="M219.8,388.7V264.9h-41.5v-49.2h41.5V177c0-42.1,25.7-65,63.3-65c18,0,33.5,1.4,38,1.9v44H295  c-20.4,0-24.4,9.7-24.4,24v33.9h46.1l-6.3,49.2h-39.8v123.8" /></svg></span></a><a title="Youtube" target="_self" href="https://www.youtube.com/@SPGuides?sub_confirmation=1" rel="nofollow noopener" class="saboxplugin-icon-color"><svg class="sab-youtube" viewBox="0 0 500 500.7" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect class="st0" x=".4" y="-.3" width="500" height="500" fill="#ff0000" /><polygon class="st1" points="500.4 311.3 500.4 499.7 311.8 499.7 139.5 326.7 205 196.6 360.9 172.5" /><path class="st2" d="m371.3 188.8c-2.9-10.9-11.4-19.5-22.3-22.4-19.7-5.3-98.6-5.3-98.6-5.3s-78.9 0-98.6 5.3c-10.9 2.9-19.4 11.5-22.3 22.4-5.3 19.8-5.3 61.1-5.3 61.1s0 41.3 5.3 61.1c2.9 10.9 11.4 19.2 22.3 22.1 19.7 5.3 98.6 5.3 98.6 5.3s78.9 0 98.6-5.3c10.9-2.9 19.4-11.2 22.3-22.1 5.3-19.8 5.3-61.1 5.3-61.1s0-41.3-5.3-61.1zm-146.7 98.6v-75l65.9 37.5-65.9 37.5z" /></svg></span></a></div></div></div>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Create and Apply a SharePoint Site Template with PnP PowerShell</title>
		<link>https://www.spguides.com/sharepoint-site-template-pnp-powershell/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Tue, 27 Oct 2026 09:05:04 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=140377</guid>

					<description><![CDATA[Three new client projects landed on our PMO desk in one week, and each one wanted the same SharePoint site: a Project Tasks list with owners, due dates and status, plus a Project Documents library with a document type and review date. Building that by hand three times is an afternoon gone. A PnP site ... <a title="How to Create and Apply a SharePoint Site Template with PnP PowerShell" class="read-more" href="https://www.spguides.com/sharepoint-site-template-pnp-powershell/" aria-label="Read more about How to Create and Apply a SharePoint Site Template with PnP PowerShell">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>Three new client projects landed on our PMO desk in one week, and each one wanted the same SharePoint site: a Project Tasks list with owners, due dates and status, plus a Project Documents library with a document type and review date. Building that by hand three times is an afternoon gone.</p>



<p>A PnP site template fixes it. You build the lists once on a model site, export them to a template file with PnP PowerShell, and apply that file to every new site in about a minute, sample tasks included.</p>



<p>In this tutorial, we&#8217;ll build a <strong>SharePoint site template PnP PowerShell</strong> workflow from scratch: install the module, create a source site, add the lists, export them with Get-PnPSiteTemplate, pack in sample rows, and apply everything to a brand-new site with Invoke-PnPSiteTemplate. Every command below ran in my tenant on 10/27/2026.</p>



<p>Here&#8217;s what we&#8217;re building. This is SPGuides Project Alpha, a site that didn&#8217;t exist a minute earlier, with the Project Tasks list, all four custom columns and the four starter tasks copied from the template:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f4f3" data-has-transparency="false" style="--dominant-color: #f4f4f3;" loading="lazy" decoding="async" width="1100" height="563" sizes="auto, (max-width: 1100px) 100vw, 1100px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-project-tasks-copied.avif" alt="New SharePoint site with the Project Tasks list and four tasks copied by a PnP site template" class="wp-image-140391 not-transparent" title="pnp site template target site project tasks copied" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-project-tasks-copied.avif 1100w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-project-tasks-copied-300x154.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-project-tasks-copied-1024x524.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-project-tasks-copied-768x393.avif 768w" /></figure></div>


<p>The finished setup has four parts:</p>



<ul class="wp-block-list">
<li><strong>A source site</strong> (/sites/SPGuidesProjectTemplate) that holds the model lists.</li>


<li><strong>A template file</strong> (ProjectSiteTemplate.pnp) exported from that site.</li>


<li><strong>Data rows</strong> inside the template, so every new site starts with the same tasks.</li>


<li><strong>A reusable function</strong> that creates a project site and applies the template in one call.</li>
</ul>



<h2 class="wp-block-heading">Before You Start</h2>



<p>You need PowerShell 7.4 or later and the PnP.PowerShell module (Step 1 checks both). If you&#8217;ve never used it, my guide to <a href="https://www.spguides.com/install-pnp-powershell/">install PnP PowerShell for SharePoint Online</a> covers the setup in more detail.</p>



<ul class="wp-block-list">
<li><strong>Permissions:</strong> creating sites with New-PnPSite needs the SharePoint Administrator or Global Administrator role, or self-service site creation turned on for your tenant.</li>


<li><strong>Entra ID app:</strong> since September 9, 2024, PnP PowerShell requires your own Entra ID app registration. Creating it needs rights to register apps and grant consent (Step 2).</li>


<li><strong>Tools:</strong> any editor works. I used VS Code for the longer scripts and a plain PowerShell 7 window for the short ones.</li>
</ul>



<p>Here&#8217;s the list and library we&#8217;ll put in the template. Internal names matter, because the scripts and the data rows refer to them:</p>



<figure class="wp-block-table"><table><thead><tr><th>List / column</th><th>Type</th><th>Notes</th></tr></thead><tbody><tr><td>Project Tasks</td><td>Custom list (GenericList, 100)</td><td>Title plus the four columns below</td></tr><tr><td>Assigned To (AssignedTo)</td><td>Person</td><td>Who owns the task</td></tr><tr><td>Due Date (DueDate)</td><td>Date only</td><td>Display format set to date only</td></tr><tr><td>Status (TaskStatus)</td><td>Choice</td><td>Not Started, In Progress, Completed; default Not Started</td></tr><tr><td>Priority (Priority)</td><td>Choice</td><td>High, Medium, Low</td></tr><tr><td>Project Documents</td><td>Document library (101)</td><td>URL ProjectDocuments</td></tr><tr><td>Document Type (DocumentType)</td><td>Choice</td><td>Proposal, Contract, Report, Meeting Notes</td></tr><tr><td>Review Date (ReviewDate)</td><td>Date only</td><td>When the document needs a review</td></tr></tbody></table></figure>



<p>And the four starter tasks every new project gets:</p>



<figure class="wp-block-table"><table><thead><tr><th>Task</th><th>Due Date</th><th>Status</th><th>Priority</th></tr></thead><tbody><tr><td>Kickoff meeting with stakeholders</td><td>11/02/2026</td><td>Completed</td><td>High</td></tr><tr><td>Gather requirements</td><td>11/09/2026</td><td>In Progress</td><td>High</td></tr><tr><td>Set up the project site</td><td>11/16/2026</td><td>Not Started</td><td>Medium</td></tr><tr><td>Send the weekly status report</td><td>11/20/2026</td><td>Not Started</td><td>Low</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">How a PnP Site Template Works</h2>



<p>A PnP site template is a file that describes a site: lists, columns, content types, views, pages, navigation, theme and more. Get-PnPSiteTemplate reads a live site and writes that file. Invoke-PnPSiteTemplate reads the file and creates whatever is missing on another site. It&#8217;s the open-source PnP provisioning engine, so there&#8217;s no Microsoft support SLA behind it.</p>



<p>Two rules shape everything that follows. First, a site template doesn&#8217;t create the site: the target site must already exist, which is why every apply step starts with New-PnPSite. Second, it copies structure, not content. List items only travel when you add them as data rows, and files in a library don&#8217;t travel at all with the export we use.</p>



<p><strong>Note:</strong> SharePoint also has &#8220;site templates&#8221; in the browser (Settings &gt; Apply a site template). Those are site designs built from JSON site scripts, which is a different tool.</p>



<p>If that&#8217;s what you want, read how to <a href="https://www.spguides.com/create-custom-site-template-in-sharepoint-online/">create a custom site template with site designs</a>. To find which built-in template a site was created from, see how to <a href="https://www.spguides.com/get-site-template-in-sharepoint-using-powershell/">get the site template ID with PowerShell</a>.</p>



<figure class="wp-block-table"><table><thead><tr><th></th><th>PnP site template</th><th>SharePoint site template (site design)</th></tr></thead><tbody><tr><td>Built with</td><td>PnP PowerShell (Get-PnPSiteTemplate)</td><td>JSON site script + Add-SPOSiteDesign</td></tr><tr><td>Applied by</td><td>An admin running Invoke-PnPSiteTemplate</td><td>Users from the SharePoint UI, or automatically</td></tr><tr><td>File</td><td>.xml or .pnp</td><td>JSON stored in the tenant</td></tr><tr><td>List items</td><td>Yes, as data rows</td><td>No</td></tr><tr><td>Best for</td><td>Copying a real site you already built</td><td>Self-service templates for site owners</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Step 1: Install PnP PowerShell and Check the Version</h2>



<p>Goal: make sure the shell and module are new enough before we touch SharePoint.</p>



<ol class="wp-block-list">
<li>Open <strong>PowerShell 7</strong> (pwsh), not Windows PowerShell 5.1.</li>


<li>If PnP PowerShell isn&#8217;t installed yet, remove the <code>#</code> in front of the Install-Module line and run it once.</li>


<li>Run the version checks.</li>
</ol>



<pre class="wp-block-code"><code># Run once: installs PnP PowerShell for your account (needs PowerShell 7.4 or later)
# Install-Module PnP.PowerShell -Scope CurrentUser

$PSVersionTable.PSVersion.ToString()
Get-Module PnP.PowerShell -ListAvailable |
    Sort-Object Version -Descending |
    Select-Object -First 1 Name, Version</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="302d2e" data-has-transparency="false" style="--dominant-color: #302d2e;" loading="lazy" decoding="async" width="923" height="400" sizes="auto, (max-width: 923px) 100vw, 923px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-check-pnp-powershell-module-version.avif" alt="PowerShell 7.6.7 console showing PnP.PowerShell module version 3.3.0" class="wp-image-140381 not-transparent" title="pnp site template check pnp powershell module version" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-check-pnp-powershell-module-version.avif 923w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-check-pnp-powershell-module-version-300x130.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-check-pnp-powershell-module-version-768x333.avif 768w" /></figure></div>


<p>Already have it? Run <code>Update-Module PnP.PowerShell</code> to get the latest release. If a later command says Connect-PnPOnline isn&#8217;t recognized, the module isn&#8217;t loaded in this shell; this fix for <a href="https://www.spguides.com/connect-pnponline-is-not-recognized/">Connect-PnPOnline is not recognized</a> walks through it.</p>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> The console prints 7.6.7 for PowerShell and PnP.PowerShell 3.3.0 (any PowerShell 7.4+ and a current 3.x module works the same).</p>



<h2 class="wp-block-heading">Step 2: Register Your Entra ID App and Connect</h2>



<p>Goal: get a working sign-in that every later script reuses without asking for the client ID again.</p>



<ol class="wp-block-list">
<li>Run <strong>Register-PnPEntraIDAppForInteractiveLogin</strong> once for your tenant and sign in when the browser opens.</li>


<li>Accept the consent prompt for the permissions the app asks for.</li>


<li>Copy the <strong>client ID</strong> the command prints. That&#8217;s your app&#8217;s ID.</li>
</ol>



<pre class="wp-block-code"><code># Run once per tenant: creates your own Entra ID app for interactive PnP PowerShell sign-ins
# Replace yourtenant with your tenant name
Register-PnPEntraIDAppForInteractiveLogin -ApplicationName "PnP.PowerShell" -Tenant yourtenant.onmicrosoft.com</code></pre>



<p>Now connect. Putting the client ID in <code>$env:ENTRAID_CLIENT_ID</code> means you can leave <code>-ClientId</code> off every Connect-PnPOnline call. <code>-DeviceLogin</code> shows a code you enter at the Microsoft sign-in page, and <code>-PersistLogin</code> caches the token so you don&#8217;t sign in again after a restart. There&#8217;s more on the options in my post on how to <a href="https://www.spguides.com/connect-to-sharepoint-online-using-powershell/">connect to SharePoint Online using PowerShell</a>.</p>



<pre class="wp-block-code"><code># Paste the client ID of the app you registered
$env:ENTRAID_CLIENT_ID = "&lt;your app's client id&gt;"

# Replace yourtenant with your tenant name
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuides" -DeviceLogin -PersistLogin

# Always confirm which site this window is connected to
(Get-PnPWeb).Url</code></pre>



<p>Prefer a browser pop-up over a device code? Use <code>-Interactive</code> with <code>-ClientId</code> instead. The second line below is the fix I needed once when the device login refused to start:</p>



<pre class="wp-block-code"><code># Alternative: sign in through a browser window instead of a device code
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuides" -Interactive -ClientId "&lt;your app's client id&gt;"

# If -DeviceLogin asks for the tenant, add it
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuides" -DeviceLogin -PersistLogin -Tenant yourtenant.onmicrosoft.com</code></pre>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> <code>(Get-PnPWeb).Url</code> returns https://yourtenant.sharepoint.com/sites/SPGuides, the site you just connected to.</p>



<h2 class="wp-block-heading">Step 3: Create the Source Site</h2>



<p>Goal: a clean communication site that will hold the model lists. Keeping the model on its own site means the template never picks up clutter from a real project.</p>



<ol class="wp-block-list">
<li>Stay connected to any site in the tenant (I used /sites/SPGuides).</li>


<li>Run <strong>New-PnPSite</strong> with <code>-Type CommunicationSite</code> and <code>-SiteDesign Blank</code>.</li>


<li>Leave <code>-Wait</code> on so the command returns only when the site is ready.</li>
</ol>



<pre class="wp-block-code"><code># Your Entra ID app's client ID is in $env:ENTRAID_CLIENT_ID, so -ClientId can be left out
# Replace yourtenant with your tenant name
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuides" -DeviceLogin -PersistLogin

# Create the source (model) site that we'll turn into a template
New-PnPSite -Type CommunicationSite `
    -Title "SPGuides Project Template" `
    -Url "https://yourtenant.sharepoint.com/sites/SPGuidesProjectTemplate" `
    -SiteDesign Blank -Wait</code></pre>



<p>Be patient here. With <code>-Wait</code>, New-PnPSite took more than 90 seconds for this site, and the window just sits there until SharePoint finishes. A team site works too, but <code>-Type TeamSite</code> needs <code>-Alias</code> and creates a Microsoft 365 group. My guide to <a href="https://www.spguides.com/create-a-site-in-sharepoint-online-using-pnp-powershell/">create a site in SharePoint Online using PnP PowerShell</a> compares both.</p>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> The command returns https://yourtenant.sharepoint.com/sites/SPGuidesProjectTemplate and the empty site opens in the browser.</p>



<h2 class="wp-block-heading">Step 4: Build the Project Tasks List and Project Documents Library</h2>



<p>Goal: create the two lists and their columns on the source site, exactly as the Before You Start table describes.</p>



<ol class="wp-block-list">
<li>Connect to the new source site.</li>


<li>Create <strong>Project Tasks</strong> with New-PnPList, then add each column with Add-PnPField.</li>


<li>Use Set-PnPField to make the dates date-only (DisplayFormat 0) and to give Status a default of Not Started.</li>


<li>Create the <strong>Project Documents</strong> library with the URL ProjectDocuments and add its two columns.</li>


<li>List the results to confirm both exist.</li>
</ol>



<pre class="wp-block-code"><code># Replace yourtenant with your tenant name
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuidesProjectTemplate" -DeviceLogin -PersistLogin

# 1. Custom list: Project Tasks
New-PnPList -Title "Project Tasks" -Template GenericList -OnQuickLaunch | Out-Null
Add-PnPField -List "Project Tasks" -DisplayName "Assigned To" -InternalName "AssignedTo" -Type User -AddToDefaultView | Out-Null
Add-PnPField -List "Project Tasks" -DisplayName "Due Date" -InternalName "DueDate" -Type DateTime -AddToDefaultView | Out-Null
Set-PnPField -List "Project Tasks" -Identity "DueDate" -Values @{DisplayFormat = 0}   # 0 = date only
Add-PnPField -List "Project Tasks" -DisplayName "Status" -InternalName "TaskStatus" -Type Choice `
    -Choices "Not Started", "In Progress", "Completed" -AddToDefaultView | Out-Null
Set-PnPField -List "Project Tasks" -Identity "TaskStatus" -Values @{DefaultValue = "Not Started"}
Add-PnPField -List "Project Tasks" -DisplayName "Priority" -InternalName "Priority" -Type Choice `
    -Choices "High", "Medium", "Low" -AddToDefaultView | Out-Null

# 2. Custom library: Project Documents
New-PnPList -Title "Project Documents" -Url "ProjectDocuments" -Template DocumentLibrary -OnQuickLaunch | Out-Null
Add-PnPField -List "Project Documents" -DisplayName "Document Type" -InternalName "DocumentType" -Type Choice `
    -Choices "Proposal", "Contract", "Report", "Meeting Notes" -AddToDefaultView | Out-Null
Add-PnPField -List "Project Documents" -DisplayName "Review Date" -InternalName "ReviewDate" -Type DateTime -AddToDefaultView | Out-Null
Set-PnPField -List "Project Documents" -Identity "ReviewDate" -Values @{DisplayFormat = 0}

Get-PnPList | Where-Object Title -like "Project*" | Select-Object Title, ItemCount, BaseTemplate</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f3f1f1" data-has-transparency="false" style="--dominant-color: #f3f1f1;" loading="lazy" decoding="async" width="1084" height="820" sizes="auto, (max-width: 1084px) 100vw, 1084px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-build-list-library-pnp-vscode.avif" alt="VS Code running Build-SourceSite.ps1 to create the Project Tasks list and Project Documents library" class="wp-image-140380 not-transparent" title="pnp site template build list library pnp vscode" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-build-list-library-pnp-vscode.avif 1084w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-build-list-library-pnp-vscode-300x227.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-build-list-library-pnp-vscode-1024x775.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-build-list-library-pnp-vscode-768x581.avif 768w" /></figure></div>


<p>The <code>-InternalName</code> values matter later: data rows and scripts use internal names, not display names. For more column types, see how to <a href="https://www.spguides.com/create-sharepoint-list-and-add-columns-using-pnp-powershell/">create a SharePoint list and add columns using PnP PowerShell</a>, and these <a href="https://www.spguides.com/customize-sharepoint-list-fields-with-pnp-powershell/">tips for customizing SharePoint list fields</a> cover defaults and formats.</p>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> The output table shows Project Documents with BaseTemplate 101 and Project Tasks with BaseTemplate 100, both with ItemCount 0.</p>



<h2 class="wp-block-heading">Step 5: Add Sample Tasks and a Test Document</h2>



<p>Goal: give the source site real data, so we can see exactly what the template carries over.</p>



<ol class="wp-block-list">
<li>Set <code>$me</code> to a user in your tenant (Assigned To needs a real account).</li>


<li>Add the four tasks with <strong>Add-PnPListItem</strong>, using internal names as the keys.</li>


<li>Upload one file, Project-Charter.docx, to Project Documents with <strong>Add-PnPFile</strong> and set its two columns.</li>


<li>Read the tasks back.</li>
</ol>



<pre class="wp-block-code"><code># Replace yourtenant with your tenant name
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuidesProjectTemplate" -DeviceLogin -PersistLogin
$me = "bijay&#64;yourtenant.onmicrosoft.com"   # any user in your tenant

# Sample tasks every new project should start with (noon UTC keeps the date right in US time zones)
$tasks = @(
    @{Title = "Kickoff meeting with stakeholders"; DueDate = "2026-11-02T12:00:00Z"; TaskStatus = "Completed";   Priority = "High"}
    @{Title = "Gather requirements";               DueDate = "2026-11-09T12:00:00Z"; TaskStatus = "In Progress"; Priority = "High"}
    @{Title = "Set up the project site";           DueDate = "2026-11-16T12:00:00Z"; TaskStatus = "Not Started"; Priority = "Medium"}
    @{Title = "Send the weekly status report";     DueDate = "2026-11-20T12:00:00Z"; TaskStatus = "Not Started"; Priority = "Low"}
)
foreach ($t in $tasks) {
    $t.AssignedTo = $me
    Add-PnPListItem -List "Project Tasks" -Values $t | Out-Null
}

# One document in the library (to test what the template copies)
Add-PnPFile -Path ".\Project-Charter.docx" -Folder "ProjectDocuments" `
    -Values @{DocumentType = "Proposal"; ReviewDate = "2026-11-06T12:00:00Z"} | Out-Null

Get-PnPListItem -List "Project Tasks" -Fields Title, TaskStatus, Priority |
    ForEach-Object { [pscustomobject]@{ Task = $_["Title"]; Status = $_["TaskStatus"]; Priority = $_["Priority"] } }</code></pre>



<p>Notice the dates end in <code>T12:00:00Z</code>. My first run used plain &#8220;2026-11-02&#8221; strings, and SharePoint showed 11/1/2026: a date-only string is stored as midnight UTC, which is still the previous day in a US time zone. Noon UTC lands on the right day everywhere in the US.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="1a1817" data-has-transparency="false" style="--dominant-color: #1a1817;" loading="lazy" decoding="async" width="1013" height="185" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-add-sample-tasks-pnp.avif" alt="PowerShell output listing four sample project tasks with Status and Priority" class="wp-image-140379 not-transparent" title="pnp site template add sample tasks pnp" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-add-sample-tasks-pnp.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-add-sample-tasks-pnp-300x55.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-add-sample-tasks-pnp-768x140.avif 768w" /></figure></div>


<p>Here&#8217;s the list in the browser, with Assigned To, Due Date, Status and Priority filled in. For more ways to add items (CSV, batches, every column type), see how to <a href="https://www.spguides.com/add-sharepoint-online-list-items-using-powershell/">add SharePoint Online list items using PnP PowerShell</a>.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f4f3" data-has-transparency="false" style="--dominant-color: #f4f4f3;" loading="lazy" decoding="async" width="1100" height="563" sizes="auto, (max-width: 1100px) 100vw, 1100px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-tasks-list.avif" alt="Source site Project Tasks list with four custom columns and four sample tasks" class="wp-image-140389 not-transparent" title="pnp site template source site project tasks list" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-tasks-list.avif 1100w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-tasks-list-300x154.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-tasks-list-1024x524.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-tasks-list-768x393.avif 768w" /></figure></div>


<p>And the library with Document Type set to Proposal and a Review Date of 11/06/2026:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f5f6f7" data-has-transparency="false" style="--dominant-color: #f5f6f7;" loading="lazy" decoding="async" width="1100" height="413" sizes="auto, (max-width: 1100px) 100vw, 1100px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-documents-library.avif" alt="Source site Project Documents library with Document Type and Review Date columns and one file" class="wp-image-140388 not-transparent" title="pnp site template source site project documents library" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-documents-library.avif 1100w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-documents-library-300x113.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-documents-library-1024x384.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-source-site-project-documents-library-768x288.avif 768w" /></figure></div>


<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> Project Tasks has 4 items (Completed, In Progress and two Not Started) and Project Documents has 1 file, Project-Charter.docx.</p>



<h2 class="wp-block-heading">Step 6: Export the Site Template with Get-PnPSiteTemplate</h2>



<p>Goal: save the two lists to a template file, once as readable XML and once as a .pnp package.</p>



<ol class="wp-block-list">
<li>Create a folder for templates (I used C:\SPGuidesDemo\Templates).</li>


<li>Run <strong>Get-PnPSiteTemplate</strong> with <code>-Handlers Lists</code> and <code>-ListsToExtract</code> naming both lists.</li>


<li>Run it a second time with a .pnp file name.</li>


<li>Compare the two files.</li>
</ol>



<pre class="wp-block-code"><code># Replace yourtenant with your tenant name
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuidesProjectTemplate" -DeviceLogin -PersistLogin
$folder = "C:\SPGuidesDemo\Templates"

# Only the two custom lists, with their columns and views
$lists = "Project Tasks", "Project Documents"

# .xml = readable file, .pnp = package (needed for list data rows)
Get-PnPSiteTemplate -Out "$folder\ProjectSiteTemplate.xml" -Handlers Lists -ListsToExtract $lists -Force
Get-PnPSiteTemplate -Out "$folder\ProjectSiteTemplate.pnp" -Handlers Lists -ListsToExtract $lists -Force

Get-ChildItem "$folder\ProjectSiteTemplate.*" | Select-Object Name, Length, LastWriteTime</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f3f1f1" data-has-transparency="false" style="--dominant-color: #f3f1f1;" loading="lazy" decoding="async" width="1084" height="812" sizes="auto, (max-width: 1084px) 100vw, 1084px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-get-pnpsitetemplate-export-xml-pnp.avif" alt="VS Code running Get-PnPSiteTemplate to export the site template as XML and PNP files" class="wp-image-140383 not-transparent" title="pnp site template get pnpsitetemplate export xml pnp" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-get-pnpsitetemplate-export-xml-pnp.avif 1084w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-get-pnpsitetemplate-export-xml-pnp-300x225.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-get-pnpsitetemplate-export-xml-pnp-1024x767.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-get-pnpsitetemplate-export-xml-pnp-768x575.avif 768w" /></figure></div>


<p>Why two formats? The .xml file is plain text you can read and edit. The .pnp file is a package (a zipped container) that can also hold files and data rows, and Add-PnPDataRowsToSiteTemplate in Step 8 only works with .pnp. In practice I keep the .xml for reading and apply the .pnp.</p>



<p><code>-Handlers Lists</code> limits the export to lists and their columns and views. Without it, Get-PnPSiteTemplate also exports pages, navigation, theme and site settings. The docs warn that naming handlers &#8220;might result in a non-working template&#8221; when something depends on a skipped part, so test it, as we do in Step 9.</p>



<p>These export switches are the ones you&#8217;ll reach for most:</p>



<figure class="wp-block-table"><table><thead><tr><th>Parameter</th><th>What it does</th></tr></thead><tbody><tr><td>-Out</td><td>File to write: .xml, .pnp (package with files) or .md (a readable report, still a work in progress)</td></tr><tr><td>-Handlers</td><td>Only process the named parts, for example Lists, Fields, ContentTypes, Pages, Navigation, Theme, SiteSecurity</td></tr><tr><td>-ListsToExtract</td><td>Export only these lists, by title or ID</td></tr><tr><td>-IncludeAllPages</td><td>Export every page, not only the home page</td></tr><tr><td>-PersistBrandingFiles</td><td>Save theme and branding files into the template</td></tr><tr><td>-IncludeSiteGroups</td><td>Include the site&#8217;s SharePoint groups</td></tr><tr><td>-Configuration</td><td>Read all export settings from a JSON configuration file</td></tr><tr><td>-Force</td><td>Overwrite the file if it already exists</td></tr></tbody></table></figure>



<p>If you want the whole site instead of two lists, drop <code>-Handlers</code> and <code>-ListsToExtract</code> and add the switches you need:</p>



<pre class="wp-block-code"><code># Everything the default handlers export (pages, navigation, theme, lists, columns ...)
Get-PnPSiteTemplate -Out "C:\SPGuidesDemo\Templates\FullSite.pnp" -IncludeAllPages -PersistBrandingFiles -IncludeSiteGroups -Force</code></pre>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> ProjectSiteTemplate.xml is 7,949 bytes and ProjectSiteTemplate.pnp is 4,922 bytes.</p>



<h2 class="wp-block-heading">Step 7: Look Inside the Template File</h2>



<p>Goal: confirm the template holds what we expect before we apply it anywhere. Two minutes here saves a confused hour later.</p>



<ol class="wp-block-list">
<li>Load the .xml file into an <code>[xml]</code> variable.</li>


<li>Walk down to <strong>Lists.ListInstance</strong> and print each list.</li>


<li>Print every field inside each list.</li>
</ol>



<pre class="wp-block-code"><code># Read the exported template and list what it will create
[xml]$template = Get-Content "C:\SPGuidesDemo\Templates\ProjectSiteTemplate.xml"
$lists = $template.Provisioning.Templates.ProvisioningTemplate.Lists.ListInstance

$lists | Select-Object Title, Url, TemplateType | Format-Table

foreach ($list in $lists) {
    $list.Fields.Field | Select-Object @{n = "List"; e = { $list.Title }}, DisplayName, Name, Type
}</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="292826" data-has-transparency="false" style="--dominant-color: #292826;" loading="lazy" decoding="async" width="923" height="677" sizes="auto, (max-width: 923px) 100vw, 923px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-read-template-xml-lists-fields.avif" alt="Console reading the site template XML and listing its lists and seven fields" class="wp-image-140387 not-transparent" title="pnp site template read template xml lists fields" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-read-template-xml-lists-fields.avif 923w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-read-template-xml-lists-fields-300x220.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-read-template-xml-lists-fields-768x563.avif 768w" /></figure></div>


<p>There&#8217;s one surprise: a seventh field, <strong>Checked Out To</strong> (CheckoutUser), which SharePoint adds to every document library on its own. It&#8217;s harmless.</p>



<p>The template records each list&#8217;s TemplateType (100 and 101) and URL, and the file uses the 2022/09 schema with SITEPAGEPUBLISHING#0 as its base template. To read the same details from a live site, see how to <a href="https://www.spguides.com/get-sharepoint-list-column-details-pnp-powershell/">get SharePoint list column details using PnP PowerShell</a>.</p>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> Two lists (Project Documents, Project Tasks) and seven fields: Document Type, Review Date, Checked Out To, Assigned To, Due Date, Status and Priority.</p>



<h2 class="wp-block-heading">Step 8: Add the Sample Tasks to the Template as Data Rows</h2>



<p>Goal: make every new site start with the four tasks, not an empty list.</p>



<ol class="wp-block-list">
<li>Stay connected to the source site (the cmdlet reads the items from there).</li>


<li>Run <strong>Add-PnPDataRowsToSiteTemplate</strong> against the .pnp file.</li>


<li>Name the list and the internal names of the fields to copy.</li>
</ol>



<pre class="wp-block-code"><code># Replace yourtenant with your tenant name
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuidesProjectTemplate" -DeviceLogin -PersistLogin

# Copy the 4 sample tasks into the .pnp package (works with .pnp files only)
Add-PnPDataRowsToSiteTemplate -Path "C:\SPGuidesDemo\Templates\ProjectSiteTemplate.pnp" `
    -List "Project Tasks" -Fields "Title", "AssignedTo", "DueDate", "TaskStatus", "Priority"

Get-Item "C:\SPGuidesDemo\Templates\ProjectSiteTemplate.pnp" | Select-Object Name, Length, LastWriteTime</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="302f2e" data-has-transparency="false" style="--dominant-color: #302f2e;" loading="lazy" decoding="async" width="1013" height="400" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-add-pnpdatarowstositetemplate.avif" alt="Add-PnPDataRowsToSiteTemplate adding Project Tasks rows to the PNP template package" class="wp-image-140378 not-transparent" title="pnp site template add pnpdatarowstositetemplate" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-add-pnpdatarowstositetemplate.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-add-pnpdatarowstositetemplate-300x118.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-add-pnpdatarowstositetemplate-768x303.avif 768w" /></figure></div>


<p>Add-PnPDataRowsToSiteTemplate also takes <code>-Query</code> (a CAML query) to copy only some items, <code>-IncludeSecurity</code> for item permissions, and <code>-TokenizeUrls</code> to swap site URLs for tokens. The rows land with an empty KeyColumn and UpdateBehavior Overwrite, which matters in the test below. To edit or remove rows on a live site instead, see <a href="https://www.spguides.com/sharepoint-list-items-crud-operations-using-pnp-powershell/">SharePoint list item CRUD with PnP PowerShell</a>.</p>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> The .pnp file grows from 4,922 to 5,187 bytes and now contains 4 data rows for Project Tasks.</p>



<h2 class="wp-block-heading">Step 9: Create a New Site and Apply the Template with Invoke-PnPSiteTemplate</h2>



<p>Goal: the payoff. A new project site gets both lists, all columns and the starter tasks.</p>



<ol class="wp-block-list">
<li>Create the target site, SPGuides Project Alpha, with New-PnPSite (again, give it 90 seconds or more).</li>


<li>Connect to the new site&#8217;s URL. This line matters: Invoke-PnPSiteTemplate applies to whatever site you&#8217;re connected to.</li>


<li>Run <strong>Invoke-PnPSiteTemplate</strong> with the .pnp path.</li>


<li>Count the items in both lists.</li>
</ol>



<pre class="wp-block-code"><code># 1. Create the empty target site (replace yourtenant with your tenant name)
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuides" -DeviceLogin -PersistLogin
$newSite = New-PnPSite -Type CommunicationSite -Title "SPGuides Project Alpha" `
    -Url "https://yourtenant.sharepoint.com/sites/SPGuidesProjectAlpha" -SiteDesign Blank -Wait
"Created: $newSite"

# 2. Connect to the new site and apply the template
Connect-PnPOnline -Url $newSite -DeviceLogin -PersistLogin
Invoke-PnPSiteTemplate -Path "C:\SPGuidesDemo\Templates\ProjectSiteTemplate.pnp"

# 3. Check the result
Get-PnPList | Where-Object Title -like "Project*" | Select-Object Title, ItemCount</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f3f1f0" data-has-transparency="false" style="--dominant-color: #f3f1f0;" loading="lazy" decoding="async" width="1084" height="812" sizes="auto, (max-width: 1084px) 100vw, 1084px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-pnpsitetemplate-new-site.avif" alt="VS Code creating SPGuides Project Alpha and applying the template with Invoke-PnPSiteTemplate" class="wp-image-140385 not-transparent" title="pnp site template invoke pnpsitetemplate new site" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-pnpsitetemplate-new-site.avif 1084w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-pnpsitetemplate-new-site-300x225.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-pnpsitetemplate-new-site-1024x767.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-pnpsitetemplate-new-site-768x575.avif 768w" /></figure></div>


<p>Project Tasks arrived with all four tasks. Project Documents arrived with Document Type and Review Date, but no files. That&#8217;s expected: the Lists handler exports list structure and data rows, not library documents.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f5f6f5" data-has-transparency="false" style="--dominant-color: #f5f6f5;" loading="lazy" decoding="async" width="1100" height="500" sizes="auto, (max-width: 1100px) 100vw, 1100px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-library-columns-no-files.avif" alt="Target site Project Documents library with copied columns and no files" class="wp-image-140390 not-transparent" title="pnp site template target site library columns no files" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-library-columns-no-files.avif 1100w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-library-columns-no-files-300x136.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-library-columns-no-files-1024x465.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-target-site-library-columns-no-files-768x349.avif 768w" /></figure></div>


<p>If new projects also need starter documents, upload them after the apply step. My guide to <a href="https://www.spguides.com/upload-files-to-a-sharepoint-document-library-using-pnp-powershell/">upload files to a SharePoint document library using PnP PowerShell</a> has a ready loop for a whole folder, and you can pull the originals off the model site with a script to <a href="https://www.spguides.com/download-all-files-from-sharepoint-using-powershell/">download all files from a document library</a>.</p>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> The output shows Created: https://yourtenant.sharepoint.com/sites/SPGuidesProjectAlpha, Project Documents 0 items and Project Tasks 4 items.</p>



<h2 class="wp-block-heading">Test the Template: Apply It Twice to the Same Site</h2>



<p>Real templates get re-applied: someone adds a column to the model, and you push the update to existing sites. So let&#8217;s run the exact same Invoke against Alpha a second time.</p>



<pre class="wp-block-code"><code># Replace yourtenant with your tenant name
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuidesProjectAlpha" -DeviceLogin -PersistLogin

# Apply the same template a second time
Invoke-PnPSiteTemplate -Path "C:\SPGuidesDemo\Templates\ProjectSiteTemplate.pnp"

Get-PnPList | Where-Object Title -like "Project*" | Select-Object Title, ItemCount</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="302f2d" data-has-transparency="false" style="--dominant-color: #302f2d;" loading="lazy" decoding="async" width="1013" height="400" sizes="auto, (max-width: 1013px) 100vw, 1013px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-again-duplicate-rows.avif" alt="Second Invoke-PnPSiteTemplate run doubling Project Tasks to eight items" class="wp-image-140384 not-transparent" title="pnp site template invoke again duplicate rows" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-again-duplicate-rows.avif 1013w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-again-duplicate-rows-300x118.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-invoke-again-duplicate-rows-768x303.avif 768w" /></figure></div>


<p>The lists and columns were fine, because the engine skips what already exists. The data rows weren&#8217;t: Project Tasks went from 4 to 8 items, with every task twice. With an empty KeyColumn, the engine has no way to tell that a row is already there, so it adds it again.</p>



<p><strong>Important:</strong> never re-apply a template that has data rows to a live site until you&#8217;ve set a key column, or every run adds another copy of the starter tasks.</p>



<h2 class="wp-block-heading">Stop Duplicate Rows With KeyColumn and UpdateBehavior</h2>



<p>Same template, one change: tell the data rows which column identifies a row. I removed the four extra tasks first, then ran this:</p>



<ol class="wp-block-list">
<li>Load the .pnp file with <strong>Read-PnPSiteTemplate</strong>.</li>


<li>Find the Project Tasks list in <code>$template.Lists</code>.</li>


<li>Set <code>DataRows.KeyColumn</code> to Title and <code>DataRows.UpdateBehavior</code> to Overwrite.</li>


<li>Write it back with <strong>Save-PnPSiteTemplate</strong>, then apply it again.</li>
</ol>



<pre class="wp-block-code"><code>$path = "C:\SPGuidesDemo\Templates\ProjectSiteTemplate.pnp"

# Tell the engine how to recognize a row that already exists
$template = Read-PnPSiteTemplate -Path $path
$tasks = $template.Lists | Where-Object Title -eq "Project Tasks"
$tasks.DataRows.KeyColumn = "Title"            # match rows on the Title column
$tasks.DataRows.UpdateBehavior = "Overwrite"   # update the match instead of adding a copy
Save-PnPSiteTemplate -Template $template -Out $path -Force

# Apply it again to the same site: still 4 tasks, no duplicates (replace yourtenant)
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/SPGuidesProjectAlpha" -DeviceLogin -PersistLogin
Invoke-PnPSiteTemplate -Path $path
Get-PnPList | Where-Object Title -like "Project*" | Select-Object Title, ItemCount</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f3f2f1" data-has-transparency="false" style="--dominant-color: #f3f2f1;" loading="lazy" decoding="async" width="1084" height="812" sizes="auto, (max-width: 1084px) 100vw, 1084px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-datarows-keycolumn-fix-duplicates.avif" alt="Setting KeyColumn Title and UpdateBehavior Overwrite so re-applying keeps four tasks" class="wp-image-140382 not-transparent" title="pnp site template datarows keycolumn fix duplicates" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-datarows-keycolumn-fix-duplicates.avif 1084w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-datarows-keycolumn-fix-duplicates-300x225.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-datarows-keycolumn-fix-duplicates-1024x767.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-datarows-keycolumn-fix-duplicates-768x575.avif 768w" /></figure></div>


<p>Now the engine matches each row on Title. Overwrite means a match gets updated, not copied.</p>



<p>In my re-apply, the existing tasks took the Due Date values from the template, and no copies were added. Pick a column whose values are unique in that list. If you ever need to clean up duplicates by hand, see how to <a href="https://www.spguides.com/remove-all-items-from-a-sharepoint-online-list-using-pnp-powershell/">delete all items from a SharePoint list using PnP PowerShell</a>.</p>



<p class="spg-check" style="background-color:#EEF8F1;border-left:4px solid #1E8E5A;padding:12px 16px;border-radius:6px"><strong>Checkpoint:</strong> After the second apply, Project Tasks still has 4 items.</p>



<h2 class="wp-block-heading">Make It Reusable: One Command per New Project Site</h2>



<p>Typing three commands for every project gets old. Wrap them in a function that takes the project name, builds the URL, creates the site, applies the template and reports back:</p>



<ol class="wp-block-list">
<li>Paste the function into your session (or a .ps1 file you dot-source).</li>


<li>Call it with <code>-ProjectName "Beta"</code>.</li>


<li>Read the summary it prints.</li>
</ol>



<pre class="wp-block-code"><code>function New-SPGuidesProjectSite {
    param(
        [Parameter(Mandatory)] [string] $ProjectName,   # e.g. "Beta"
        [string] $TemplatePath = "C:\SPGuidesDemo\Templates\ProjectSiteTemplate.pnp",
        [string] $Tenant = "https://yourtenant.sharepoint.com"   # replace yourtenant
    )
    $url = "$Tenant/sites/SPGuidesProject$ProjectName"

    Connect-PnPOnline -Url "$Tenant/sites/SPGuides" -DeviceLogin -PersistLogin
    $site = New-PnPSite -Type CommunicationSite -Title "SPGuides Project $ProjectName" `
        -Url $url -SiteDesign Blank -Wait

    Connect-PnPOnline -Url $site -DeviceLogin -PersistLogin
    Invoke-PnPSiteTemplate -Path $TemplatePath

    $lists = Get-PnPList | Where-Object Title -like "Project*"
    [pscustomobject]@{ Site = $site; Lists = ($lists.Title -join ", "); Tasks = ($lists | Where-Object Title -eq "Project Tasks").ItemCount }
}

New-SPGuidesProjectSite -ProjectName "Beta" | Format-List</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f2f2" data-has-transparency="false" style="--dominant-color: #f4f2f2;" loading="lazy" decoding="async" width="1084" height="860" sizes="auto, (max-width: 1084px) 100vw, 1084px" src="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-new-spguidesprojectsite-function.avif" alt="Reusable New-SPGuidesProjectSite function creating the SPGuides Project Beta site" class="wp-image-140386 not-transparent" title="pnp site template new spguidesprojectsite function" srcset="https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-new-spguidesprojectsite-function.avif 1084w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-new-spguidesprojectsite-function-300x238.avif 300w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-new-spguidesprojectsite-function-1024x812.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/10/pnp-site-template-new-spguidesprojectsite-function-768x609.avif 768w" /></figure></div>


<p>That one call created /sites/SPGuidesProjectBeta with Project Documents and Project Tasks and the 4 starter tasks. To check later which sites you&#8217;ve created this way, list them with the script that shows how to <a href="https://www.spguides.com/get-all-sharepoint-online-sites-using-pnp-powershell/">get all SharePoint Online sites using PnP PowerShell</a>.</p>



<h2 class="wp-block-heading">Invoke-PnPSiteTemplate Options Worth Knowing</h2>



<p>Our build only needed <code>-Path</code>, but these switches cover most of what people ask about. The cheat sheet of <a href="https://www.spguides.com/powershell-sharepoint-commands/">SharePoint PowerShell commands for SPO and PnP</a> lists the related cmdlets.</p>



<figure class="wp-block-table"><table><thead><tr><th>Parameter</th><th>What it does</th></tr></thead><tbody><tr><td>-Path</td><td>The .xml or .pnp file; a .pnp can also be a URL to a file in a document library</td></tr><tr><td>-Parameters</td><td>Hashtable of values for {parameter:Key} tokens in the template</td></tr><tr><td>-Handlers / -ExcludeHandlers</td><td>Apply only some parts, or everything except some parts</td></tr><tr><td>-ClearNavigation</td><td>Remove the target site&#8217;s existing navigation nodes first</td></tr><tr><td>-IgnoreDuplicateDataRowErrors</td><td>Suppress errors when a data row already exists</td></tr><tr><td>-ResourceFolder</td><td>Folder with files the template refers to (for .xml templates)</td></tr><tr><td>-TemplateId</td><td>Pick one template when the file contains several</td></tr></tbody></table></figure>



<p>Parameters let one template serve many projects. Put a token in the XML, then pass the value when you apply it:</p>



<pre class="wp-block-code"><code># In the template XML, a value can be a token such as {parameter:ProjectName}
Invoke-PnPSiteTemplate -Path "C:\SPGuidesDemo\Templates\ProjectSiteTemplate.xml" -Parameters @{"ProjectName" = "Alpha"}</code></pre>



<h2 class="wp-block-heading">What a PnP Site Template Copies (and What It Doesn&#8217;t)</h2>



<p>From the tests above and the docs, here&#8217;s the short version. If you only need to move one list between sites, the <a href="https://www.spguides.com/copy-items-from-one-list-to-another-in-sharepoint-online-using-pnp-powershell/">copy items from one list to another with PnP PowerShell</a> approach is lighter than a template.</p>



<figure class="wp-block-table"><table><thead><tr><th>Item</th><th>Copied?</th><th>How</th></tr></thead><tbody><tr><td>Lists, libraries, columns, views</td><td>Yes</td><td>Lists handler (default export)</td></tr><tr><td>List items</td><td>Only as data rows</td><td>Add-PnPDataRowsToSiteTemplate on a .pnp file</td></tr><tr><td>Files in a library</td><td>No (tested: 0 files)</td><td>Upload them after applying</td></tr><tr><td>Pages</td><td>Home page by default</td><td>-IncludeAllPages for the rest</td></tr><tr><td>Theme and branding files</td><td>Theme yes; files only on request</td><td>-PersistBrandingFiles</td></tr><tr><td>SharePoint groups</td><td>Only on request</td><td>-IncludeSiteGroups</td></tr><tr><td>The site itself</td><td>No</td><td>Create it first, or use a tenant template</td></tr></tbody></table></figure>



<p>That last row is the big one. A <strong>site template</strong> applies to an existing site. A <strong>tenant template</strong> can also create site collections through a Sequence and is applied with Invoke-PnPTenantTemplate, which is the route for provisioning many sites from one file.</p>



<h2 class="wp-block-heading">Make It Production-Ready</h2>



<ul class="wp-block-list">
<li><strong>Keep the template in source control.</strong> The .xml diff shows exactly what changed between versions.</li>


<li><strong>Set a KeyColumn on every list with data rows</strong> before anyone re-applies the template.</li>


<li><strong>Check the connection before every apply.</strong> Print <code>(Get-PnPWeb).Url</code>; a script that <a href="https://www.spguides.com/check-if-a-folder-exists-in-sharepoint-online-using-pnp-powershell/">checks whether a site or list exists</a> first is safer still.</li>


<li><strong>Test on a throwaway site.</strong> Apply to a new test site, check the lists, then delete it.</li>


<li><strong>Name sites consistently</strong> (SPGuidesProject + name), so the function builds predictable URLs.</li>
</ul>



<h2 class="wp-block-heading">Troubleshooting</h2>



<p>These are the problems I actually hit while building this, with the fix for each:</p>



<ul class="wp-block-list">
<li><strong>&#8220;Default authentication request failed. Please specify the -Tenant parameter&#8221;:</strong> Connect-PnPOnline -DeviceLogin couldn&#8217;t work out the tenant. Add <code>-Tenant yourtenant.onmicrosoft.com</code> and it connects.</li>


<li><strong>The template went to the wrong site:</strong> when a Connect-PnPOnline call fails, the PREVIOUS connection stays active, so the next Invoke runs against the old site. Always check <code>(Get-PnPWeb).Url</code> before applying.</li>


<li><strong>Due dates one day early:</strong> &#8220;2026-11-02&#8221; is stored as midnight UTC, which a US time-zone site shows as 11/1/2026. Write &#8220;2026-11-02T12:00:00Z&#8221; (noon UTC) instead.</li>


<li><strong>New-PnPSite seems frozen:</strong> with -Wait it can take more than 90 seconds. Let it finish; don&#8217;t start the apply step early.</li>


<li><strong>Duplicate list items after re-applying:</strong> set DataRows.KeyColumn and UpdateBehavior Overwrite (see above).</li>


<li><strong>Library arrives empty:</strong> templates don&#8217;t carry documents. Upload the files after applying.</li>
</ul>



<p><strong>Note:</strong> if Connect-PnPOnline still fails after adding -Tenant, go back to the app registration from Step 2. The app needs consent before anyone can sign in through it.</p>



<h2 class="wp-block-heading">Where to Take It Next</h2>



<ul class="wp-block-list">
<li><strong>Add a document library structure:</strong> extend the model with folders using the steps to <a href="https://www.spguides.com/create-document-library-sharepoint-pnp-powershell/">create a document library with folders using PnP PowerShell</a>, then export again.</li>


<li><strong>Let site owners apply it themselves:</strong> for a no-PowerShell option, rebuild the lists as a site design (linked above), and browse the <a href="https://www.spguides.com/sharepoint-tutorials/">SharePoint tutorials hub</a> for the site setup guides around it.</li>


<li><strong>Clean up test sites:</strong> remove the throwaway sites you created while testing with the script to <a href="https://www.spguides.com/delete-a-sharepoint-online-site-using-powershell/">delete a SharePoint Online site using PowerShell</a>.</li>
</ul>



<p>If you&#8217;re automating more of your SharePoint setup, these PnP PowerShell guides pair well with this one:</p>



<ul class="wp-block-list spg-related">
<li><a href="https://www.spguides.com/pnp-powershell-scripts/">PnP PowerShell Scripts for SharePoint Developers</a></li>


<li><a href="https://www.spguides.com/get-all-lists-and-libraries-from-sharepoint-online-site-using-pnp-powershell/">How to Get All Lists and Libraries from a SharePoint Online Site Using PnP PowerShell</a></li>


<li><a href="https://www.spguides.com/get-sharepoint-list-guid-powershell/">How to Get a SharePoint List GUID Using PowerShell (PnP and Get-SPWeb)</a></li>


<li><a href="https://www.spguides.com/get-sharepoint-site-members-using-powershell/">How to Get SharePoint Site Members Using PowerShell (PnP and SPO)</a></li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-140377000000001" class="rank-math-list-item">
<h3 class="rank-math-question ">Does Get-PnPSiteTemplate copy the files in a document library?</h3>
<div class="rank-math-an