Skip to main content

Intune Custom Compliance Policies: Detecting Real-World Drift

· 10 min read

intune-custom-compliance

When it comes to configuring compliance policies, Intune provides settings out of the box that can be monitored for config drift, such as BitLocker enabled, minimum OS version, and Antivirus to name a few. However, if the setting (or state) you want to capture isn't included, Intune provides a way to use PowerShell to detect this information on an endpoint with custom compliance policies.

The example used throughout this post is CVE-2013-3900 which is a WinVerifyTrust flaw that lets a signed binary carry appended data and still pass signature validation. Microsoft shipped the fix as opt-in: the EnableCertPaddingCheck registry value is not set by default. You can push down the registry fix with a Platform script, Win32 App, or remediation script with Intune, but without compliance detection in place, you have no way to detect drift. That gap is what a custom compliance policy covers.

Prerequisites

  • Microsoft Intune subscription
  • A text editor (Visual Studio Code is a great open-source editor)

What is a Custom Compliance Policy?

Creating a custom compliance policy in Intune requires a PowerShell detection script as well as a JSON file. Custom compliance policies can be used when the out of the box compliance settings don't satisfy the requirement.

Create a Custom Compliance Detection Script

note

Download the PowerShell detection script and JSON file used in the example below from my GitHub repo

tip

If you want to learn more about how to write your own custom compliance detection script and JSON file, Steve Weiner has a great video on this topic that you can check out here.

All the following steps are performed in the Microsoft Intune admin center.

First we need to create the PowerShell detection script and JSON file that will be used for the custom compliance policy. The PowerShell script will confirm that the EnableCertPaddingCheck registry value is set to 1, and the JSON file will define the compliance policy configuration.

  1. Select Devices > Manage Devices > Compliance > Scripts > + Add > Windows 10 and later
  2. Under the Basics tab, provide a name and description for the script, then click Next.
    • Name: WinVerifyTrust Signature Validation Required
  3. Under Settings configure the following:
    • Detection script: If following along with the example, use the Get-WinVerifyTrustCompliance.ps1 PowerShell script from the GitHub repo linked above.
    • Run this script using the logged on credentials: No
    • Enforce script signature check: No
    • Run script in 64 bit PowerShell Host: Yes
  4. Click Next > Create Creating a custom script

Create a Custom Compliance Policy

  1. Select Devices > Manage Devices > Compliance > + Create Policy

    • Platform: Windows 10 and later
    • Platform type: Windows 10/11 compliance policy
  2. Click Create and configure the following:

    • Name: WinVerifyTrust Compliance Policy
    • Compliance settings: Custom compliance
    • Custom compliance: Require
    • Select your discovery script: Select the WinVerifyTrust Signature Validation Required PowerShell script

    custom compliance json upload

  3. Configure the noncompliance action:

    • Actions for noncompliance: Mark device noncompliant immediately
  4. Assign the appropriate group(s) to the policy and click Create.

How the IME Executes Custom Compliance Detection Scripts

The Intune Management Extension (IME) is the on-device agent that orchestrates the retrieval of the custom compliance detection script from Intune, running the script using the AgentExecutor, and updating the appropriate registry keys to indicate whether the device is compliant or not.

Let's see it in action.

Force the Detection Script to Re-Evaluate

tip

If you want to use a PowerShell script to automate the steps below, take a look at the blog post written by Rudy Ooms on custom compliance policies. His script parses C:\WINDOWS\IMECache\HealthScripts\ to discover the detection script GUID, then deletes the matching registry subkeys.

To bypass the internal 8 hour timer, we're going to speed things up so we can watch what takes place when a custom compliance policy runs in real time. We can do this by performing a few steps.

From the Intune admin portal, navigate to the PowerShell script associated with the compliance policy to locate the GUID of the script. This can be found in the URL path of the script.

From the endpoint, under the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IntuneManagementExtension\SideCarPolicies\Scripts\Execution and the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IntuneManagementExtension\SideCarPolicies\Scripts\Reports registry keys, delete any subkeys that match the GUID of the PowerShell script that is associated with the custom compliance policy that was located earlier.

delete registry execution

tip

Make sure to look under the System registry key 00000000-0000-0000-0000-000000000000 and the user registry key(s) {USER_GUID} for the PowerShell script GUID, and delete any subkeys that match the GUID of the PowerShell script.

If we look in the registry value of the Result registry key before deleting it, it shows the cached result of the last time the IME ran the custom compliance script.

In this case, the last time the IME ran the custom compliance script, the JSON key WinVerifyTrustSigValEnabled that was defined when the policy was configured is set to true, which would indicate the device passed the script validation and is compliant.

delete results registry key

custom compliance json result

Why this works

The IME caches the results of the last time the custom compliance policy script ran in the registry values we just deleted. Now, if we restart the IME service, we can essentially trick the IME service into re-running the compliance script prior to the time set by the hard-coded 8-hour timer.

Here is an example of the hard-coded 8-hour timer at work. Every hour the IME checks to see if the next run time has arrived. If it has, the script will attempt to execute; if not, the IME will check again in another hour. There isn't a way to adjust this 8-hour timer from Intune – that's why it's considered "hard-coded".

8-hour-hard-coded-timer

Restart the IME service by running the following PowerShell one-liners.

Get-Service -DisplayName "Microsoft Intune Management Extension" | Stop-Service
Get-Service -DisplayName "Microsoft Intune Management Extension" | Start-Service

Give the IME around 10 minutes to execute the custom compliance script. If you take a look at the IME logs, you'll notice there is quite a bit of logs being generated after restarting the IME service. So be patient...

Now that the cached results of the last time the custom compliance policy PowerShell script have been deleted, and after restarting the IME service, there are a few specific IME logs that are of interest to understand what is happening from start to finish when the compliance policy is executed.

IME specific logs of interest are:

  • HealthScripts.log
  • AgentExecutor.log
  • IntuneManagementExtension.log
tip

IME logs are located here: C:\ProgramData\Microsoft\IntuneManagementExtension\Logs

Detection Script Run: Compliant Device

In this first go around, the necessary EnableCertPaddingCheck registry values are set to 1, so the script should return that the device is compliant. But let's take a closer look at the IME logs to get a better idea of what's happening under the hood.

note

If you want to know what is being used in the screenshots below, it's a IME log parser PowerShell script. You can download the script here.

  1. After restarting the IME service, the IntuneManagementExtension locates a cached SideCarGatewayServiceUrl and uses this to check-in with the Intune service for applicable policies and scripts.

    01-sidecar-gateway-session

  2. One more hourly schedule (associated with the hard-coded 8-hour timer) is run before the IME service determines it "will try to execute [the detection script] now".

    02-runner-handoff

  3. Then the IME service runs C:\Program Files (x86)\Microsoft Intune Management Extension\agentexecutor.exe" -remediationScript with the necessary arguments to pass to agentexecutor.exe

    02a-runner-handoff

  4. agentExecutor.exe receives the arguments passed down by the IME service and then invokes PowerShell to run the detection script.

    03-agentexecutor-invocation

  5. The results from the script execution are returned. The log indicates the PreRemediationDetectScriptOutput is "WinVerifyTrustSigValEnabled\":true.

    05-upload-and-cache

    note

    Notice the logs indicate the results version is different from cached one, and to save to cache. This is because the registry values that contained the results of the last time the detection script ran were deleted prior to restarting the IME service.

    different-from-cached

  6. The "new" results are written to the registry.

    results-written-registry

Detection Script Run: Noncompliant Device

In this run, most of the steps covered in the compliant IME run will be the same for the noncompliant run, so we won't cover everything all over again; however, we will go over where things change.

This time, from the registry HKLM:\SOFTWARE\Microsoft\Cryptography\WinTrust\Config\, I deleted the following registry value: EnableCertPaddingCheck so when the custom compliance script re-runs, the device should be evaluated as noncompliant.

As we did previously, the registry subkeys that contain the cached results of the last time the detection script ran are deleted, and the IME service is restarted.

restart-ime-service-powershell

The log output is essentially the same as we went over earlier. The primary difference is the output returned from the detection script. The output indicates that the WinVerifyTrustSigValEnabled JSON key is equal to false

upload-and-cache-noncompliant

There is something interesting here. Notice the log line the pre-remdiation detection script compliance result for bb1d46dc-09b7-478a-a3e3-e50775abef11 is True in the same run, but the device just returned false from the custom compliance policy detection script output... 🤔

This log output is misleading and can trip someone up that is digging through the logs troubleshooting custom compliance scripts. That value is actually set from the detection script's exit code, not from the script's output, and a PowerShell script that runs to completion without an explicit exit returns 0, so it logs True on every successful run.

compliance-result-misleading-noncompliant

The script output results are written to the following registry subkeys: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IntuneManagementExtension\SideCarPolicies\Scripts\Reports\<USER_GUID>\<SCRIPT_GUID>\

Subsequently, the results are shipped back to Intune and the endpoint is marked as noncompliant.

note

As we can see, the misleading log output: pre-remdiation detection script compliance result for bb1d46dc-09b7-478a-a3e3-e50775abef11 is True has no impact on Intune's decision making process when it comes to determining whether a device is compliant or not. Ultimately, Intune uses the custom compliance script output and the JSON file defined to determine what key value pair to extract from the script output to determine compliance.

intune-noncompliant-state

Conclusion

What we've discovered in this post is that if something can be queried with PowerShell, a custom compliance policy can detect drift on it. We also found that a device can sit noncompliant for up to 8 hours before the detection script runs again and the result is shipped back to Intune.

Our best remediation for this problem using native Intune tools would be to configure an Intune Remediation (formerly Proactive Remediations), which can run a detection script and corresponding remediation script (if needed) as frequently as every hour.

Running a Remediation every hour would significantly shorten the gap from drift to remediation; however, even this solution has its drawbacks:

  1. The remediation is essentially running on a CRON job and isn't truly event-driven.
  2. Intune Remediations require a Windows Enterprise or Education license, which is separate from the license that grants Intune itself. At the time of writing, Microsoft requires Windows Enterprise E3 or E5 (included in Microsoft 365 F3, E3, or E5), or Windows Education A3 or A5 (included in Microsoft 365 A3 or A5). Business Premium and EMS do not meet this requirement.

As we continue through this series, we'll explore different solutions to reduce the delta from drift to remediation while minimizing disruption for our users.