The Problem Defined
- You have frontline workers who you want to use OneDrive to ease the pain of computer transfers, and perform pseudo backup functionality.
- These frontline may have stretches of limited connectivity.
- Organizationally or otherwise it’s not possible to deal with the consequences of a user not having access to files in their desktop or documents folder.
- You can’t disable Files on Demand because they need to sync or create a shortcuts to internal Sharepoint sites. Without Files on Demand the whole Sharepoint library will be downloaded
- Training users to pin (“Always keep on this device”) their files, while the best solution, is not good enough due to #3.
With that Said how do we fix that?
As a starting point I came across Florian Salzmann’s solution which is to check the Pinned and Unpinned file attributes on all files using attrib.exe and set them if needed. You can read more about his solution here. While this is a perfectly fine solution I’ve never been a big fan of wrapping binaries in regex for powershell scripts unless absolutely necessary. Can we do this in native PowerShell?
The Dive 🤿
⚠️ This section is going to be about the discovery process of working with extended attributes, and writing the Proactive Remediation. if all you want is the completed PR skip to the bottom.
I sought out more or less the same solution but to use native methods to achieve the needed functionality. If setting an attribute threw an error I wanted to be able to make a note of that without having to do additional text processing. We know that OneDrive pinning is controlled by the Pinned and Unpinned attributes let’s explore how to set attributes using PowerShell. A bit of background research shows us that file attributes are simply a property on file(FileInfo)/Folder(DirectoryInfo) objects that can be returned using Get-Item.
Knowing that, let’s create a test environment with a file that is pinned (always keep on this device), a file that is unpinned (free up space) and a file that is neither.Let’s now try to get the attributes.Well that’s a bit unexpected. Looking back at our previous research the file attributes are contained in the FileAttributes EnumThere is No Pinned or Unpinned attribute defined. What’s going on here?Let’s take a step back. What are file attributes anyway? File attributes are essentially metadata flags that are represented in a bitmask format. You can think of a bitmask as a virtual DIP switch.Each switch is represented as a power of 2 (2,4,16) (AKA a binary BIT) and as such the entire state can be represented by a single number that is the addition of all switches.
Archive (32) + ReparsePoint (1024) = 1056
or 000000000000000000010000100000
.
Yet the attributes Pinned or Unpinned are not defined in our FileAttributes Enum. Only some of our virtual DIP switch settings are defined in this class.
After way too much time spent googling, I found some documentation to help us reach our goal.
- Our extended attribute definitions
- Enumeration types as bit flags
- Some powershell examples of attribute manipulation from ss64
Following the enum types documentation, we can create an enum with the [flags] argument. Let’s define our own enum containing all possible attributes and enter this into our PS console:
|
|
Now when we get our file attributes, let’s recast them to our custom enum:
Great!, but why does the Unpinned file have so many attributes? Well to cut to the chase most of these other attributes are attributes OneDrive manages. Offline notates the file is off of disk, RecallOnDataAccess notates that there is a time penalty to opening this file… After OneDrive picks up on the Pinned/Unpinned it will set/unset these flags as it moves them on or off the disk. These details might be interesting to examine at another time but it’s not what we are here for. We just need to set the Pinned and Unpinned attributes and the OneDrive client will take care of the rest.How can we do that?
We can do that by manipulating the bitmask through bitwise operations.
- Bitwise AND or
-band
: Our comparison operator. By itself on a bitmask of powers of 2, comparison against a power of 2 will always be 0 ( is unset) or the same power ( is set). - Bitwise OR or
-bor
: Our on toggle. If an attribute isn’t set this will set it. It won’t turn an attribute off if it is on. - Bitwise XOR or
-bxor
: Our Flip flop operator. If an attribute is on this will turn it off and if it is off it will turn it on. - Bitwise NOT or
-band
: Invert our bits, combined with bitwise and this can be a off toggle.
If your unfamiliar with bitwise operators and would like to learn how they work. I recommend checking out this video by Alex Hyett, Or watching Ben Eater’s Series on building an 8-bit computer from scratch to learn more about low level programming in general.
You may be wondering why windows uses bitmasks for file system attributes and it’s a matter of efficiency. Bitmask are very efficient in speed and size and this matters when this applies to every single file and folder on the system.
Let’s look at some examples. First let’s explore unsetting the Unpinned attribute.
As you can see from our examples -band
and -bnot
together only unset an attribute.
Now let’s set the Pinned attribute.
As you can see from the example bitwise OR always sets an unset attribute and has no effect on a set attribute.
This is pretty convenient as -band
,-bnot
and -band
are the only operators that we need. We can completely ignore the more troublesome -bxor
which would toggle attributes expectantly if we were not careful to check the state ahead of time.
Writing the Remediation
Let’s first start with our detection script.
Step 1:
Get the location of the Desktop & Documents folder and make sure KFM is on. If KFM is not on then nothing to do, exit cleanly.
|
|
Step 2:
The preconditions have been met so let’s define our attribute Enum.
|
|
Step 3:
Let’s check if Desktop & Documents are unpinned.
|
|
Step 4:
If Desktop and Documents are not pinned/unpinned exit for remediation.
else check for each subfolder/file. If there are any folders/files not pinned/unpinned, exit for remediation.
|
|
Now our Remediation Script:
Step 1:
So like in our detection script, we will check for KFM and set our attributes.
|
|
Step 2:
Then for each folder: Desktop, Documents. We hold the current folder object in a temporary variable. This is so if we throw an exception we can act on it. The $psitem
or $_
pipe variable gets overwritten by the exception in a try/catch statement.
We then unset the Unpinned attribute, then set the Pinned attribute.
|
|
Step 3:
Since existing subfiles and folders won’t inherit the attributes, although new files/folders will, we will need to iterate through each subfolder/file and set them individually.
We want to continue on an error, but throw an error on each file we couldn’t process at the end.
|
|
Setting up the PR
You can get the completed PR from my github here
Let’s set it up shall we?
Step 1:
In the Intune Admin Center navigate to Reports > Endpoint Analytics > Proactive Remediations.
Name your PR.Step 3:
Upload the detection and remediation scripts. Be sure to set “Run this script using the logged-on credentials”.
Step 4:
Setup your assignments. When your setting up your assignments you have a few choices. You could apply this to all users or just your frontline workers, you could apply the PR only once as a gentle suggestion or you can set it hourly and force it.
That’s It! 🙂
Alec Weber Contributor
Alec is an Intune engineer with a focus on Azure and automation