Recently, I had to create a SharePoint folder structure extracted from a file share. I only wanted the structure, and not the files.
So I had the local part to extract what I needed, and then I thought “sure I’ll use PowerShell PnP with a foreach loop, and done!”
Well, surprisingly (or not), that didn’t work as expected 😟 So after a few hours trying and trying again, I decided to ask for help… (Never be afraid to ask for help, because you’ll 100% learn something!)
In this blog post, I’m going to share the script that Adam Listek (Twitter: @alistek) made within a few minutes, and help me tremendously (I have his permission for sharing). Thanks Adam!
Extract structure from File Shares
The below one-liner allowed me to export the root folder as well as all nested folders like I needed.
Get-ChildItem -Path '\\<SharedDrive>\RootFolder' -Recurse -Directory | select FullName | Export-Csv -Path "C:\users\$env:USERNAME\Desktop\myFolderStructure.csv" -NoTypeInformation

So… I’m using… :
- Recurse parameter to get the nested folders
- Directory parameter to get only the folders (not the files)
- FullName property will give me the full path
NoTypeInformationto avoid too much info at the top of the excel file…
Now that we have the original structure, let’s modify it a little bit.
- Ctrl+F on the keyboard,
- Replace tab,
- Enter the location for the root folder (C:\Github\ in my case),
- Replace All

This is what we need:

Create the structure in SharePoint Online
Now’s the time to make it like a breeze… I’ll spare you my hair pulling theories, which were too complex when simplicity should have been thought through…
I will however, explain the logic and requirements behind it.
On this extract above, we have folders alone (i.e.: Dev-SPFarm-2016), and we have nested folders (i.e.: vPS365\docs)
Folders alone are not such a big deal to create. But nested folders… they need the ‘root’ to be created before right? 😉
So here we go. This is what we need to do:
- Connect to the site collection (where the library is)
- Declare the structure
- If not too long, it’s easy to insert in the script.
- if too long, just replace by Import-Csv -Path <Your_Csv_file>
- Declare the document library (which you’ll find in the URL – not the display name with any space you may have created it!)
- And then we start creating the structure.
- Set the
$i = 0 - Loop through each item,
- Condition if ‘root’ or subfolder
- Set the
-ErrorActionto‘SilentlyContinue’if you don’t want to see red when the ‘root’ folder is already created - A bit of artefacts with
-ForegroundColor - Use the Add-PnPFolder cmdlet for creation
- Insert
$i++to continue as long as we have folders to create!
- Set the
Script for MacOS users
#Connect- to SPO site
Connect-PnPOnline -Url 'https://<TENANT-NAME>.sharepoint.com/sites/<YOUR-SITE>' -Credentials <YOUR-CREDS>
#array of folders
$paths = @(
"Dev-SPFarm-2016"
"O365-Data-Retriever-Tool"
"O365_EXO_PowerShellScripts"
"O365_LCSING_PowerShellScripts"
"O365_SPO_PowerShellScripts"
"O365_SPO_Reports"
"SharePointServer_PowerShellScripts"
"vPS365"
"Dev-SPFarm-2016\screenshots"
"O365-Data-Retriever-Tool\github"
"O365-Data-Retriever-Tool\assembly"
"O365-Data-Retriever-Tool\Images"
"O365-Data-Retriever-Tool\resources"
"O365-Data-Retriever-Tool\screenshots"
"O365-Data-Retriever-Tool\github\ISSUE_TEMPLATE"
"O365_SPO_Reports\Get-SPOSiteInfo"
"O365_SPO_Reports\Get-SPOSiteInfo\screenshots"
"vPS365\docs"
"vPS365\functions"
)
#Declare the Document Library
$topLevelRoot = "TargetLibrary" ### No forward slash in the front ###
Write-Host "Destination library is: " $topLevelRoot -f Cyan
#Create the structure
$paths | Foreach-Object {
$split = $_ -Split "\\"
$root = $null
$previousRoot = $null
$i = 0
$split | Foreach-Object {
If ($i -ne 0) {
$previousRoot = $root
$root = (Join-Path $root $_)
$current = $_
Write-Host "Sub-Folder Name: $current | Library: $previousRoot" -ForegroundColor Magenta
Add-PnPFolder -Name $current -Folder $previousRoot -ErrorAction SilentlyContinue
}
Else {
$root = (Join-Path $topLevelRoot $_)
$current = $_
Write-Host "Folder Name: $current | Library: $topLevelRoot" -ForegroundColor Cyan
Add-PnPFolder -Name $current -Folder $topLevelRoot -ErrorAction SilentlyContinue
}
$i++
}
}
Script for Windows users
#Connect- to SPO site
Connect-PnPOnline -Url 'https://<TENANT-NAME>.sharepoint.com/sites/<YOUR-SITE>' -Credentials <YOUR-CREDS>
#array of folders
$paths = @(
"Dev-SPFarm-2016"
"O365-Data-Retriever-Tool"
"O365_EXO_PowerShellScripts"
"O365_LCSING_PowerShellScripts"
"O365_SPO_PowerShellScripts"
"O365_SPO_Reports"
"SharePointServer_PowerShellScripts"
"vPS365"
"Dev-SPFarm-2016\screenshots"
"O365-Data-Retriever-Tool\github"
"O365-Data-Retriever-Tool\assembly"
"O365-Data-Retriever-Tool\Images"
"O365-Data-Retriever-Tool\resources"
"O365-Data-Retriever-Tool\screenshots"
"O365-Data-Retriever-Tool\github\ISSUE_TEMPLATE"
"O365_SPO_Reports\Get-SPOSiteInfo"
"O365_SPO_Reports\Get-SPOSiteInfo\screenshots"
"vPS365\docs"
"vPS365\functions"
)
#Declare the Document Library
$topLevelRoot = "TargetLibrary" ### No forward slash in the front ###
Write-Host "Destination library is: " $topLevelRoot -f Cyan
#Create the structure
$paths | Foreach-Object {
$split = $_ -Split "\\"
$root = $null
$previousRoot = $null
$i = 0
$split | Foreach-Object {
If ($i -ne 0) {
$previousRoot = $root
$root = ($root + ‘/’ + $_)
$current = $_
Write-Host "Sub-Folder Name: $current | Library: $previousRoot" -ForegroundColor Magenta
Add-PnPFolder -Name $current -Folder $previousRoot -ErrorAction SilentlyContinue
}
Else {
$root = ($topLevelRoot + ‘/’ + $_)
$current = $_
Write-Host "Folder Name: $current | Library: $topLevelRoot" -ForegroundColor Cyan
Add-PnPFolder -Name $current -Folder $topLevelRoot -ErrorAction SilentlyContinue
}
$i++
}
}
Thanks for reading! 🙂