In my work with Microsoft Intune, I often try to leverage built-in features as much as possible to simplify management and reduce complexity. However, there are cases where I’ve seen the need to go beyond the defaults, especially when using tools outside the Microsoft ecosystem. For example, organizations relying on a third-party antivirus solution or vulnerability scanner might have specific compliance requirements that aren’t covered by Intune’s standard options.
While I try to avoid custom compliance policies whenever possible, there are situations where they make perfect sense. These policies provide the flexibility to enforce tailored checks, ensuring your devices align with even the most unique requirements. However, it’s worth noting that custom compliance policies require Intune’s Enterprise license, as they rely on the remediation functionality.
Table of Contents
- Why Custom Compliance Policies?
- How the Custom Compliance works
- Generic Example with Multiple Outputs
- Example Use Cases
- Optimizing Compliance Check Intervals
- Leveraging Conditional Access
- Conclusion
Why Custom Compliance Policies?
Default compliance policies in Intune cover common requirements, such as OS version checks, encryption, and password enforcement. But what if you need to:
- Verify that a third-party service, like an antivirus agent, is running?
- Check for the presence of a registry key applied by a vulnerability scanner?
- Ensure specific files or configurations exist on devices?
These are scenarios where custom compliance policies shine, offering flexibility to meet niche requirements.
That said, keep in mind that custom compliance checks, including scripts, typically run every 8 hours by default. If you need more frequent checks, this can be tweaked by deploying a Scheduled Task using PowerShell, a workaround I’ll detail later.
How the Custom Compliance works
Before we dive into the use cases themselves, I’ll show you how to create these custom compliance policies. A custom compliance policy always consists of two parts:
- A PowerShell script as the detection mechanism.
- A JSON file specifying the validations for the script’s output. This determines how the status is displayed both in the compliance policy itself and in the Company Portal for the user. Optionally, you can add a description text and help link for user guidance.
JSON Parameter Details
The JSON file defines the validation rules and user guidance. Here’s an explanation of each parameter and its options:
- SettingName: The name of the custom setting to validate compliance. This name is case-sensitive and must match the key in the PowerShell script output.
- DataType: Specifies the data type of the compliance check result. Supported options include:
Boolean
Int64
Double
String
DateTime
Version
- Operator: Defines the comparison method for compliance checks. Supported options include:
IsEquals
NotEquals
GreaterThan
GreaterEquals
LessThan
LessEquals
- Operand: Represents the value the operator works on. For example,
true
for a Boolean or1
for an integer. - MoreInfoUrl: A link shown to users with noncompliant devices to guide them on resolving the issue (e.g., internal documentation).
- RemediationStrings: Provides detailed guidance in the Company Portal when a device is noncompliant. Includes:
Language
: Specifies the language (e.g.,en_US
).
(all languages you can find here: https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/available-language-packs-for-windows?view=windows-11)Title
: A short description of the issue, including the actual noncompliant value ({ActualValue}
).Description
: Detailed remediation steps for the user.
Here's how the end user sees the notifications from the Remediation String in case a policy is not complaint:
How to Add Custom Compliance Policies
To create and deploy a custom compliance policy:
- Write a PowerShell script to perform the desired check.
- Navigate to Devices > Compliance policies > Policies in Intune.
- Click + Create Policy and select your platform (e.g., Windows 10 and later).
- Under Compliance settings, choose Custom compliance.
- Upload the PowerShell script and set compliance rules (e.g.,
True
= compliant,False
= non-compliant). - Assign the policy to the relevant user or device group.
Deploy and Test
- Assign the policy to a small test group first to ensure it works as expected.
- Monitor compliance results in Intune under Devices > Monitor > Device compliance.
Generic Example with Multiple Outputs
Before diving into specific use cases, let’s build a generic example where the PowerShell script produces multiple outputs, and the JSON file validates two different conditions.
PowerShell Script
The script below demonstrates a setup with multiple checks:
# Example script with multiple outputs
$PSlogicOutput1 = $true # Replace with logic for Check1
$PSlogicOutput2 = 3 # Replace with logic for Check2
$output = @{
Check1 = $PSlogicOutput1
Check2 = $PSlogicOutput2
}
return $output | ConvertTo-Json -Compress
Validation JSON
The corresponding JSON validates two conditions based on the script’s output:
{
"Rules": [
{
"SettingName": "Check1",
"Operator": "IsEquals",
"DataType": "Boolean",
"Operand": "true",
"MoreInfoUrl": "https://scloud.work/custom-compliance-windows-intune",
"RemediationStrings": [
{
"Language": "en_US",
"Title": "Check1 is not compliant, instead it is {ActualValue}.",
"Description": "Make sure you enable XYZ or contact your support organisation."
}
]
},
{
"SettingName": "Check2",
"Operator": "IsEquals",
"DataType": "Int64",
"Operand": "1",
"MoreInfoUrl": "https://scloud.work/custom-compliance-windows-intune",
"RemediationStrings": [
{
"Language": "en_US",
"Title": "Check2 is not compliant, instead it is {ActualValue}.",
"Description": "Make sure you enable XYZ or contact your support organisation."
}
]
}
]
}
This approach can be adapted for use cases involving multiple logic checks, ensuring comprehensive validation within a single policy.
Example Use Cases
The following examples showcase practical implementations of custom compliance policies using PowerShell scripts and JSON validations. These use cases illustrate how you can address specific compliance requirements in your organization, building on the concepts introduced earlier.
Checking If a Service Is Running
This is just an example, but similar checks could be used for services like CrowdStrike, SentinelOne, Qualys, Rapid7, or Trend Micro.
PowerShell Script
The script below checks if the Windows Update service (wuauserv) is running and outputs True
(compliant) or False
(non-compliant):
# Check if the Windows Update service is running
$ServiceName = "wuauserv"
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
$output = @{
WindowsUpdateService = if($Service.Status -eq "Running"){ $true }else{ $false }
}
return $output | ConvertTo-Json -Compress
Validation JSON
{
"Rules": [
{
"SettingName": "WindowsUpdateService",
"Operator": "IsEquals",
"DataType": "Boolean",
"Operand": "true",
"MoreInfoUrl": "https://scloud.work/service-check",
"RemediationStrings": [
{
"Language": "en_US",
"Title": "The WindowsUpdateService is not running",
"Description": "Restart your computer and if the error persists, contact your support organisation."
}
]
}
]
}
Checking for a Registry Key
PowerShell Script
Another common scenario is verifying that a specific registry key exists. For instance, a vulnerability scanner might write a key indicating it’s up-to-date.
# Check a registry key
$RegistryPath = "HKLM:\SOFTWARE\CustomSecurity"
$propertyName = "Version"
$output = @{
RegistryCheck = (Get-ItemProperty -Path $RegistryPath -Name $propertyName).$propertyName
}
return $output | ConvertTo-Json -Compress
Validation JSON
{
"Rules": [
{
"SettingName": "RegistryCheck",
"Operator":"IsEquals",
"DataType":"Int64",
"Operand":1,
"MoreInfoUrl": "https://scloud.work/registry-check",
"RemediationStrings": [
{
"Language": "en_US",
"Title": "RegistryCheck is not compliant, instead of 1 it is {ActualValue}.",
"Description": "Contact your support organisation."
}
]
}
]
}
Checking for a Specific File
PowerShell Script
Sometimes, you might need to ensure that a particular file exists on devices, such as a configuration file required by third-party software.
# Check if a specific file exists
$FilePath = "C:\ProgramData\CustomApp\config.xml"
$output = @{
FileCheck = if (Test-Path $FilePath) { $true } else { $false }
}
return $output | ConvertTo-Json -Compress
Validation JSON
{
"Rules": [
{
"SettingName": "FileCheck",
"Operator": "IsEquals",
"DataType": "Boolean",
"Operand": "true",
"MoreInfoUrl": "https://scloud.work/file-check",
"RemediationStrings": [
{
"Language": "en_US",
"Title": "FileCheck is not compliant, instead it is {ActualValue}.",
"Description": "Ensure the required file is present or contact your support organisation."
}
]
}
]
}
Optimizing Compliance Check Intervals
By default, Intune runs custom compliance checks every 8 hours. However, for scenarios requiring more frequent checks, you can enforce a custom compliance sync using the synccompliance extension of the Intune Management Extension. This method, as described by Rudy Oomy, ensures compliance scripts are run without affecting other Intune sync processes.
PowerShell Script for Immediate Compliance Sync
The following PowerShell script triggers the compliance check using the synccompliance
URI of the Intune Management Extension:
# Trigger a custom compliance sync
Start-Process -FilePath "C:\Program Files (x86)\Microsoft Intune Management Extension\Microsoft.Management.Services.IntuneWindowsAgent.exe" `
-ArgumentList "intunemanagementextension://synccompliance"
Note: This script focuses solely on syncing custom compliance policies and their statuses. It does not perform the same action as clicking "Check Access" in the Company Portal.
Automating with a Scheduled Task
To make this process automated and recurring, you can create a scheduled task that triggers the compliance check every 60 minutes (or a frequency suitable for your organization):
# Schedule the compliance sync task
$repeat = (New-TimeSpan -Minutes 60)
$trigger = New-JobTrigger -Once -At (Get-Date).Date -RepeatIndefinitely -RepetitionInterval $repeat
$User = "SYSTEM"
$Action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-ex bypass -encodedcommand UwB0AGEAcgB0AC0AUAByAG8AYwBlAHMAcwAgAC0ARgBpAGwAZQBQAGEAdABoACAAIgBDADoAXABQAHIAbwBnAHIAYQBtACAARgBpAGwAZQBzACAAKAB4ADgANgApAFwATQBpAGMAcgBvAHMAbwBmAHQAIABJAG4AdAB1AG4AZQAgAE0AYQBuAGEAZwBlAG0AZQBuAHQAIABFAHgAdABlAG4AcwBpAG8AbgBcAE0AaQBjAHIAbwBzAG8AZgB0AC4ATQBhAG4AYQBnAGUAbQBlAG4AdAAuAFMAZQByAHYAaQBjAGUAcwAuAEkAbgB0AHUAbgBlAFcAaQBuAGQAbwB3AHMAQQBnAGUAbgB0AC4AZQB4AGUAIgAgAC0AQQByAGcAdQBtAGUAbgB0AEwAaQBzAHQAIAAiAGkAbgB0AHUAbgBlAG0AYQBuAGEAZwBlAG0AZQBuAHQAZQB4AHQAZQBuAHMAaQBvAG4AOgAvAC8AcwB5AG4AYwBjAG8AbQBwAGwAaQBhAG4AYwBlACIACgAKAAoACgA="
Register-ScheduledTask -TaskName "Custom Compliance Sync" -Trigger $Trigger -User $User -Action $Action -Force
Set-ScheduledTask -TaskName "Custom Compliance Sync" -Settings $(New-ScheduledTaskSettingsSet -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries)
This ensures the device checks for compliance more frequently and becomes non-compliant (if applicable) within the specified interval.
Credits: Special thanks to Rudy Oomy for describing this method of enforcing compliance checks. You can find more of Rudy's work and insights on his blog: https://call4cloud.nl/custom-compliance-policy-intune/#6_Scheduling_the_Custom_Compliance_Check
Leveraging Conditional Access
One of the main benefits of custom compliance policies is their seamless integration with Conditional Access. For example:
- If a device fails the compliance check (e.g., the antivirus service isn’t running), it’s marked as non-compliant.
- Conditional Access can then block the device from accessing corporate resources until the issue is resolved.
This creates an additional layer of security and ensures only healthy devices access your organization’s data.
Conclusion
Custom compliance policies are a powerful tool for addressing unique requirements in your Intune environment. While I recommend sticking to built-in compliance features whenever possible, there are cases where custom policies make perfect sense, especially when dealing with third-party tools or specific configurations.
By following the steps above, you can create tailored policies that align with your organization’s needs. Just remember to test thoroughly, monitor compliance results, and optimize intervals if needed. Have you used custom compliance policies in your setup? Share your experiences in the comments, I’d love to hear how they’ve helped you!
All Scripts in this post you find as well on my github to download.