Update 24/02/17 – you can do this with a standard roaming profile, it would seem, as long as you use the fully-patched 1607 update version of Windows 10. See https://www.youtube.com/watch?v=R4R4QlExLsU&t=3s for a demonstration of this in action.
FTAs (file type associations) seem to be rapidly becoming one of my biggest bugbears. Microsoft had this functionality nailed-down in Windows 7, allowing you to control it via GPO on a per-user basis and roam it from session to session, but Windows 8.x and Windows 10 took it away and replaced it with a horrid mish-mash of device settings and forced resets. I’ve tried a couple of times to sort this out, but it never worked satisfactorily, especially with regard to browsers. I’m not going to go over all the details of the changes again – you can read about them here and here if you want a recap – but this is the current state of affairs.
Citrix UPM can manage this, to a degree, but it bloats the profile horribly and also chokes on browser FTAs, for some reason. The Citrix method is covered here, but I’m after something that does everything, if possible.
On Windows 10, you can’t effectively roam FTAs from machine to machine, making non-persistent VDI environments and even standard roaming estates a nightmare. We had a full 11% of support calls during a Windows 10 rollout related to FTA issues. We want this to work, because it worked before and I don’t see why it shouldn’t be able to work again. So here’s my goal:
I have a bunch of Windows 10 desktops in a roaming environment, and I want users to be able to:-
- Get their default FTAs for PDFs set to Foxit Reader, and their default browser set to Google Chrome
- Change the FTAs to a different application (Sumatra for PDFs, and Internet Explorer for browsing), and have this association persist when they log on to another machine
This obviously has a few caveats:-
- We need to make sure that the applications are available on all devices for the FTAs to persist. We’ve got Chrome and the two PDF readers installed in the base image, so that’s cool.
- We need to remove cached copies of profiles when the users log out. You can do this in many ways – we’re using a mandatory profile to enable this.
- You will need to do some privilege elevation to allow this. We’ve used AppSense Application Manager, but we will discuss other ways to achieve this.
Setting the defaults for new users
When a user logs on to a Windows 10 machine that doesn’t have a profile currently assigned (local, roaming, or third-party), the user’s default file type associations are generated from an XML file called OEMDefaultAssociations that sits in %WINDIR%\system32.
For starters, we’re going to create a version of this file with our own default associations in it (for goal #1 above).
Let’s first copy the OEMDefaultAssociations.xml file somewhere in case you break it
Log on as a test user, and make your default associations. I’ve set Chrome as the default for “everything” using the Default Programs applet in Control Panel…
…and I’ve set the PDF association by using the “Open With” context menu from Explorer (see below)…
…which leaves me with my default associations configured just as I want them…
…so now let’s put them into the default area.
Open up a command prompt with admin privileges, and run the following command
C:\windows\system32\dism.exe /Online /Export-DefaultAppAssociations:%TMP%\AppAssoc.xml
The file can be exported anywhere you want, I’ve just used the %TMP% folder as an example.
Now, open up the file you’ve just exported in Notepad (not IE!), and also open up the OEMDefaultAssociations.xml in Notepad as well (you may need to launch Notepad as an administrator to be able to save this file). Copy the entire text from the file you exported using the dism command and paste it into the OEMDefaultAssociations.xml file, replacing the entire text in there with your exported XML
Save the OEMDefaultAssociations.xml file and now log in as a new user. You should see instead of associating with Edge (which is the normal defaults), the user will have associations with Foxit and Chrome.
OK, so that’s how to set your own defaults. So far so good, nice and easy. I know Microsoft have a GPO which allows you to override this default XML file, but that GPO will apply to all users every time they log in, so it isn’t exactly flexible (or suitable, really). Now for the complicated bit – enabling some roaming.
Roaming your FTAs from device to device
So, the key part is this OEMDefaultAssociations.xml file. If a user doesn’t have a profile (which you normally don’t when you log in on a new machine), it will generate one from this file. How can we leverage this for some roaming capability?
Firstly, the damn thing isn’t editable by ordinary users. So make it editable, either through a script, or (like we did), just editing the permissions in the base image to give users Full Control (actually all they need is Modify, but I am a fan of sledgehammers and nuts) 🙂
Next comes the tricky bit. What we are going to do is, when the user logs off, capture his current settings using the dism command and export them out to an XML file in his home drive. The problem is – you need administrative access to run dism.exe. Running it as SYSTEM or another user won’t work, because then it will export their settings, not the user’s. How can we make a user an admin just for this one command?
The easy answer (from my perspective) is to use AppSense Application Manager’s privilege elevation feature to give the user administrative access when they launch dism.exe. You will need to set up the User Privileges item like below
I’ve highlighted “Install as trusted owner” because it is vital this option is selected. Also, if you’ve got an Application Manager configuration running in Restricted mode you will also need to add dismhost.exe as an Allowed Item
How could you do this if you didn’t have Application Manager? Somehow, you need a script that elevates the user to an admin, executes the dism command, and then de-elevates again. This isn’t easy, because even if you add the user to an elevated group their token still needs updating on-the-fly. You could maybe use CPAU from JoeWare (http://www.joeware.net/freetools/tools/cpau/) but I’m not sure. Really, a proper privilege escalation tool like AppSense AM, RES or Scense or the like would be the ideal candidate for this.
So now we have set up the dism command so that when the user runs it, they get temporary admin privileges, we need to call the command when the user logs out, and then copy the resulting XML file into their home drive. Interestingly, when I tried to do this via AppSense Environment Manager in the Logoff trigger it never executed, so I had to resort to using a GPO Logoff Script to call a quick batch file to do it. Not sure why EM wouldn’t let me do it – a debug indicated everything was working fine. Here’s the batch I called from my Logoff Script:-
%SystemRoot%\System32\Dism.exe /Online /Export-DefaultAppAssociations:%TMP%\AppAssoc.xml
copy %TMP%\AppAssoc.xml \\UKSLDC003\FileStore\HomeDrives\%USERNAME%
Naturally substitute paths as required for your environment.
So, we now want to get our user to make some changes to his FTAs. We will change the browser to IE through Default Programs, and change the PDF association to Sumatra.
When the user logs out, it should now save a file called AppAssoc.xml into his home drive with his new associations in them.
Now, how do we get this into his profile when he logs on to a new machine?
The key is, we overwrite the OEMDefaultAssociations.xml file (which we allowed everyone to edit) with the content from the file we exported at logoff. We can do this easily through an AppSense Environment Manager Action, a Group Policy Preferences item, a script – whichever is easiest for you. The key is the copied file must be called OEMDefaultAssociations.xml but have the content from the AppAssoc.xml we saved off to the network. We’ve shown the AppSense EM Action below
So now we simply need to fire up another Windows 10 device and log our test user onto it, and see what FTAs he gets…
Woot…we have persistence!!!
So we’re achieving this by overwriting the OEMDefaultAssociations.xml file in the %WINDIR%\system32 folder. But that means if another user comes along and logs on after this user, and this next user is logging on for the very first time, they wouldn’t get the system defaults – they’d get the previous user’s settings. Which isn’t good. How do we mitigate against this?
Easy enough – when a new user is created, we simply bung a copy of the original defaults file we created (in the first section) into their home drive and call it AppAssoc.xml. So when they log in for the first time, it will overwrite any “leftover” entries in the OEMDefaultAssociations.xml file that other users may have written. Either that, or you could call some script or AppSense EM Action to check if the user has a file in their home drive – if they don’t have one (because they haven’t logged on before), simply grab a copy of the original OEMDefaultAssociations.xml file you created and copy it into the %WINDIR%\system32 folder.
This works great – assuming that you are removing the user’s profile copy at logoff, and that you can do the privilege escalation for the dism command at logoff. The profile removal is necessary to ensure that the OEMDefaultAssociations.xml file is correctly used to populate the new profile. The privilege escalation is key (otherwise you can’t save your settings!), and though it’s dead easy if you have a tool like AppSense Application Manager in use, if you don’t, the solution may need some extra work (I’d be very interested in seeing or testing any scripts that manage to do it OK)
There are some minor security changes to make, to be fair. I don’t really think allowing users to run dism elevated is a huge security issue, although in some environments it might get raised as a problem. There’s also the editing allowed on the XML file to consider, but again, I can’t see it being a huge blocker.
Some people always throw in there “why don’t you just use User Profile Disks, or FSLogix Profile Containers?” FSLogix is one of my favourite pieces of tech, and is for the most part quite reliable, but used for FTAs in this fashion it always seems to produce a hybrid based around the profile contents and the OEMDefaultAssociations file, as if it is getting a bit confused with what is in the mounted Registry and what the endpoint reckons it should inject into it. Sometimes it is right, sometimes it changes slightly. I’m currently working with roaming FTAs using FSLogix and UPD and I should have an updated article out soon, but for this moment, the method covered in this article is the only method I seem to be able to get it to be 100% reliable for non-persistent environments.
I will be putting a video together later on today to show this in action because it’s a fairly complicated setup – this will be embedded into this article as soon as I send it live.
Please send all feedback via comments, Twitter or email – I’d be very interested in hearing how this scales for other people and wider application sets. Always test before sending live! 🙂