As a Managed Service Provider (MSP), you enforce device compliance with Intune’s built-in policies: antivirus running, encryption enabled, OS version current. But your customers have unique requirements that are absolute, non-negotiable conditions for their business operations – requirements that Intune’s standard compliance framework simply cannot validate.
Common requirements Intune can’t natively check:
- Specific vendor EDR software running – CrowdStrike, SentinelOne, BitDefender
- Custom application deployment – RMM tool or other types of agents
- Security hardening policies – Specific service or custom configuration needs to applied for full compliance
- Service functional status – Applications are running and operational, not just installed, but actively running
These aren’t optional configurations – they’re contractual SLAs, regulatory mandates (HIPAA, SOC2), and cyber insurance requirements. This guide shows how Custom Compliance Policies enforce any PowerShell validation as device compliance.
What are Custom Compliance Policies?
Custom Compliance Policies enhance Intune by running PowerShell detection scripts on devices that return JSON key-value pairs. Intune evaluates these results against JSON rules, marking devices compliant or non-compliant. Conditional Access policies can then block access for non-compliant devices.
How it works:
- PowerShell Detection Script runs on device → returns
{"RMMAgentRunning":true} - JSON Compliance Rules define expected values →
"Operand":truemeans compliant whentrue - Intune evaluates → if script returns
false, device marked non-compliant - Conditional Access enforces → non-compliant devices blocked from accessing your tenant
What you can validate:
- File existence, registry keys, service status, application versions
- Certificate expiration, disk space, process monitoring, network configuration
- Third-party security tools, backup agents, monitoring software
- Basically any condition verifiable via PowerShell
Key capabilities:
- Automated remediation messages: Users see custom guidance (“Contact helpdesk to install TeamViewer”)
- Conditional Access integration: Block non-compliant devices from cloud apps automatically
- Continuous validation: Scripts run approximately every 8 hours by default, ensuring compliance status updates regularly. Users or admins can manually trigger a sync to expedite evaluation.
More information can be found here:
Use custom compliance policies and settings for Linux and Windows devices with Microsoft Intune
Configuration
This section demonstrates creating custom compliance policies with PowerShell detection scripts and JSON compliance rules. Each example shows a complete implementation.
Step 1: Create Detection Scripts
The detection script runs on enrolled devices and returns compressed JSON with compliance check results. Below are four examples showing different validation patterns.
Example 1: File Existence Check (TeamViewer Executable)
What It Checks: Verifies a specific file exists on the device (TeamViewer Quick Support executable for remote assistance).
Why It Matters: Ensures critical support tools are deployed before users need them. Intune shows “Installed” for Win32 apps but can’t verify if portable executables or configuration files actually exist on disk.
Detection Script: Detect-TeamViewerFile.ps1
# Check if TeamViewer Quick Support executable exists
$filePath = "C:\Temp\TeamViewerQS_x64.exe"
$fileExists = Test-Path -Path $filePath
if ($fileExists) {
Write-Output "TeamViewer executable found at $filePath"
} else {
Write-Output "TeamViewer executable NOT found at $filePath"
}
# Return result as JSON
$hash = @{
TeamViewerFileExists = $fileExists
}
return $hash | ConvertTo-Json -Compress
Expected PowerShell Output:
{"TeamViewerFileExists":true}
JSON file for Detection
{
"Rules":[
{
"SettingName":"TeamViewerFileExists",
"Operator":"IsEquals",
"DataType":"Boolean",
"Operand":true,
"MoreInfoUrl":"https://www.teamviewer.com/en-us/download/quick-support/",
"RemediationStrings":[
{
"Language":"en_US",
"Title":"TeamViewer executable is not present",
"Description": "The required TeamViewer executable (TeamViewerQS_x64.exe) was not found in C:\\Temp. Please contact the helpdesk for assistance."
}
]
}
]
}
When to Use: Verify deployment of portable executables, installer files, configuration files, or any files that don’t appear in Add/Remove Programs.
Example 2: Service Running Check (Windows Defender)
What It Checks: Verifies a specific Windows service is actively running (Windows Defender Antivirus service).
Why It Matters: Intune’s built-in check reports “antivirus enabled” but doesn’t verify the service is actually running. Users or malware can stop services while still passing standard compliance checks.
Detection Script: Detect-DefenderService.ps1
# Check if Windows Defender Antivirus service is running
$defenderService = Get-Service -Name "WinDefend" -ErrorAction SilentlyContinue
if ($defenderService) {
$defenderRunning = $defenderService.Status -eq "Running"
Write-Output "Windows Defender service status: $($defenderService.Status)"
} else {
$defenderRunning = $false
Write-Output "Windows Defender service not found"
}
# Return result as JSON
$hash = @{
WindowsDefenderRunning = $defenderRunning
}
return $hash | ConvertTo-Json -Compress
Expected PowerShell Output:
{"WindowsDefenderRunning":true}
JSON file for detection
{
"Rules":[
{
"SettingName":"WindowsDefenderRunning",
"Operator":"IsEquals",
"DataType":"Boolean",
"Operand":true,
"MoreInfoUrl":"https://support.microsoft.com/en-us/windows/stay-protected-with-windows-security",
"RemediationStrings":[
{
"Language":"en_US",
"Title":"Windows Defender service is not running",
"Description": "The Windows Defender Antivirus service (WinDefend) is not running. Please contact the helpdesk to ensure your device is protected."
}
]
}
]
}
When to Use: Verify antivirus services, backup agents, monitoring tools, EDR solutions (CrowdStrike, SentinelOne), or any service-based software must be actively running.
Example 3: Service Stopped Check (Print Spooler)
What It Checks: Verifies a specific Windows service is stopped/disabled (Print Spooler for PrintNightmare mitigation).
Why It Matters: Security hardening policies require certain services disabled (Print Spooler, Remote Registry, SSDP Discovery). This validates the service stays disabled even if users or malware attempt to re-enable it.
Detection Script: Detect-PrintSpoolerDisabled.ps1
# Check if Print Spooler service is stopped (security best practice)
$spoolerService = Get-Service -Name "Spooler" -ErrorAction SilentlyContinue
if ($spoolerService) {
$spoolerStopped = $spoolerService.Status -eq "Stopped"
Write-Output "Print Spooler service status: $($spoolerService.Status)"
} else {
# If service doesn't exist, consider it "stopped" (compliant)
$spoolerStopped = $true
Write-Output "Print Spooler service not found (compliant)"
}
# Return result as JSON
$hash = @{
PrintSpoolerStopped = $spoolerStopped
}
return $hash | ConvertTo-Json -Compress
Expected PowerShell Output:
{"PrintSpoolerStopped":true}
JSON file for Detection
{
"Rules":[
{
"SettingName":"PrintSpoolerStopped",
"Operator":"IsEquals",
"DataType":"Boolean",
"Operand":true,
"MoreInfoUrl":"https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/print-spooler",
"RemediationStrings":[
{
"Language":"en_US",
"Title":"Print Spooler service is running",
"Description": "The Print Spooler service is running. For security reasons, this service should be disabled on devices that don't require printing. Please contact the helpdesk."
}
]
}
]
}
When to Use: Enforce security hardening policies per CIS Benchmarks, NIST guidelines, or Zero Trust requirements. Verify prohibited services are disabled.
Example 4: Combined File and Service Check (Backup Agent)
What It Checks: Performs two validations in one policy – verifies Veeam executable file exists AND the backup service is running.
Why It Matters: Intune’s install status confirms deployment but doesn’t guarantee the application or service is running. Custom Compliance Policies verify both installation and active operation.
Detection Script: Detect-VeeamBackup.ps1
# Check if Veeam Backup executable exists
$veeamPath = "C:\Program Files\Veeam\Endpoint Backup\Veeam.EndPoint.Manager.exe"
$veeamFileExists = Test-Path -Path $veeamPath
if ($veeamFileExists) {
Write-Output "Veeam executable found at $veeamPath"
} else {
Write-Output "Veeam executable NOT found at $veeamPath"
}
# Check if Veeam Backup service is running
$veeamService = Get-Service -Name "VeeamEndpointBackupSvc" -ErrorAction SilentlyContinue
if ($veeamService) {
$veeamServiceRunning = $veeamService.Status -eq "Running"
Write-Output "Veeam service status: $($veeamService.Status)"
} else {
$veeamServiceRunning = $false
Write-Output "Veeam service not found"
}
# Return both results as JSON
$hash = @{
VeeamFileExists = $veeamFileExists
VeeamServiceRunning = $veeamServiceRunning
}
return $hash | ConvertTo-Json -Compress
Expected PowerShell Output:
{"VeeamFileExists":true,"VeeamServiceRunning":true}
JSON file for Detection
{
"Rules":[
{
"SettingName":"VeeamFileExists",
"Operator":"IsEquals",
"DataType":"Boolean",
"Operand":true,
"MoreInfoUrl":"https://www.veeam.com/endpoint-backup-free.html",
"RemediationStrings":[
{
"Language":"en_US",
"Title":"Veeam Backup not installed",
"Description": "Veeam Endpoint Backup is not installed on your device. Your data is not being backed up. Please contact the helpdesk immediately to install backup software."
}
]
},
{
"SettingName":"VeeamServiceRunning",
"Operator":"IsEquals",
"DataType":"Boolean",
"Operand":true,
"MoreInfoUrl":"https://www.veeam.com/endpoint-backup-free.html",
"RemediationStrings":[
{
"Language":"en_US",
"Title":"Veeam Backup service not running",
"Description": "Veeam Endpoint Backup is installed but the service is not running. Your data may not be protected. Please contact the helpdesk to restart the backup service."
}
]
}
]
}
When to Use: Comprehensive validation of critical software requiring both installation AND active operation. Perfect for backup agents, monitoring tools, EDR solutions, or any service-based application where “installed” doesn’t guarantee “working.”
Real-World Scenarios:
- Backup verification: File exists + service running + last backup timestamp < 24 hours
- EDR deployment: CrowdStrike executable exists + CSFalconService running + sensor version >= 7.10
- Monitoring agents: DataDog agent file exists + service running + API connectivity check
- VPN clients: Cisco AnyConnect installed + VPN service running + certificate not expired
Key Concepts:
- SettingName Matching: The
SettingNamein the JSON compliance rules must exactly match the key name from the PowerShell output, including case sensitivity. Any mismatch or invalid JSON schema will cause the compliance policy to fail. - Boolean Logic: When
Operand:true, the device is compliant when PowerShell returnstrue. WhenOperand:false, compliant when PowerShell returnsfalse - Multiple Rules: Each detection script returns multiple key-value pairs evaluated by individual JSON rules. Devices must pass every rule to be compliant.
- Inverse Checks: Example 3 shows “stopped is good” – we want
PrintSpoolerStopped:true(meaning the service IS stopped) - Error Handling:
-ErrorAction SilentlyContinuein PowerShell to prevent script failure when checking services or files that might not exist, ensuring the script runs reliably.
Step 2: Define Compliance Rules
The JSON compliance rules define what values are expected from the detection script and what messages users see when non-compliant. Each rule matches a SettingName from the PowerShell script output.
JSON Rule Properties:
- SettingName: Must exactly match the key name from PowerShell hash output
- Operator:
IsEquals,NotEquals,GreaterThan,LessThan,GreaterEquals,LessEquals - DataType:
Boolean,Int64,String,DateTime,Version - Operand: Expected value for compliance (device is compliant when SettingName matches Operand)
- MoreInfoUrl: Link shown in Company Portal for self-service remediation
- RemediationStrings: User-facing messages (supports multiple languages)
Step 3: Deploy the Policy to Intune
Now we’ll create the Intune compliance policy and upload both the PowerShell detection script and JSON compliance rules.
Upload the Powershell Script
- Sign in to the Microsoft Intune admin center as an Intune Administrator.
- Navigate to Devices > Compliance policies > Scripts.
- Select Platform: Windows 10 and later.
- Enter the Name and Version for your compliance script.
- Paste your PowerShell detection script into the script editor.
- Configure the following options:
- Run this script using the logged-on user credentials: Yes / No
- Enforce script signature check: Yes / No
- Run script in 64-bit PowerShell Host: Yes / No
- Click Next.
- Click Create.


Create the Custom Compliance Policy
- Sign in to the Microsoft Intune admin center as an Intune Administrator.
- Go to Devices > Compliance policies > Policies.
- Click Create Policy.
- Select Platform: Windows 10 and later.
- On the Basics tab:
- Name:
TeamViewer - Description:
Validate if the QuickSupport executable exists on the device - Click Next.
- Name:
- On the Compliance settings tab:
- Expand Custom Compliance.
- Click Select custom compliance settings.
- Choose your Discovery Script.
- Upload the corresponding Compliance JSON file that matches your script.
- Click Next.
- On the Actions for noncompliance tab:
- Set Default action to Mark device noncompliant (Immediate).
- Optional: Add Send email to end user (after 1 day).
- Optional: Add Send push notification to end user (Immediate).
- Click Next.
- On the Assignments tab:
- Assign to All devices or choose specific groups as needed.
Click Next. - On the Review + create tab:
- Assign to All devices or choose specific groups as needed.
- Verify all settings.
- Click Create.


Deployment Timeline:
- Intune Management Extension (IME) runs detection scripts roughly every 8 hours. Compliance updates occur after each run.
- Users can manually sync devices to speed up compliance evaluation.
NOTE: Before the detection script completes its first run, devices may show compliance as “Not applicable” in Intune. This temporary state clears once compliance data is received.
Conclusion
Custom Compliance Policies extend Intune’s built-in compliance framework to enforce customer-specific requirements that Microsoft’s standard checks can’t validate. Instead of accepting predefined compliance rules, you define exactly what “compliant” means using PowerShell detection scripts that runs on enrolled devices.
What this enables:
- Vendor-specific software validation: Verify CrowdStrike, SentinelOne, or BitDefender is running
- File and service verification: Confirm applications are deployed AND functional, catching broken installations where services fail to start
- Security hardening enforcement: Continuously validate Print Spooler stays disabled, prohibited services remain stopped
- Conditional Access integration: Automatically block non-compliant devices from accessing SharePoint, Exchange, VPN, or custom apps
The backup agent example demonstrates the power: Intune shows “Installation Success” but can’t verify the backup service actually started. Custom Compliance checks both file existence and service status, blocking access until both conditions pass. Compliance becomes continuously validated, not assumed.
For MSPs managing customers with contractual SLAs, regulatory requirements (HIPAA, SOC2), or cyber insurance mandates, Custom Compliance Policies transform compliance from documentation into automated technical enforcement.