Last time I wrote about the magic at work when OneDrive performs its Known Folder Move (KFM). This time, I’ll show you how to get your overalls dirty and tinker under the hood of this beast. Buckle up, it’s going to be a wild ride.
Also, please be kind to me in the comments. This is my first real adventure into the depths of SharePoint and its PowerShell Possibilities tm.
I’ve grown up a little… Since embarking on my first adventures in blogging, with the gracious help of Oktay, I’ve created my own blog: threeisacloud.tech. This post was re-published there (albeit in slightly redacted form). The version here will stay, forever reminding me of my roots. |
Did you know?
MS Word marks Onedrive and Sharepoint (without a capital D or P) as a spelling error. Nice one, Microsoft!
You might want to read the previous post
You can still read it here.
Here’s the gist of it: OneDrive uses a SharePoint List called “Documents” (conveniently named to avoid any confusion). All folders, including the ‘Known’ ones, live inside this List. To keep track of which folders are ‘Known’, some metadata is published inside the List’s properties. OneDrive clients use this metadata during KFM which can cause issues in multi-lingual environments.
To use, or not to use
Let’s just make this clear upfront: if you need to use this information, you’re probably suffering from some technical debt. KFM is a wonderful thing, but you really should have been using it from the get-go.
So, when would this blog come in handy? Imagine someone moved (let’s say 10.000) home folders (with English names) to OneDrive, using SharePoint Migration Tool (SPMT). Now imagine someone else having to redirect a Dutch endpoints Known Folders to those OneDrive folders.
Oh, and someone else is not allowed to change folder names or the display language because the fate of the universe depends on it.
Now, say hello to someone else (I’ll wave back👋).
Not Plug ‘n’ Play
You can’t really expect this kind of configuration to be available in a GUI, can you? So, as always, PowerShell comes to the rescue. Apparently, the library of choice is PnP.PowerShell (I’m once again trusting the judgement of Patrick Lamber here 😊). The PnP-part doesn’t stand for “Plug and Play”, as I soon found out (it stands for Patterns and Practices).
Installation of modules is easy these days (just agree to everything like you always would) and it’s probably a good idea to import it as well.
Install-Module -Name PnP.PowerShell; Import-Module -Name PnP.PowerShell;
Basically…
I started by figuring out the basics. Apparently, you need a SharePoint Url to connect to (duh), and some credentials to authenticate. I used the -Interactive
switch because my Conditional Access policies don’t want to deal with basic sign-ins.
$url = 'https://notreally-my.sharepoint.com/personal/n_scheffers_alsonotreally_nl'; Connect-PnPOnline -Url $url -Interactive;
Remember that classmate that always did just about everything in your school projects? PnP.PowerShell has one of those. It’s called the Context. It’s the fabric that ties everything together.
Let’s get one of those. It’ll come in handy in the future.
$siteCtx = Get-PnPContext;
There you go; that’s the basics done. I took me way longer to read the docs than to write this code.
Also, respect this classmate. They helped get your career started.
Get all the things
Let’s move on to getting some information out of this thing. Remember that OneDrive always makes a List called “Documents”?
$documentsList = Get-PnPList -Identity 'Documents';
What I really, really want is Spice Girls reference. But seriously, I’m only interested in the RootFolder
of this list. Unfortunately, at first, only some basic properties are loaded, and the metadata is far from basic. Let’s ask our favorite classmate to get some more.
Notice the ExecuteQuery()
method: it instructs the Context to perform any outstanding operations, albeit read- or write-operations.
$siteCtx.Load($l.RootFolder.Properties); $siteCtx.ExecuteQuery();
Although this particular classmate is awesome, you always need to tell them what you want, and then tell them to go do it. That’s what the ExecuteQuery()
method is for, it basically yells “GO!”. We’ll see that a few more times.
Now, we can get our hands on all that juicy metadata:
$documentsList.RootFolder.Properties['vti_DesktopFolderGuid']; $documentsList.RootFolder.Properties['vti_DocumentsFolderGuid']; $documentsList.RootFolder.Properties['vti_CameraRollFolderGuid']; $documentsList.RootFolder.Properties['vti_ScreenShotsFolderGuid']; $documentsList.RootFolder.Properties['vti_PhotosFolderGuid'];
Falter folder
Next up is figuring out how I’m going to get to this Guid
I keep reading about. Can’t be too hard, can it now?
It’s not. At least not if I tell you a little secret first.
Even though this metadata is named vti_SomethingFolderGuid
, it doesn’t really want to know the generic GUID
property you’ll find in each SharePoint-folder. What it does want, is the UniqueId
-property. Yes, they are different. No, using the wrong one doesn’t throw an error. It simply doesn’t do anything. Took me a while to figure that out.
First, I tried this:
$documentsFolderGuid = (Get-PnPFolder 'Desktop').UniqueId;
It gets the job done. Sometimes. But every so often, the UniqueId
-property is empty.
Next, I tried this:
$documentsFolderGuid = (Get-PnPFolder '/personal/n_scheffers_alsonotreally_nl/Documents/Documen-ta-daa').UniqueId;
That’s the ServerRelativeUrl, right there, so we aren’t leaving anything to chance. Doesn’t give me the UniqueId
-property though. It exists but is always empty.
While I was trying to be funny, I somehow used an (almost) real Spanish word for my folder name.
Then there’s that classmate, saving the day again:
$documentsFolder = (Get-PnPFolder '/personal/n_scheffers_alsonotreally_nl/Documents/Documen-ta-daa'); $siteCtx.Load($documentsFolder.ListItemAllFields); $siteCtx.ExecuteQuery(); $documentsFolderGuid = $documentsFolder.ListItemAllFields.FieldValues['UniqueId'];
Ready? Set.
You can set these properties by simply assigning data to them. Remember to cast your fresh Guid
to a string. For example:
$documentsList.RootFolder.Properties['vti_DocumentsFolderGuid'] = $documentsFolderGuid.ToString();
You then need to mark the RootFolder
as ready-to-update and ask the Context to commit it.
$documentsList.RootFolder.Update(); $siteCtx.ExecuteQuery();
Go!
If you put all of this together, you’ll get something like this:
$siteUrl = 'https://notreally-my.SharePoint.com/personal/n_scheffers_alsonotreally_nl'; $documentsFolderName = 'Documen-ta-daa'; Import-Module -Name PnP.PowerShell; Connect-PnPOnline -Url $siteUrl -Interactive; $siteCtx = Get-PnPContext; $documentsList = Get-PnPList -Identity 'Documents'; if ($siteCtx -and $documentsList) { $documentsListUrl = $documentsList.RootFolder.ServerRelativeUrl; $documentsFolderUrl = "$($documentsListUrl)/$($documentsFolderName)"; $documentsFolder = Get-PnPFolder -Url $documentsFolderUrl; $siteCtx.Load($documentsFolder.ListItemAllFields); $siteCtx.ExecuteQuery(); if ($documentsFolder) { $documentsFolderGuid = $documentsFolder.ListItemAllFields.FieldValues['UniqueId']; if ($documentsFolderGuid) { $documentsList.RootFolder.Properties['vti_DocumentsFolderGuid'] ` = $documentsFolderGuid.ToString(); $documentsList.RootFolder.Update(); $siteCtx.ExecuteQuery(); } } }
Keep in mind that this doesn’t magically change your existing OneDrive deployments. The mapping is performed by KFM, and KFM is only executed once per deployment.
As I said in the beginning: this will only be of interest when you already have data in OneDrive, and you must ensure the correct Known Folder mapping is applied on additional endpoints.
I hope you enjoyed this two-part series. I sure enjoyed writing it. If only I had some more free time to spend on this kind of stuff.
[…] Part 2 is now available. I’ll get up close and personal with this metadata and explain how to make it fall in line. […]
[…] This was originally published on December 20th, 2022 as a guest post for allthingscloud.blog. It’s been (slightly) redacted and placed here as its permanent home. You can still read the original here. […]