The Win32 app removes fine from Programs and Features. A week later, Intune still lists it as installed on half the fleet, or the "Uninstall" assignment sits at Failed with no useful error. The install side of Win32 app deployment gets most of the attention, but the uninstall side is where legacy EXE installers quietly rot — because the string Intune calls to remove the app was never designed to be called by anything but the vendor's own installer shell.
This article covers why EXE uninstall strings break in ways MSI uninstalls do not, where the orphaned registry entries come from, and how to stop debugging a different broken UninstallString for every app in your catalog.
Fast Verdict Matrix
| Failure Surface | Legacy EXE Behavior | Native MSI Behavior |
|---|---|---|
Uninstall command formatUninstallString | Points at the vendor's bootstrapper EXE with a silent-uninstall switch that is different per framework — /S, -uninstall, /VERYSILENT — and sometimes not silent-capable at all. | Always msiexec.exe /x {ProductCode} /qn. Same shape for every MSI ever built, so Intune's uninstall command is predictable and never app-specific. |
| Registry cleanup after removal Uninstall key | Frequently leaves its own Uninstall\{AppName} key behind, especially on a silent uninstall that exits early or a crash mid-removal. The app is gone; the ARP entry is not. | The Windows Installer service owns the ARP key it created and removes it as part of the same transaction as the file/registry rollback — atomic, no orphaned entries. |
| Detection rule after uninstall Version drift | A detection rule written against a registry value or file version can keep matching a stale artifact from a partial removal, so Intune reports the app as still present when the user experience says it is gone. | ProductCode-based detection is tied to the exact package instance that was installed. When that instance is removed, the code it was detecting no longer exists — no drift to reconcile. |
The Rule of Thumb
- Never trust the vendor's uninstall switch until you've tested it under SYSTEM. An EXE's documented silent-uninstall flag is frequently wrong, undocumented, or version-specific — verify it with PsExec before pasting it into the Win32 app's uninstall command field.
- An orphaned ARP entry is a packaging bug, not an Intune bug. If Programs and Features still lists an app after a "successful" uninstall, the installer failed to clean up its own registry key — fix the uninstall string or the package, not the detection rule.
The Fix: One Uninstall Shape For Every App
The pattern in the matrix above is the same one that shows up on the install side: every EXE vendor invents its own bootstrapper behavior, and every one of those inventions is a place an uninstall can quietly fail. The typical workaround is a PowerShell wrapper — PSAppDeployToolkit or a hand-rolled script — that tries every known silent-uninstall switch, greps the registry for leftover ARP keys, and forces a cleanup pass. That works, but it is another script to write, test, and maintain per app, and it has to be revisited every time the vendor changes their installer.
InstallMage removes the variable instead of scripting around it. Converting the source EXE to a native MSI means every app in your catalog gets the same uninstall shape — msiexec.exe /x against a stable ProductCode, with the Windows Installer service handling the registry rollback automatically. There is no vendor-specific switch to find, no orphaned ARP key to clean up after the fact, and no PSADT uninstall wrapper to keep in sync with the next installer version.
Deep Dive
The UninstallString Free-For-All
Every installed application on Windows registers an entry under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{AppID} (or the WOW6432Node mirror for 32-bit apps). Two values in that key matter for automated removal: UninstallString, which Programs and Features calls when a human clicks Uninstall, and the optional QuietUninstallString, which is supposed to be the non-interactive equivalent. The problem is that nothing enforces what either value actually contains. Common patterns you'll find in the wild:
- No quiet variant at all. Only
UninstallStringis populated, pointing at the interactive uninstaller. Intune runs it underNT AUTHORITY\SYSTEMwith no session to render a UI, and the process hangs until it times out. - A quiet variant with the wrong flag. NSIS uses
/S, Inno Setup uses/VERYSILENT, InstallShield varies by version — and it is common for a vendor to shipQuietUninstallStringwith a flag that was correct for a previous major version but never updated. - A bootstrapper that re-downloads content to uninstall. Some EXE uninstallers phone home to fetch an uninstall payload before removing anything. On an endpoint with restricted outbound access, the uninstall fails for a reason that has nothing to do with the local install.
Before setting the uninstall command in the Win32 app's properties, reproduce it with PsExec: psexec -i -d -s cmd.exe, confirm the context with whoami, then run the exact string from QuietUninstallString and watch what happens. If it hangs, spawns a window, or exits non-zero, that is the string that needs fixing — not the Intune assignment. For framework-specific silent flags, see the silent install switches guide.
Orphaned Registry Keys and the "Ghost App" Problem
A silent EXE uninstaller that exits successfully does not guarantee it removed its own ARP entry. This happens more than it should, for a few structural reasons:
- The uninstaller deletes files before the registry key. If the process is killed, times out, or hits a permissions error partway through — for example, a per-user path under
%APPDATA%that does not resolve correctly in a SYSTEM session — the file removal can complete while the registry cleanup step never runs. - Multiple install methods registered separate ARP entries. An app installed once via the vendor EXE and once via a repackaged MSI (common during a migration) leaves two Uninstall keys. Removing one leaves the other, and Intune's detection rule may still match it.
- The uninstaller only removed the executable, not the ARP key it wrote for itself. This is a packaging defect, but it is a surprisingly common one — the vendor tested "uninstall removes the app" and never checked whether Programs and Features still lists it afterward.
A ghost entry looks identical to a real install to any registry-based detection rule, which is why the app keeps reporting as present in Intune long after a user confirms it is gone from their Start menu. Confirm the orphan with reg query against the specific Uninstall\{AppID} key after the uninstall completes — if it still resolves, the uninstall did not do what it reported doing, regardless of its exit code.
Reconciling Intune's Uninstall Detection With What Actually Ran
Intune re-runs the same detection rule after an uninstall assignment to decide whether removal succeeded — it does not have a separate "uninstall verification" mechanism. That means a detection rule written loosely for the install (checking only that a folder exists, for instance) will just as loosely report success or failure on the uninstall side, independent of whether the registry and file system actually match. Two logs are worth checking together when the uninstall status does not match reality:
C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log
C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\AppWorkload.logIntuneManagementExtension.log shows the exact uninstall command IME launched and its exit code. AppWorkload.log shows the detection rule evaluation that followed. If the exit code was 0 but detection still reports the app present, the fix belongs in the uninstall string or the leftover artifact — not in loosening the detection rule to match the broken state. A detection rule that is more lenient makes the install side less reliable to save the uninstall side, which trades one failure mode for another. For how detection rule choice interacts with registry redirection specifically, see the WOW6432Node guide.
Frequently Asked Questions
Why does Intune say uninstall failed even though Programs and Features shows the app is gone?
The app's files were likely removed but its ARP registry key under Uninstall\{AppID} was not, or a detection rule is matching a leftover file or registry value the uninstaller didn't clean up. Confirm with reg query against the specific uninstall key after removal — if it still resolves, the uninstall did not fully complete regardless of what Programs and Features displays.
How do I find the correct silent uninstall switch for a legacy EXE installer?
Check QuietUninstallString in the app's registry Uninstall key first — if it's populated, test it under NT AUTHORITY\SYSTEM with PsExec before trusting it. If it's empty or wrong, identify the installer framework (NSIS, Inno Setup, InstallShield) from the uninstaller binary and try that framework's known silent flag, verifying under SYSTEM the same way.
Does converting an EXE to MSI fix broken uninstall strings automatically?
Yes, for new deployments going forward — a native MSI always uses msiexec.exe /x {ProductCode} as its uninstall command, with the Windows Installer service handling registry cleanup atomically. It does not retroactively fix an app already installed via the broken EXE path; that endpoint still needs the orphaned entry cleaned up once before the MSI-based version takes over.
Broken uninstall strings are an install-time problem that only shows up at removal time, which is exactly why they get missed in initial packaging review. Every EXE vendor's uninstaller is a slightly different, under-tested code path; every MSI uninstaller is the same Windows Installer transaction. Converting the source EXE to a native MSI removes the variable instead of adding a cleanup script for it. For the exit codes that show up during the install side of this same class of problem, see the Intune Win32 app exit codes guide.