HomeNewsletterCommunityMVP FeedToolsArchiveBlogToday's NewsAboutServicesQuick Links Subscribe free
← Back to Blog
Windows 11 Windows 11File AssociationsDISMIntuneGroup PolicyDefault AppsRegistry

Setting Default Apps and File Associations at Scale: the XML That Works, and the Per-User Hash That Defeats Scripting

IA
Imran Awan
21 August 2026

Someone opens a ticket that reads like a five-minute job. "Please make Adobe Acrobat the default for PDF files on all machines." You have done harder things before breakfast. So you write six lines of PowerShell that set a registry value, you push it, and by the next morning every affected user has a toast in their Action Center that says An app default was reset. Every PDF still opens in Edge. Your script ran successfully. Windows just undid it.

This is not a bug and it is not a permissions problem. Windows deliberately guards the user's choice of default app with a per-user cryptographic hash, and it re-validates that hash every time a user signs in. If the hash does not match what Windows expects, Windows concludes that something tampered with the setting and reverts to its own default. That single design decision is why "set the default PDF handler" is one of the most-asked and least-understood questions in Windows endpoint management. This post walks the whole model from the ground up: what an association actually is, where every piece of it lives, why the hash exists, the supported way to set defaults at scale, and the difference between setting a default once and enforcing it forever.

The short version

Every default app choice is stored per user as a ProgId plus a Hash under the UserChoice key, and the shell validates that hash at sign-in. Writing the registry directly gives you a ProgId with no valid hash, so Windows detects the tamper and resets the association. The supported route is an XML association file exported with Dism /Online /Export-DefaultAppAssociations, then either imported into an image (seeds the first sign-on only) or applied through the Set a default associations configuration file policy (reapplies at every sign-in). A user can still change an enforced default, and it will hold until they next sign in.

The problem: the script runs, and Windows undoes it

Let us be precise about the failure, because the symptom is easy to misread.

You want .pdf files to open in Acrobat. You go looking for where that setting lives. You find it quickly, because it is not hidden. It sits in the current user's registry hive, one key per extension, and it holds the name of the program that handles that extension. You write that value. Nothing errors. You sign the user out and back in.

Three things then happen, in this order. First, the association reverts to whatever Windows thinks the default should be. Second, the user gets a notification saying an app default was reset. Third, an event is written to a log channel almost nobody looks at, recording precisely what happened and why.

The important detail is that nothing failed. Your write succeeded. Windows read the value back, ran a check on it, decided the value had not been set by the user through the proper user interface, and rolled it back. Microsoft documented this behaviour in support article KB4001770 as "Reset app default when a registry setting is deleted or corrupted and streamlined notification about the corruption."

That wording is worth reading twice. From Windows' point of view, your hand-written registry value is not a configuration change. It is corruption.

Context: why Microsoft did this. Before Windows 10, an application could set itself as the default handler for a file type by writing the registry, and plenty of installers did exactly that without asking. Microsoft's own guidance on the change is blunt about the design goal: "The main requirement for default file association is often forgotten: the end-user is in control." The hash is not there to make your life difficult. It is there so that a PDF reader you installed in 2019 cannot silently steal .html from your browser.

Why it happens: the association model and the hash that guards it

To fix this properly you need the whole model, not just the one key. There are four layers, and they are consulted in a specific order.

Layer 1: the ProgId, which is the name of a handler

A ProgId (programmatic identifier) is a short string that names a way of opening a file. AcroExch.Document.DC is a ProgId. So is ChromeHTML, and so is MSEdgeHTM. Modern Store-packaged apps get machine-generated ProgIds that look like AppX4ztfk9wxr86nxmzzq47px0nh0e58b8fw, which is why the Notepad handler in your registry looks like line noise.

ProgIds are registered under HKEY_CLASSES_ROOT. That subtree is not a real hive. Microsoft documents it as "a view formed by merging HKEY_CURRENT_USER\Software\Classes and HKEY_LOCAL_MACHINE\Software\Classes". Machine-wide registrations go in the HKLM half; per-user registrations go in the HKCU half. Microsoft also states plainly that "in general, HKEY_CLASSES_ROOT is intended to be read from but not written to".

Under the ProgId you find the friendly name in the key's default value, and the actual command line under shell\open\command. That command line is what finally launches when someone double-clicks a file.

Layer 2: the extension key, which is the machine's opinion

Each file extension gets its own key directly under HKEY_CLASSES_ROOT, including the leading period. Its default value holds a ProgId. Microsoft's file types documentation gives the shape as extension=ProgID and adds an important caveat: "Windows respects the Default value only if the ProgID found there is a registered ProgID. If the ProgID is unregistered, it is ignored."

Beside that default value sits a subkey called OpenWithProgids. Microsoft describes it as "a list of alternate ProgIDs for this file type. The programs for these ProgIDs appear in the Open with menu". Applications are supposed to add themselves to this list rather than seize the default. This is the polite, supported way for an app to say "I can also open these".

Layer 3: UserChoice, which is the user's opinion and it wins

Here is the layer that matters. When a user picks a default through Settings, Windows writes it to a per-user key, and that key overrides everything in layer 2.

For file extensions the parent key is:

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\<.ext>\UserChoice

For URL protocols such as http or mailto it is a different tree entirely, which is the single most common reason a "default browser" script only half works:

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\<protocol>\UserChoice

Both of those keys carry exactly two values that matter. ProgId names the handler. Hash is a short base64 string that proves the ProgId was written by the shell on behalf of the signed-in user, on this machine, for this extension.

Layer 4: the hash check at sign-in

At each sign-in the shell walks the UserChoice keys and recomputes the expected hash for every one of them. If a stored hash does not match the computed hash, the entry is treated as tampered. Windows resets that association and, depending on the situation, shows the user a notification.

The algorithm that produces the hash is not documented by Microsoft. There is no supported API, no cmdlet, and no published specification for generating a valid hash for an arbitrary ProgId. Community projects have reverse-engineered it, and they generally work, but you are then depending on undocumented behaviour that Microsoft is free to change in any monthly update. Do not build an enterprise standard on it.

The COM interface that used to do this job tells the same story in Microsoft's own words. IApplicationAssociationRegistration::SetAppAsDefault is documented as setting an application as default "provided that the application's publisher matches the current default's", returning E_ACCESSDENIED when it does not, and is flagged in the documentation as "Not intended for use in Windows 8." The programmatic door was closed a long time ago and clearly labelled.

Gotcha: Windows 11 made this per-extension, and that changed the shape of the request. In Windows 11 the Settings app exposes defaults primarily by file type. Microsoft's guidance is to "type the file extension or protocol you wish to change, such as .txt" and pick an app, or select an app and change individual file and link types from there. There is no single switch that makes an application the default for everything it claims. Even the browser Set default button covers a defined list, not everything. For devices in the European Economic Area, Microsoft documents that button as setting link types ftp, http, https and read, plus file types .htm, .html, .mht, .mhtml, .shtml, .svg, .xht, .xhtml and .xml. If your ticket says "make X the default", your first job is to turn that into a list of extensions and protocols.

The chain, end to end

Put together, a double-click resolves like this.

  1. Explorer takes the file's extension, for example .pdf.
  2. It looks for a UserChoice key for that extension in the current user's hive.
  3. If one exists, the shell validates the Hash value against the ProgId value.
  4. If the hash is valid, the ProgId in UserChoice wins and nothing else is consulted.
  5. If the hash is invalid or missing, the entry is discarded, an event is logged, and the association falls back to the machine-level default.
  6. With no UserChoice at all, the default value of the HKEY_CLASSES_ROOT\.pdf key decides, provided that ProgId is registered.
  7. The winning ProgId's shell\open\command is executed with the file path.

Registry reference for the whole feature area

These are the keys and values involved in file and protocol associations, machine and user. Paths are shown relative to the roots named in each row.

Policy parent key: HKLM\SOFTWARE\Policies\Microsoft\Windows\System
Key or valueTypeWhat it does
DefaultAssociationsConfiguration (in the policy parent key above)REG_SZThe Set a default associations configuration file policy. Holds the path to the association XML. Confirmed as the value name in the shipped WindowsExplorer.admx.
HKLM\SOFTWARE\Policies\Microsoft\Windows\Explorer\NoNewAppAlertREG_DWORDBacks the Do not show the 'new application installed' notification policy. Suppresses the toast a newly installed handler triggers.
HKCU\...\Explorer\FileExts\<.ext>\UserChoice, values ProgId and HashREG_SZThe per-user default for one file extension, plus the hash that proves the shell wrote it. Overrides the machine registration.
HKCU\...\Explorer\FileExts\<.ext>\OpenWithProgidssubkeyPer-user list of alternate handlers offered in Open with.
HKCU\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\<protocol>\UserChoiceREG_SZThe same mechanism for protocols such as http, https, mailto, ftp. A separate tree from file extensions.
HKCR\.<ext> default valueREG_SZMachine or user-level ProgId registration for the extension. Ignored if the ProgId is not registered.
HKCR\.<ext>\OpenWithProgidssubkeyDocumented list of alternate ProgIDs shown in Open with. Where a well-behaved installer adds itself.
HKCR\<ProgId> default value, and \shell\open\commandREG_SZFriendly name and the actual command line for the handler.
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\ApplicationAssociationToastsREG_DWORD per entryTracks which association toasts have been shown to this user. Observed on a live device and not documented by Microsoft; read it for context, never build detection on it.

Do not write UserChoice by hand. Setting ProgId without a matching Hash, or copying a Hash from another machine or another user, produces exactly the state Windows is designed to reject. You get a reset at the next sign-in, a toast for every affected extension, and on a machine with a long extension list that means a notification storm. Microsoft's own guidance on this is one sentence long: "Stop using script or other pre-Windows 10 ways for configuring file association." Deleting UserChoice keys is equally risky, because that is indistinguishable from the corruption case and triggers the same reset path.

System files and binaries in the flow

FileRoleNotes
C:\Windows\System32\shell32.dllThe shell library that resolves an extension to a handler and launches it.Version 10.0.26100.8972 on the Windows 11 25H2 device used for this article.
C:\Windows\System32\windows.storage.dllCarries much of the modern association and file-type plumbing that used to live in shell32.Ships in lockstep with shell32.dll.
C:\Windows\System32\shsvcs.dllRegistered message and resource file for the Microsoft-Windows-Shell-Core event provider.This is where the AppDefaults event strings come from.
C:\Windows\System32\Dism.exeExports, imports, lists and removes default app associations.The only supported tool for producing the association XML.
C:\Windows\System32\OEMDefaultAssociations.xmlThe image-level association file. Dism /Import-DefaultAppAssociations writes here.Present on stock Windows 11. Ships with OverwriteOnVersionMax and OverwriteIfProgIdIs attributes on inbox entries.
shobjidl_core.h / IApplicationAssociationRegistrationThe legacy COM surface for querying and setting defaults.QueryCurrentDefault still reads. SetAppAsDefault is documented as "Not intended for use in Windows 8."
assoc and ftypeBuilt-in commands for viewing extension-to-ProgId and ProgId-to-command mappings.Internal to cmd.exe, not standalone executables. They read and write the machine registration, not UserChoice, so they cannot set a user's default.

Context: there is no service and no scheduled task for this. Association resolution happens inside the shell process that needs it, and the hash validation runs as part of sign-in processing by Explorer. There is no dedicated Windows service to check, and there is no task under \Microsoft\Windows\... that drives file associations. If you are troubleshooting, do not go looking for a stopped service. The nearest thing to a moving part is the sign-in itself, which is why "sign out and back in" is a real diagnostic step here rather than a brush-off.

How to verify: read the four places that decide

Before changing anything, establish what the device currently believes. There are four sources of truth and they can disagree.

1. Read the live UserChoice values with PowerShell

This reads the per-user default for a handful of extensions and protocols, and reports whether a Hash value exists beside each ProgId. Run it as the signed-in user, not as an elevated different account, because UserChoice is per user.

PowerShell - run as the signed-in user
foreach ($x in '.pdf','.htm','.html','.txt','.csv') { $k = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\$x\UserChoice" $v = Get-ItemProperty -Path $k -ErrorAction SilentlyContinue if ($v) { '{0,-8} ProgId={1} HashChars={2}' -f $x, $v.ProgId, $v.Hash.Length } else { '{0,-8} no UserChoice key' -f $x } } # Healthy: every line shows a ProgId AND a non-zero HashChars, typically 12 characters. # Broken: a ProgId with HashChars 0, or an error - that entry will be reset at next sign-in. # Normal: "no UserChoice key" simply means the user never explicitly chose for that type.

Protocols live somewhere else, so check them separately. Forgetting this is why half-working default-browser scripts are so common.

PowerShell - protocol handlers
foreach ($p in 'http','https','mailto','ftp') { $k = "HKCU:\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\$p\UserChoice" $v = Get-ItemProperty -Path $k -ErrorAction SilentlyContinue if ($v) { '{0,-7} ProgId={1}' -f $p, $v.ProgId } else { '{0,-7} none' -f $p } } # Healthy: http and https name the same browser ProgId as .htm and .html. # Broken: http says one browser and .html says another - links and files open in different apps.

2. Look at the keys in Registry Editor

Sometimes you just want to see it. Open Registry Editor and paste the path into the address bar.

Registry Editor
Computer\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.pdf\UserChoice
Hash   REG_SZ   aBcDeFgHiJkL
ProgId   REG_SZ   AcroExch.Document.DC
Two values, both present. That is a healthy user-set default.

3. Ask DISM what the image thinks

DISM reads and writes the image-level association list, which is a different thing from the user's live choice. Both commands below are read-only.

Command Prompt - run elevated
Dism.exe /Online /Get-DefaultAppAssociations # Lists the associations already applied to this image. Empty output means no custom # list has been imported - it does NOT mean users have no defaults. Dism.exe /Online /Export-DefaultAppAssociations:C:\Temp\AppAssoc.xml # Writes the CURRENT USER's configured associations to an XML file. Read-only for the # device; it only creates the file you name. This is the file you will deploy.

4. Read the AppDefaults event channel

There is a dedicated channel for exactly this feature, and almost nobody knows it exists. Microsoft's own guidance points at it: "You can check the Microsoft-Windows-Shell-Core/AppDefaults event log for clues about file associations reset."

Event Viewer
Applications and Services Logs > Microsoft > Windows > Shell-Core > AppDefaults
Information   62443   AppDefault Info: AppDefaults-Logon-UserProfileLoaded
Information   62443   AppDefault Info: File Association hash version is still the same
Two 62443 events per sign-in, and no 62440 or 62441, is the healthy pattern.

The event identifiers below were read from the Microsoft-Windows-Shell-Core provider's own manifest on a Windows 11 25H2 device, using Get-WinEvent -ListProvider. The strings are Microsoft's, shipped in shsvcs.dll. Microsoft does not publish this table on learn.microsoft.com, so treat the numbers as version-specific and re-read the manifest on your own build before you write an alert rule against them.

Channel: Microsoft-Windows-Shell-Core/AppDefaults
IDManifest stringWhat it tells you
62440Hash mismatch detected for: %1. ProgId: %2. UserSid: %3. HashInRegistry: %4. ComputedHash: %5.The smoking gun. Something wrote a UserChoice ProgId without a valid hash. The extension and the ProgId in the event name the culprit.
62441User choice has been reset to prog id %1 for %2. CurrentDefaultProgId: %3. ShouldToast: %4Windows reverted the association. ShouldToast tells you whether the user was notified.
62442Upgraded to prog id %1 from prog id %2 for %3A legitimate handler upgrade, for example an inbox app superseding an older one at OS upgrade.
62443AppDefault Info: %1Informational. Carries the sign-in messages, including the hash version check.
62444Missing Hash -- ProgId: %1 FileExtOrUriScheme: %2A ProgId with no hash at all. This is the exact shape a hand-written registry change leaves behind.
62445Migration Info: %1Informational, emitted around profile and OS migration.

Log files

Association handling itself is event-driven rather than file-logged, but DISM keeps a text log, and that is where an XML import failure shows up.

PathSearch forHealthy versus broken
C:\Windows\Logs\DISM\dism.logDefaultAppAssociationsHealthy: the operation is logged with a success result and the XML path you passed. Broken: an error immediately after the path, usually because the file is unreachable or is not valid XML.
C:\Windows\Logs\DISM\dism.logImport-DefaultAppAssociationsConfirms the import ran at all. Silence here after a task-sequence step means the step never executed.

Gotcha: an empty /Get-DefaultAppAssociations is not a clean bill of health. That command reports the custom list applied to the image. Users get their defaults from their own UserChoice keys, seeded at first sign-on and possibly overridden by policy since. A device can show nothing from DISM and still have every user opening PDFs in Acrobat. Always read the user hive as well.

The fix: export the XML, then choose once or enforced

The supported route has three steps: build a reference device, export an XML from it, and then decide how that XML reaches your fleet. The third step is where most projects go wrong, because the two delivery mechanisms behave completely differently.

Step 1: set the defaults on a reference device, through the UI

Install the applications you care about on a clean device of the same Windows version as your fleet. Then set the defaults by hand, in Settings. This is the one place where clicking through the UI is not a shortcut but the actual requirement, because only the UI produces valid hashes.

SettingsAppsDefault appssearch the extension, pick the app

Two traps here, both documented by Microsoft. First, on a brand-new reference machine the exported XML "may be truncated until the delay-install apps have fully installed", so wait 10 to 30 minutes and open the inbox apps before exporting. Second, if only one application on the device can handle a type, "that application will appear as the default for that type, even though there is no explicit choice in the registry", and "exporting the default applications to XML will not gather these implied settings". To capture an implied default you must select it explicitly, even though the UI already shows it.

Step 2: export the XML with DISM

Run this as the same user account that set the defaults in step 1. Microsoft is explicit that using a different account produces a malformed file.

Command Prompt - elevated, as the user who set the defaults
Dism /Online /Export-DefaultAppAssociations:"C:\Temp\AppAssoc.xml" # Healthy: "The operation completed successfully." and a file of a few hundred lines. # Broken: a file with only a handful of entries - delay-installed apps had not finished. Dism /Online /Get-DefaultAppAssociations # Run this BEFORE you change anything and compare, as Microsoft suggests, to confirm # the export contains everything the running image already had.

The result is a flat list of Association elements. Each carries an Identifier (the extension or protocol), a ProgId, and a human-readable ApplicationName.

AppAssoc.xml - Microsoft's documented sample shape
<?xml version="1.0" encoding="UTF-8"?> <DefaultAssociations Version="1"> <Association Identifier=".htm" ProgId="AppX4hxtad77fbk3jkkeerkrm0ze94wjf3s9" ApplicationName="Microsoft Edge" Suggested="true" /> <Association Identifier=".pdf" ProgId="AppXd4nrz8ff68srnhf9t5a8sbjyar1cr723" ApplicationName="Microsoft Edge" /> <Association Identifier="http" ProgId="AppXq0fevzme2pys62n3e0fbqa7peapykr8v" ApplicationName="Microsoft Edge" /> </DefaultAssociations> # Version and Suggested are the two attributes that control enforcement frequency. # No Suggested attribute means the entry is reapplied at EVERY sign-in.

Step 3a: one-time defaults, by importing into the image

If you want to hand users a sensible starting point and then leave them alone, import the XML into the image. Microsoft's description of the switch is the whole story: "Imports a set of default application associations to a specified Windows image from an .xml file. The default application associations will be applied for each user during their first logon."

Command Prompt - elevated, offline or online image
Dism.exe /Image:C:\test\offline /Import-DefaultAppAssociations:F:\AppAssociations.xml # Offline: stages the list into a mounted WIM or VHD before deployment. Dism.exe /Online /Import-DefaultAppAssociations:C:\Temp\AppAssoc.xml # Online: writes the list to C:\Windows\System32\OEMDefaultAssociations.xml on this device. # Affects NEW profiles only. Existing profiles keep whatever they already have. Dism.exe /Image:C:\test\offline /Remove-DefaultAppAssociations # Removes the custom list from the image. Does not touch any existing user's choices.

Step 3b: enforced defaults, by policy

If you need the association to come back after a user changes it, you need the policy. The setting is Set a default associations configuration file, it takes a path to the XML, and the shipped administrative template describes its behaviour precisely: "If this group policy is enabled and the client machine is domain-joined, the file will be processed and default associations will be applied at logon time." It then adds the sentence that answers the question everyone asks: "If the policy is enabled, users will still be able to override default file type and protocol associations, but on next logon the file will be reapplied."

QuestionDISM import into imagePolicy XML
When does it apply?At each user's first sign-on only.At every sign-in, for every entry without Suggested="true".
Can the user change it afterwards?Yes, permanently.Yes, but only until they next sign in.
Effect on existing profilesNone.Applies to every user who signs in while the policy is in force.
Can the XML be a partial list?No. A missing entry triggers reset notifications.Yes. This is the supported way to force only a subset.
Where the setting livesC:\Windows\System32\OEMDefaultAssociations.xmlDefaultAssociationsConfiguration under the policy key.

Tip: use two files, and let the policy carry the short one. Microsoft's own guidance is to keep one complete XML for the image import, containing every association the OS already had plus your changes, and a second, deliberately short XML containing only the handful of types you actually want to force. The complete file goes in via Dism /Import-DefaultAppAssociations and keeps the reset notifications quiet. The short file goes in the policy and enforces just your subset. At first sign-on the shell applies both. This is also the answer to "we want Acrobat for PDF but we do not care about anything else".

Group Policy walkthrough

Computer ConfigurationAdministrative TemplatesWindows ComponentsFile ExplorerSet a default associations configuration file
  1. Copy your association XML to a location every device can reach. A read-only UNC path such as \\contoso-fs01\NETLOGON\AppAssoc.xml works, and so does a local path you stage with the same package that installs the applications.
  2. Open the Group Policy Management Editor on a GPO linked to the target computers. This is a Computer Configuration policy, so linking it to a user OU will do nothing.
  3. Navigate to Computer Configuration > Administrative Templates > Windows Components > File Explorer.
  4. Open Set a default associations configuration file and select Enabled.
  5. In Default Associations Configuration File, enter the full path to the XML. This is a path, not the XML content.
  6. Click OK, then confirm on a test device with gpupdate /force followed by a full sign-out and sign-in. A gpupdate alone will not reapply associations, because the work happens at logon.
  7. While you are in the same node, consider enabling Do not show the 'new application installed' notification to suppress the toast that fires when an installer registers a new handler.

Intune walkthrough

There are two ways to deliver this from Intune, and they take different input.

intune.microsoft.comDevicesConfigurationCreateSettings catalog
  1. Go to Devices > Configuration > Create > New policy, platform Windows 10 and later, profile type Settings catalog.
  2. In the settings picker, search for Default Associations Configuration. The setting sits under the Administrative Templates > Windows Components > File Explorer category, mirroring the GPO, and under Application Defaults for the CSP-backed form.
  3. Enable the setting and supply its value. For the administrative-template form this is the path to the XML, exactly as in Group Policy.
  4. Assign to a device group. This node is device-scoped: Microsoft documents the CSP scope as Device, not User.
  5. For the CSP form, create a Custom profile instead and add an OMA-URI setting. The node is ./Device/Vendor/MSFT/Policy/Config/ApplicationDefaults/DefaultAssociationsConfiguration, data type String.
  6. The CSP data is base64-encoded XML content, not a path. Microsoft's instruction is unambiguous: "The file then needs to be base64 encoded before being added to SyncML." Encode the whole file, paste the result as the value.
  7. Sync a pilot device, sign out and sign in, and verify with the companion script below.
PowerShell - produce the base64 string for the CSP node
$bytes = [System.IO.File]::ReadAllBytes('C:\Temp\AppAssoc.xml') [System.Convert]::ToBase64String($bytes) | Set-Clipboard # Encodes the file's exact bytes, preserving the XML declaration and encoding. # Healthy: the string starts PD94bWwgdmVyc2lvbj0i - that is "<?xml version=" encoded. # Broken: anything else at the start means you encoded the wrong thing, or a string # rather than the file bytes, and the node will be rejected.

Gotcha: the two delivery paths take different input, and Microsoft documents one applicability limit you must read carefully. The administrative template takes a path; the CSP node takes base64-encoded XML. Both map to the same registry value name, DefaultAssociationsConfiguration under Software\Policies\Microsoft\Windows\System. Microsoft does not document which form lands in that value when the setting arrives by MDM, so read the value on a pilot device and confirm before you assume. Separately, the ADMX text says the file is processed "if the client machine is domain-joined", while the CSP text says associations are applied "if policy is enabled and the client machine is Microsoft Entra joined". Neither sentence mentions Entra-registered or workgroup devices. Pilot on a device that matches your real join state rather than trusting either sentence to cover you.

What happens to a user who tries to change it

Nothing stops them. The Settings UI still works, the change writes a valid hash, and files open in their chosen app for the rest of the session and every session until they sign out. At the next sign-in the policy XML is processed again and their choice is replaced for any identifier the XML names. Identifiers the XML does not name are left alone permanently.

If you want a default that is a strong suggestion rather than a monthly argument, that is what the Suggested attribute is for. Microsoft documents it as: "The default value is false. If it's false, the Association is applied on every sign-in. If it's true, the Association is only applied once for the current DefaultAssociations Version." Increment the Version attribute on the root element and the suggested entries apply once more, on the next sign-in. Those two attributes arrived in Windows 11, version 22H2.

Context: Defender has nothing to do with this. There is no Defender, ASR, exploit protection, WDAC or firewall setting that controls file associations, and no Endpoint Security profile to check. The hash validation is a shell integrity mechanism, not a security product feature. The one genuine overlap is indirect: if you use WDAC or AppLocker to block an executable, its ProgId can still hold the association and users get a launch failure rather than a fallback handler. That is worth knowing, but it is not a Defender setting.

Proof it worked: real output from a live device

The companion script is Get-FileAssociationState.ps1. It reads the policy value, resolves and parses the XML if one is in force, reads every relevant UserChoice key, resolves each ProgId to its friendly name, counts recent AppDefaults events, and flags any identifier where the policy and the live value disagree. It writes nothing. It never sets a UserChoice value and never calls a DISM import or remove verb. It needs no PowerShell module and runs on both Windows PowerShell 5.1 and PowerShell 7.

The output below is a genuine run on a Windows 11 25H2 device with identifiers replaced. This device has no association policy applied, which is the baseline you should expect before you deploy anything.

PowerShell - real run, no policy in force
== Policy state ====================================================== Default associations policy : NOT SET No DefaultAssociationsConfiguration value under HKLM:\SOFTWARE\Policies\Microsoft\Windows\System Nothing is reapplying associations at sign-in from policy. Image-level XML : PRESENT at C:\WINDOWS\System32\OEMDefaultAssociations.xml == Effective associations ============================================ Identifier Kind LiveProgId HashPresent PolicyProgId Verdict ---------- ---- ---------- ----------- ------------ ------- .pdf extension AcroExch.Document.DC True NoPolicyXml .htm extension ChromeHTML True NoPolicyXml .html extension ChromeHTML True NoPolicyXml .txt extension AppX4ztfk9wxr86nxmzzq47px0nh0e58b8fw True NoPolicyXml .csv extension Excel.CSV True NoPolicyXml .zip extension CompressedFolder True NoPolicyXml .docx extension False NoUserChoice http protocol ChromeHTML True NoPolicyXml mailto protocol Outlook.URL.mailto.15 True NoPolicyXml # HashPresent True on every row with a ProgId - nothing has been tampered with. # .docx shows NoUserChoice: the user never explicitly chose, so the machine default decides. == AppDefaults events, last 14 day(s) ================================ Id Count Meaning -- ----- ------- 62443 48 AppDefault informational (includes the sign-in hash-version check) # Only 62443. No 62440 hash mismatch and no 62441 reset. This is what healthy looks like.

Now the interesting run. Passing -PolicyXmlPath compares the live values against a candidate XML without deploying it, which is how you test an XML before it reaches users. This XML asks for Edge on .htm, .html, http and https, while the device is set to Chrome.

PowerShell - real run, drift against a candidate XML
== Policy state ====================================================== Comparing against XML supplied on the command line: C:\Temp\AppAssoc.xml XML readable : YES (5 association entries) DefaultAssociations Version : 3 (governs Suggested="true" entries) == Effective associations ============================================ Identifier Kind LiveProgId HashPresent PolicyProgId Verdict ---------- ---- ---------- ----------- ------------ ------- .pdf extension AcroExch.Document.DC True AcroExch.Document.DC Match .htm extension ChromeHTML True MSEdgeHTM Drift .html extension ChromeHTML True MSEdgeHTM Drift .txt extension AppX4ztfk9wxr86nxmzzq47px0nh0e58b8fw True NotInPolicy http protocol ChromeHTML True MSEdgeHTM Drift https protocol ChromeHTML True MSEdgeHTM Drift == Verdict =========================================================== 4 identifier(s) disagree with the policy XML: .htm policy wants MSEdgeHTM but live is ChromeHTML .html policy wants MSEdgeHTM but live is ChromeHTML http policy wants MSEdgeHTM but live is ChromeHTML https policy wants MSEdgeHTM but live is ChromeHTML Either the user changed it since the last sign-in, or the policy never applied. A Suggested="true" entry only reapplies when the XML Version increments. Read-only run complete. Nothing on this device was modified. # Drift on four identifiers, Match on .pdf, NotInPolicy on .txt. Exactly the three # verdicts you need: enforced and holding, enforced and overridden, deliberately free.

Read the verdicts like this. Match means the policy applied and the user has not fought it. Drift means either the user changed the default since their last sign-in, or the policy never reached them at all. NotInPolicy means you deliberately left that type to the user. NoUserChoice means nobody ever chose, which is normal and often means only one handler exists.

Two follow-ups turn a Drift into a diagnosis. Check the AppDefaults channel for events 62440 and 62444 on that device: if they are present, something is writing UserChoice values directly and you have an application problem, not a policy problem. If they are absent and the drift is on every identifier, the policy is not being processed, so check the join state, the XML path reachability from the user's context, and whether the device has actually had a fresh sign-in since the policy landed.

Tip: make the XML path a read-only, always-available location. A policy that points at an unreachable file does not error and does not warn. It simply does nothing at sign-in, and every association silently stays wherever it was. The companion script calls this out explicitly rather than reporting a clean result, because "no drift" and "no comparison possible" are very different answers. Stage the XML locally alongside the application package if any of your users are off the corporate network.

Community deep dives

AuthorFocusLink
Martin BengtssonDeploying the association XML through the Intune settings catalog, including the base64 conversion step.imab.dk
Donna Ryan (MSEndpointMgr)Importing an exported default app association XML into an offline image as part of image build automation.msendpointmgr.com

References

PowerShell — companion script

Download it from Imran76Awan/Windows-11-Scripts — no sign-in required. It is read-only: it reports and never changes a device or anything in Intune. Validate it in your own environment before relying on the output.

Get-FileAssociationState.ps1 — Reports the effective default application associations on this device and flags
View all scripts on GitHub
Was this post helpful?
React below — no account needed
Share this post
LinkedIn X / Twitter Reddit Bluesky

More from EndpointWeekly

Windows 11
SMB signing became mandatory and your NAS stopped working:…
Windows 11 24H2 and later require SMB signing, refuse insecure guest logons and ship…
Windows 11
Print Spooler Hardening After PrintNightmare: The Settings That…
Microsoft flipped the Point and Print driver installation default on 10 August 2021 and a…
Windows 11
Your firewall rule is deployed and the traffic is still blocked:…
Windows Defender Firewall has three profiles, several rule stores and a per-profile merge…