AppLocker policy is one of those areas of Windows management where the tooling hasn’t really kept up with how it’s actually deployed today. The Group Policy Management Console editor still assumes you’re targeting GPO — it doesn’t know or care that you’re actually going to package the result as an Intune custom OMA-URI profile. In practice that meant either editing the XML by hand (workable, but slow and error-prone at any real scale) or building the policy in the GPMC editor and then manually reshaping the output into what Intune’s Custom Profile actually expects.
Neither of those scale well past “I have three rules to add,” so I built a dedicated GUI: a .NET 8 WPF/MVVM desktop app for authoring and editing AppLocker XML and packaging it directly for Intune.
The two XML shapes that actually matter
The thing that makes this fiddly in the first place is that AppLocker XML isn’t one format — it’s two, depending on where it’s headed:
- GPO/local policy form: a full document with the
<?xml?>declaration and a top-level<AppLockerPolicy>root. This is what Save, Save As, and Export XML produce at the top level of the app. - Intune CSP form: just the bare
<RuleCollection>element, no wrapper — because Intune’s OMA-URI custom profile targets one rule collection at a time. This is what the OMA-URI panel’s Save XML and Export PS1 produce.
Getting this wrong means a policy that imports fine into one target and silently fails, or gets rejected, on the other. Import in the app deliberately accepts either form — PolicyXmlService.DeserializeFlexible sniffs the root element and figures out which shape it’s looking at, so you’re not stuck remembering which export path a given file came from.
Exporting to Intune specifically
Export PS1 creates one Intune Custom profile per non-empty rule collection rather than bundling everything into a single profile. AppLocker has four collections (Executable, Windows Installer, Script, Packaged app), and Intune’s OMA-URI targeting works per-collection — so a single combined profile isn’t actually the right shape for how Intune wants to consume this, even though it’d be the more obvious thing to build if you weren’t paying attention to how the CSP is structured.
A UI decision that bit me once, so it’s explicit now
Publisher rule conditions have “Any X (*)” wildcard checkboxes — Any Publisher, Any Product Name, Any File Name, Any File Version. The behaviour is: ticked means wildcard. Untick a box to pin that field to the specific value pulled from the inspected file. That’s the opposite of how a lot of “enable this restriction” checkboxes read at a glance, and I genuinely confused myself with my own UI on this once during testing — ticked felt like it should mean “restrict to this,” not “allow anything.” Labels and an inline hint went in after that, because if the person who built it can misread it, anyone can.
A WPF theming bug worth knowing about if you ever build a dark-mode desktop app
Dark-mode primary buttons are dark-ink text on a green fill (following our brand guidance — ink-fill buttons in light mode, green-on-dark in dark mode, for WCAG contrast). Partway through, dark-mode button labels went invisible — ink-on-ink, unreadable, despite the XAML clearly setting the right foreground on the buttons themselves.
The cause was a global implicit TextBlock style with its own Foreground setter, defined once at the application level. WPF’s style-setter foreground on TextBlock overrides whatever foreground the parent button would otherwise pass down through inheritance — so any TextBlock inside a button, regardless of what colour the button itself was told to use, fell back to whatever the global style dictated instead of inheriting from its actual visual parent.
The fix: don’t set Foreground in the implicit TextBlock style at all. Set TextElement.Foreground on each window’s root element instead, and let button labels inherit the button’s foreground the way WPF’s inheritance model expects. I didn’t trust my eyes on this one after getting burned once — verification was an offscreen RenderTargetBitmap pixel check against the actual rendered output, not a visual scan.
Where it stands
Delivered, working, two build outputs published — a framework-dependent build (~0.9MB, needs the .NET 8 Desktop Runtime already installed) and a self-contained single-file build (~69MB, no dependency). Internal testing is underway. If you’re maintaining AppLocker policy for Intune-managed devices by hand right now, the two-format problem above is probably the single biggest source of friction worth solving first — everything else in the tool exists to make that friction go away.