FTAs have been a problem for a long time. Let’s put the subject to bed.
I have done a number of articles on handling File Type Associations (from hereon in referred to as FTAs) on Windows versions from 8 upwards. Judging by the number of emails and comments I get about the subject, it’s clear that it hasn’t been satisfactorily resolved. So we are going to write up everything on this topic and finally set up some testing that will show, once and for all, how to deal with them.
What are FTAs?
We’re all familiar with FTAs – they are the program that opens a particular file type extension. Files with the .doc or .docx extension commonly open in Microsoft Word. Files with the .txt extension commonly open in Notepad, but you can use many other things, WordPad, Notepad++, even Microsoft Excel. Each user normally has a set of FTAs stored in their Registry, defining the program that will be used when you double-click a particular file type. If you wanted to open the file type in a different program (for instance, open a file with the .xml extension in Notepad instead of the default of IE), you can right-click on the file and choose Open With.
FTAs have a relationship between the extension, the filetype and the handler. If we take text files as an example:-
Why do we have a filetype (also known as a ProgId)? Well, if you associated an extension directly with a handler, you’d only ever be able to deal with files with that extension via that handler. By specifying a filetype/ProgId, you can then move on to the Registry data associated with the filetype and from there extend into handling different user-defined actions via different handlers. So you can call a printing function from the Print command, an editing function from the Edit command, etc.
When a user logs on to a system, the various Registry keys are queried that deal with FTAs and the associations are set. The system will look for user-defined FTAs in HKCU first, if this fails, it will check HKLM for default settings, and if this also fails, it will check HKCU for default settings. The following diagram shows the order of progression when a user is logging on and checking for FTA data (in this case for the .pdf extension), and how the handler is set and then used.
FTAs are a bit murky, but this is (to the best of my ability!) how I can ascertain that they behave. If anything in here is inaccurate, please let me know in the comments and we can modify!
Why deploy per-user FTAs?
Imagine you have a XenApp server hosting sessions for a multitude of users. Some users want to use, for instance, Adobe Reader to open PDF files. But some may want, for job reasons, to use something like Adobe Acrobat Pro. There may well be licensing issues to take into account that mean you can’t let everyone use the full version, so you may want to enforce the PDF file type association differently for different groups of users. Having to silo XenApp servers based around a file type association doesn’t make an awful lot of sense, so I know this is fairly common practice (provided the licensing from the vendor allows this form of control).
There are many other applications that you may want to define this way. Some users may have to open Visio drawings in Visio Viewer and others using the full version. Maybe they might want to open text files in Notepad or Notepad++ dependent on function. Picture files, video files, browsers, Office documents, etc; there are a huge amount of file formats that this could apply to.
And finally, you may want to simply set a specific default FTA for an application, but also then give the users the option to change this to a different application they prefer, and then persist that setting from session to session (potentially across different devices), a particular consideration in XenApp or RDSH environments.
But the main clincher is – it used to be so straightforward to enable this, either as an enforced policy, or a pre-defined preference that user could change and expect to persist. And in my humble experience, it’s not just confined to XenApp or RDSH or other multi-user systems – I’ve worked with plenty of enterprises who wanted to use these techniques on VDI or physical.
The old ways
In Windows 7/2008 R2/XenApp 6.x, life was so simple. Group Policy Preference Folder Options could be used to enforce a particular FTA, or you could set it to “Apply once” to give them a specific default and then control it themselves, simply by roaming the FileExts area of the user Registry. This was a great improvement over previous Windows versions (NT4, XP and 2003), where you were reduced to using the ftype and assoc commands which were a bit of a challenge to make work together effectively.
The new ways
Of course, as soon as we’re on Windows 8.1, Windows Server 2012 R2, Windows 10 or Windows Server 2016 (and along with that, anything on XenApp 7.x), the whole thing has been changed. Why? Your guess is as good as mine – Microsoft claim it’s for security, but I’m disinclined to believe that really.
Once we’re on these later platforms, the Group Policy Preferences method I wrote about previously simply flat-out doesn’t work any more. Normally, user FTA settings are written to a specific Registry area – HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts, with subkeys for each filetype (e.g. .PDF). But Microsoft appear to have changed something. Now, when you write a new filetype using the Default Programs part of Control Panel (or the Open With context menu), the Registry settings are changed in a slightly different (but very dramatic) way.
To show you what I mean, I’ve logged on to a Server 2012 XenApp 7.8 system with three different PDF readers installed locally (Adobe Reader, Foxit Reader and SumatraPDF), and I’m going to select FoxIT Reader as the application to use. The way I trigger this is by right-clicking a PDF file and choosing Open With | Choose Default Program, but you can also access this through the Control Panel.
Once I select FoxIT Reader as the default program, we can see the Registry changes we’d expect – but there are some additions we haven’t seen previously
Note the Hash value. Apparently, this is a hash generated from the application and the username, and which needs to be unique in order for this key to be used. Here’s Microsoft’s own commentary on this:-
“Pre-Win 8, apps could set the default handler for a file type/protocol by manipulating the registry, this means you could easily have a script or a group policy manipulating the registry.
However in Win 8 onwards, the registry changes are verified by a hash (unique per user and app) that detects tampering by apps. In the absence of a valid hash, we ignore the default in the registry.”
A result of this, and even more annoying, is the fact that the action of setting a default program in this way also writes a permissions change to the UserChoice key. See below:-
Note the Deny set for the user account that made the change (in this case I was logged on as kz20fl). This means that you can’t set the values in this key through a script or such like without altering the Registry permissions first. But even if you do alter the permissions, you need the Hash value to match correctly for the app and the user – and without knowledge of the algorithm in use, you’re not going to get very far.
This restriction just compounds things even further if you’re trying to set specific FTAs for different sets of users, because now we can’t change these Registry values on-the-fly the way we used to in Windows 7 (there are some methods, but they’re limited, see the next section for details).
Annoyingly, though, there must be a way that the Windows API bypasses this, because if the user changes FTAs through Control Panel or Explorer this Registry block is circumvented. Why they couldn’t expose this functionality to administrators via GPO or InTune is beyond me. Have they given us any tools to deal with this?
What do Microsoft recommend that we do?
Well, they have actually given us a method of dealing with this. Let it not be said that they haven’t made a function available for setting the file associations! It is somewhat long-winded, and involves setting the file association(s) for a user, and then exporting it (them) out to an XML file using the following command
Once you’ve exported the XML file with your configured file associations (you can edit it by hand to just get the FTAs you need), you can then deploy this at logon by using a Group Policy Object. This all sounds good so far…
…until you realize that the GPO that sets the FTA is Computer Configuration only. That’s right, it writes to a Registry key in HKEY_LOCAL_MACHINE. Making it a royal pain in the proverbials (i.e. – CAN’T BE DONE!) for deploying to multi-session environments such as XenApp. If you don’t believe me, check it out – it’s to be found only in Computer Config | Policies | Admin Templates | Windows Components | File Explorer. Even more soul-destroying is the realization that if this was user-based, it would still be useless, as it overwrites at each logon and therefore would eliminate any changes the user had made that they wanted to keep (now if they’d made this a Preference rather than Policy, it would be a different story)
This is really annoying. As I said before, I don’t think this move is in any way truly security-related. If it was, they could simply write up a GPO that does this on a user basis. It’s not the only GPO that has moved quite annoyingly to being specifically device-based – try setting a home page for Microsoft Edge or Internet Explorer for different sets of users. Anyway – Microsoft’s plans for world domination aside, let’s address this in the here and now. How can we manage it?
The good news
When I first started trying this a few years ago it was nigh-on impossible, even to roam FTAs from one session to another. If the Hash value wasn’t accurate then it would reset all of your FTAs to the defaults. Nightmare. We got involved in all sort of tricks to try and rectify this…using things like Ivanti UWM, Citrix UPM, dism.exe commands, logoff scripts, the OEMDefaultAssociations.xml file, FSLogix App Masking, Registry exports and imports – you name it, we tried it, and wrote articles and recorded videos about it. We got a load of inconsistency in the results and I’ve read many a comment or email bemoaning this fact.
Fast forward to 2018, and I do think Microsoft have eased off slightly on FTAs. The Hash value still exists and must match, but the resetting of FTA values is not nearly as intrusive. So if we can capture this accurately, we should be in business. Kudos to Microsoft for stealthily opening the door a bit on this – I think updates have modified the behaviour somewhat for the good. However – if you’re logging on to a machine and you don’t have a profile on the device, which many in the XenApp/RDSH/VDI world will do as a matter of course, then some FTAs are reset at logon. This is driven by a combination of the profile state settings and some system files on the image – more on that later.
Aside from that, the only other problem still outstanding is being able to set or enforce default FTAs for the user, as Microsoft’s GPO method is no good for this, because it runs in the device context. So we need to find a solution for that as well to consider the FTA problem fully solved.
I’m going to verify this on Windows Server 2012 R2, Windows 10, and Windows Server 2016, as these are the platforms I deal with the most. No need to try it on 2008 R2 or Windows 7 – because it was still nice and straightforward then! But make sure your systems are fully patched so that the behaviour of the FTAs is fully up to date.
One final note, about Windows 10 – this OS is very aggressive about resetting FTAs if UWP apps are available on the device, much more so than the behaviour I described for “first profile loads” mentioned above. With UWP apps available, you will commonly see FTA resets occurring even on devices where users have local profiles. To avoid this, always try and remove as many of the UWP apps as you can from your Windows 10 images. Here’s an article showing you how.
So, let’s get started with the three distinct sections I mentioned – persisting FTAs from session to session and device to device, enforcing different FTAs for different sets of users, and a combination of the two (enforcing for some FTAs, persisting for others).
Firstly – because as I said, Microsoft have modified some detail since this problem first appeared, as it used to be infinitely more difficult – we will deal with persistence. Basically, let’s allow a user to select their default FTA for a particular filetype, and roam this between sessions and devices.
This is achievable in many ways:-
Standard Microsoft roaming profiles can actually handle this, contrary to popular belief. The trick is, once you’ve configured your roaming profile, ensure that you have the following GPO settings applied to the devices you want to roam between:-
The last one is the real kicker – this means that an existing roaming profile won’t be “reset” from the perspective of file associations when logging on to a machine for the first time. Giving you smooth roaming of your user-defined FTAs.
Citrix User Profile Management (UPM) can handle this straight out-of-the-box as well. Here’s a copy of the default settings I use for quick and dirty testing on UPM, which roams FTAs without any issue.
If you use the exclusions feature, you may occasionally find issues with FTAs – further details here, but I’ve never seen it happen.
Microsoft User Profile Disks are a feature many people forget about, especially given that they can actually be used on Windows 10 as well. They have a number of drawbacks, such as being single-session only, but they are (along with a roaming profile) probably the only “no-cost” option you have for persisting FTAs across sessions and devices.
Rather than dig right into how to set them up, here’s a useful article showing exactly how to do it. But as for roaming your FTAs – yes, it works, and works well.
Ivanti User Workspace Management (UWM) can roam FTAs through the Personalization Server feature. However, there is an issue with this. In order for this to be simple, it is easiest to capture the FTAs into Personalization Server by configuring this Windows Personalization Group
However, there is a problem here. Because Ivanti UWM is most commonly used to replace roaming profiles, you get the same “reinitialize” issue which overwrites some of your FTAs at logon to a new device or session. Setting the GPO to prevent re-initialization is no use because we aren’t using a roaming profile – typically UWM implementations use local (with purge at logoff) or mandatory profile types.
There used to be an Ivanti “engineering key” that could spoof a roaming profile for the session, but this currently doesn’t work on Server 2016 or Windows 10. However, rather than admit defeat, I did some Process Monitoring to try and find out where exactly the User Profile Service gets the information from to perform the “FTA resets” at user logon (a logon that isn’t a roaming profile with the GPO set).
After a bit of filtering, we find that the OEMDefaultAssociations.xml file is queried and read when the user has a non-roaming profile (via State value), or they have a roaming profile and the download time is set as below (i.e. has never been downloaded)
LastDownloadTime: Year: 1601, Month 1, Day 1, Hour 0, Minute 0
After this is done, the User Profile Service will then overwrite the UserChoice value in the Registry with values specified in the XML file. If the user is a roaming profile that has been previously downloaded and the attendant GPO is set, the XML file is not even queried, so this is the source of the “FTA reset” process.
If we simply remove the OEMDefaultAssociations.xml file, will Ivanti UWM Environment Manager work for roaming of FTAs?
The answer is a resounding yes – removing the XML file allows us to use Ivanti UWM to roam the FTAs quickly and easily though the Personalization Server feature. There are just a couple of things to remember about this:-
a) On Windows 10, logging in for the first time on a device without the XML file will cause one of the aggressive UWP resets I mentioned earlier, with Edge being particularly aggressive. To avoid this, you could define the base FTAs in the default profile, or simply remove all the UWP apps including Edge. Once the first logon occurs though, this will no longer manifest.
b) When a Windows cumulative update or feature upgrade occurs, the XML file may well be replaced again. An easy way to avoid this is to use a Group Policy Preference File item or an Ivanti Self Heal Action (below) to avoid this happening.
FSLogix Profile Containers is a favourite technology of mine, and is essentially User Profile Disks with loads of extra features thrown in that makes it a product ready for enterprise use. And they don’t need any configuration to grab your FTAs and all other settings – simply add the Registry values to enable the feature, provide the path to the profile share, and it’s all ready to go. Here’s the settings I typically deploy in a GPO to get it up and running in minutes.
The Group Policy Object approach springs to mind first when we talk about enforcement, but we want this solution to work not only on Server 2012 R2 and Server 2016 for RDSH sessions, but also to enforce different settings for different groups. So GPO approach is out from the start, as it will only work on a machine-wide basis, enforcing the setting for every user that logs on to the machine and overriding any settings that may be user-defined. As it’s a machine setting, you can’t even “apply once then never re-apply” – there’s no user settings to reference to achieve this.
What you need is either a third-party tool (we’re going to look at Citrix WEM, PolicyPak and Ivanti UWM, specifically) or a bit of scripted fun.
Citrix Workspace Environment Management has an action that will set FTAs for you, which you can then apply on a granular basis according to user or group or many other variables. To be honest, it’s not the easiest to set up, but it does work.
When you create an FTA in WEM, under Actions | File Associations, you have a number of parameters you must specify. Name, Description (optional), State and Extension are all self-explanatory
To locate the ProgId, log on to a device as a user, set the FTA(s) manually you want to enforce, and then open Registry Editor. Browse to HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts and open the key that corresponds to the FTA you have set (in this example, .pdf). Then open the UserChoice subkey. The ProgId value here is the one you want to populate in WEM.
Next select the Action. Most of the time this will be Open, there are also Edit and Print options here.
Next, go back to the Registry of the device you configured, this time armed with the ProgId value. Browse to HKLM\Software\Classes\[insert ProgId here]\shell\open\command and read the (Default) value from this key. The first part of the data here goes in the Target application box in WEM, the second part goes in the Command section. I have flagged these in the image below
So this would leave your completed File Type Association Action looking like this
Some people have reported setting and saving FTAs through WEM can be a little haphazard – James Kindon has a good blog on using them in conjunction with SetUserFTA (more on that below!) to get around this.
PolicyPak is also another product that can manage your FTAs on a per-group or per-user basis without any fuss. If you’re deploying FSLogix or UPD into a non-Citrix environment (or even a Citrix UPM environment without any WEM entitlement), PolicyPak is a great fit.
I like PolicyPak because it’s very lightweight and you simply add it onto your Group Policy implementation by installing an MMC snap-in to the Group Policy editor and a Group Policy Client-Side Extension on the Windows 10 machines. After that, you can access it through the GPMC and use it to extend your Group Policy implementation in all sorts of ways.
As for enforcing FTAs, you can set these up in much the same way as we did with Citrix WEM. PolicyPak is a bit more developed in this area though, because it allows you to choose from installed apps, UWP apps, pretty much whatever you want from a list, rather than having to specify directly. It’s also probably worth mentioning that PolicyPak can actually not just enforce the FTAs, but also prevent these settings from being changed by the user, giving an extra level of control.
Ivanti User Workspace Management has been able to do this since early versions of Windows 10 and also allows massively granular control over the Conditions you use to trigger the FTA actions.
The Ivanti method is pretty straightforward and only needs to be provided with the extension and the application path in order to work.
There are many other tools in the profile management stack that can handle FTA enforcement, and in some of these cases they will do the persistence too. They are too numerous to name them all here, but LiquidWare Labs ProfileUnity, VMware UEM (formerly Immidio) and Scense are probably three that are definitely worthy of a mention, having worked with them in the past. This post is not intended as a bake-off – simply a guide to handling FTAs and some common methods of approaching the problem.
If you can’t – or won’t – spend a small amount of money on a tool that can enforce your FTAs for you (and probably quite a lot of other features, to be honest!), you aren’t being forced to go back to that ridiculous device-based GPO method. There is a way! Step forward Christoph Kolbicz and his nifty tool SetUserFTA (there is a GetUserFTA too which was useful when the Hash value was more problematic). SetUserFTA actually uses the hashing algorithm from Microsoft to make your FTAs “genuine”. Great work!
Christoph has written a detailed blog on how to use this tool which I have linked to here. I will quickly run through the way I most commonly use it, but you will see that there are many different ways you could use this – even perhaps in conjunction with other tools. I will leave it up to you how you decided to utilize it.
To use it, I normally just copy the tool down into the base image using a Group Policy Preference or bake it in at build time. Next, you need to arm yourself with the ProgId, in the same way we showed in the Citrix WEM section. In this instance, we are again associating the FoxIt Reader ProgId with the PDF extension. Then simply set up a GPO Logon Script to run SetUserFTA with the right switches (if your AD is at 2012 or higher forest functional level, make sure you have turned off the Logon Script Delay via GPO, or your logon script will run five minutes after you log in!)
There are loads of other ways you could also handle this – a Scheduled Task, SCCM deployment, maybe even use any of the profile management tools I have mentioned above – as I’ve already said, entirely up to you how you use it.
Here is a line that will set the PDF extension to FoxIt Reader
SetUserFTA.exe .PDF FoxitReader.Document
You could then create multiple logon scripts and Security Filter them to specific groups if you want to apply particular FTA enforcement by groups ,or you could simply create one script and use SetUserFTA functionality to provide mappings to AD groups. For instance, this line
SetUserFTA.exe .PDF FoxitReader.Document “Foxit Reader Users”
would provide the Foxit Reader association only to users who are members of the Foxit Reader Users group in AD.
You can also use central config files to provide the FTAs – more details on this are available on Christoph’s excellent blog post which I linked to above.
Combining these methods, and getting cleverer
You can combine the methods together (whether through linked product suites or not) to ensure you can both persist and enforce, and even blend them together. So you could, for instance, enforce a particular browser for users by mapping extensions and protocols to Google Chrome, but still allow them to save all the other settings. The idea is, the persistence method would restore the saved settings into their profile as they log on, and then the enforcement method would override specific settings within their profile back to the corporate defaults.
If you use these tools cleverly enough, you can even deliver sets of FTAs and levels of persistence based around very granular aspects of the user environment, such as delivering particular FTAs when specific applications are present or invoked, adjusting FTAs based around location – the possibilities are limitless. I will probably revisit the subject in future to discuss some of these more advanced methods.
However, for now, I’m happy to – as of 31/07/2018, and the full patch levels of the relevant OSes on this date – to conclude that this should be enough information for you to roam and/or enforce FTAs on a per-user basis. All of the methods tested above were verified on Windows 2012 R2, Windows 10 1803, and Windows Server 2016. Hopefully, you shouldn’t find any reason to send me any more comments, emails or Tweets regarding this (often very thorny!) subject, but if you do, well, please just fire away 🙂