Create a folder structure in SharePoint Online using PowerShell PnP (from file shares)
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 (@alistek) made within a few minutes, and help me tremendously (I have his permission for sharing). Thanks Adam! 😊
ℹ️ Here, I’m not going to debate if folders are good or bad. Each requirement is different for each project so… It always depends!
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
Results would look like this: (this is an extract of my cloned repos on Github)
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
- -NoTypeInformation to 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
- And poof!
We have what we want! 😎
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: (change to YOUR TENANT NAME, SITE, and DOC LIBRARY)
- 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
-ErrorAction
to ‘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
#Connect- to SPO site Connect-PnPOnline -Url 'https://<YOUR_TENANT_NAME>.sharepoint.com/sites/<YOUR_SITE>' #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' #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 Green 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++ } }
Now let’s run this, and observe the magic 🥳
Thank you Adam! 👍
Thanks,
Trying this with Import- CSV file. The folders it’s creating is not what is expected.
EG, top level folder Community.
But it is creating it as @{Community=Community
Subfolders then have “}” at the end of them.
eg Events} instead of Events
Seems the header field is causing he issue.
I worked around this but using Get-Content., making sure to remove the first row with “FullName” in it.
$Paths = Get-Content “Path to CSV file”