Skip to main content

Detailed Compliance for Non-Microsoft Antivirus Solutions

·
Intune Windows 10 and later Compliance Security Antivirus PowerShell
Author
Nick Benton
Principal Cloud Endpoint Consultant | Intune Blogger
Table of Contents

So what happens when you’re not using Windows Defender on your Windows 10 and later Microsoft Intune enrolled devices, and you’re not happy with the basic compliance checks for Third-Party Antivirus products?

Microsoft have come to the rescue with their Custom Compliance Settings, so let’s utilise this detect and check policy, and leverage it to detect and report on Non-Microsoft Antivirus products, their real time protection status, as well as whether the definitions are up to date.

Custom Compliance Policies
#

As Custom Compliance isn’t that new a feature of Microsoft Intune, I’m not going to deep dive into the process, a number of other people have done that already:

We’re going to focus on how to use it to achieve our goal, detecting additional information about third-party Antivirus solutions.

PowerShell and WMI
#

We need a way to get information about the expected active Antivirus product, in this instance ‘Sophos Antivirus’, installed on device; to do this we can use the WMI Class AntiVirusProduct within the root\SecurityCenter2 namespace using either:

Get-WmiObject -Namespace "root\SecurityCenter2" -Class AntiVirusProduct

Or

Get-CimInstance -Namespace "root\SecurityCenter2" -Class AntiVirusProduct

This will pull back details about the Antivirus software registered by Windows, as you can see from the below, it will pull back all products registered, including Windows Defender.

__GENUS                  : 2
__CLASS                  : AntiVirusProduct
__SUPERCLASS             :
__DYNASTY                : AntiVirusProduct
__RELPATH                : AntiVirusProduct.instanceGuid="{8E0623B8-CF1C-DFFE-CEA3-AA41BDA4B8EE}"
__PROPERTY_COUNT         : 6
__DERIVATION             : {}
__SERVER                 : LT01540
__NAMESPACE              : ROOT\SecurityCenter2
__PATH                   : \\COMPUTERNAME\ROOT\SecurityCenter2:AntiVirusProduct.instanceGuid="{8E0623B8-CF1C-DFFE-CEA3-AA41BDA4B8EE}"
displayName              : Sophos Anti-Virus
instanceGuid             : {8E0623B8-CF1C-DFFE-CEA3-AA41BDA4B8EE}
pathToSignedProductExe   : C:\Program Files (x86)\Sophos\Sophos Anti-Virus\WSCClient.exe
pathToSignedReportingExe : C:\Program Files (x86)\Sophos\Sophos Anti-Virus\WSCClient.exe
productState             : 331776
timestamp                : Tue, 16 Nov 2021 17:29:40 GMT

__GENUS                  : 2
__CLASS                  : AntiVirusProduct
__SUPERCLASS             :
__DYNASTY                : AntiVirusProduct
__RELPATH                : AntiVirusProduct.instanceGuid="{D68DDC3A-831F-4fae-9E44-DA132C1ACF46}"
__PROPERTY_COUNT         : 6
__DERIVATION             : {}
__SERVER                 :
__NAMESPACE              : ROOT\SecurityCenter2
__PATH                   : \\\ROOT\SecurityCenter2:AntiVirusProduct.instanceGuid="{D68DDC3A-831F-4fae-9E44-DA132C1ACF46}"
displayName              : Windows Defender
instanceGuid             : {D68DDC3A-831F-4fae-9E44-DA132C1ACF46}
pathToSignedProductExe   : windowsdefender://
pathToSignedReportingExe : %ProgramFiles%\Windows Defender\MsMpeng.exe
productState             : 393472
timestamp                : Thu, 16 Dec 2021 10:49:39 GMT

So we will need a way to filter only to the expected active Antivirus software installed.

Antivirus Product State
#

With the information available about the Antivirus products, we need to be able to identify whether Real Time Protection is running, as well as whether the Definitions are up to date. I’d say luckily, but I’d be lying, we have the productState information to work with.

productState             : 393472

This hex value does translate into something we can use to identify the definition and protection status checks, and thanks to Marc Schneider we can actually understand this number and how to use it as part of a Custom Compliance policy.

The six-digit value for ‘productState’ can be broken down into three pairs of two-digits, with each actually meaning something once converted to a hex string:

  • 1st Pair: Product Type
  • 2nd Pair: Real Time Protection Status
  • 3rd Pair: Definition Status
The 2nd and 3rd pair may report differently depending on your Antivirus product, as detailed in this post, so you will have to check this on a device with up-to-date definitions as well as real time protection enabled to be sure.

The values for each of these pairs for Sophos Antivirus can be seen below:

Item Value Description
Real Time Protection Status 00 Off
01 Expired
10 On
11 Snoozed
Definition Status 00 Up to Date
10 Out of Date

So we now have a way to identify and confirm that both Real Time protection is enabled, and Definitions are in place for the Antivirus product of choosing.

Detection Script Creation
#

With a couple of variables, some hex conversion, switches, and a little bit of logic, we can get the information we need and throw it into the JSON format required by Microsoft to allow the Custom Compliance script to work as expected.

We’ve used the $AVClient variable, which will allow re-use of the script depending on which product is installed across your device estate.

We also have to be able to capture and present back when the script doesn’t detect the specified $AVClient variable, otherwise we’ll get some grim looking compliance errors on those devices.

These commands and scripts have now been updated to use Get-CimInstance due to Microsoft deprecating WMIC commands.
$avClient = 'Sophos Anti-Virus'
$avProduct = Get-CimInstance -Namespace 'root\SecurityCenter2' -Class AntiVirusProduct | Where-Object { $_.displayName -eq $avClient } | Select-Object -First 1
#$avProduct = Get-WmiObject -Namespace 'root\SecurityCenter2' -Class AntiVirusProduct | Where-Object { $_.displayName -eq $avClient } | Select-Object -First 1
$avSummary = New-Object -TypeName PSObject

If ($avProduct) {
    $hexProductState = [Convert]::ToString($avProduct.productState, 16).PadLeft(6, '0')
    $hexRealTimeProtection = $hexProductState.Substring(2, 2)
    $hexDefinitionStatus = $hexProductState.Substring(4, 2)

    $realTimeProtectionStatus = switch ($hexRealTimeProtection) {
        '00' { 'Off' }
        '01' { 'Expired' }
        '10' { 'On' }
        '11' { 'Snoozed' }
        default { 'Unknown' }
    }

    $definitionStatus = switch ($hexDefinitionStatus) {
        '00' { 'Up to Date' }
        '10' { 'Out of Date' }
        default { 'Unknown' }
    }

    $avSummary | Add-Member -MemberType NoteProperty -Name "$avClient" -Value $avProduct.displayName
    $avSummary | Add-Member -MemberType NoteProperty -Name "$avClient real time protection enabled" -Value $realTimeProtectionStatus
    $avSummary | Add-Member -MemberType NoteProperty -Name "$avClient definitions up-to-date" -Value $definitionStatus
}
Else {
    $avSummary | Add-Member -MemberType NoteProperty -Name "$avClient" -Value 'Error: No Antivirus product found'
    $avSummary | Add-Member -MemberType NoteProperty -Name "$avClient real time protection enabled" -Value 'Error: No Antivirus product found'
    $avSummary | Add-Member -MemberType NoteProperty -Name "$avClient definitions up-to-date" -Value 'Error: No Antivirus product found'
}

return $avSummary | ConvertTo-Json -Compress

Running this script on a machine with the Antivirus product installed would be a good idea to test that the scripts works, and is outputting the correct information in the required format for Intune to translate.

The uncompressed output from the script should look a little bit like the below:

{
    "Sophos Anti-Virus": "Sophos Anti-Virus",
    "Sophos Anti-Virus real time protection enabled": "On",
    "Sophos Anti-Virus definitions up-to-date": "Up to Date"
}

With the compressed version looking like this:

{"Sophos Anti-Virus":"Sophos Anti-Virus","Sophos Anti-Virus real time protection enabled":"On","Sophos Anti-Virus definitions up-to-date":"Up to Date"}

JSON Checks
#

As we’ve seen in the JSON check requirements from Microsoft, we now need to build out a JSON file using the template as a starter, making sure that our JSON output from the script can be translated into something the Custom Compliance Policy can use.

Below is the updated JSON content we can use for the check, I’ve kept this quite light touch if I’m honest, and not specific to the Antivirus product, again so this can be re-used without changing a million things.

You will need to change the Operand for the first Rule to match the Antivirus product name you have installed, and if you want to, feel free to update the following fields to your taste:

  • MoreInfoUrl
  • Title
  • Description
Updated to include improved presentation to the end user of the issues as displayed in the Company Portal.
{
    "Rules": [
        {
            "SettingName": "Sophos Anti-Virus",
            "Operator": "IsEquals",
            "DataType": "String",
            "Operand": "Sophos Anti-Virus",
            "MoreInfoUrl": "https://support.home.sophos.com/hc/en-us/categories/115001242663-Installing-Sophos-Home",
            "RemediationStrings": [
                {
                    "Language": "en_US",
                    "Title": "Sophos Anti-Virus was not detected.",
                    "Description": "You must have Sophos Antivirus installed on your device to protect it from malware."
                }
            ]
        },
        {
            "SettingName": "Sophos Anti-Virus real time protection enabled",
            "Operator": "IsEquals",
            "DataType": "String",
            "Operand": "On",
            "MoreInfoUrl": "https://support.home.sophos.com/hc/en-us/articles/115005596186-Configuring-Real-Time-Protection",
            "RemediationStrings": [
                {
                    "Language": "en_US",
                    "Title": "Sophos Antivirus real time protection is not enabled",
                    "Description": "Real time protection must be enabled in Sophos to protect your device, please enable this setting."
                }
            ]
        },
        {
            "SettingName": "Sophos Anti-Virus definitions up-to-date",
            "Operator": "IsEquals",
            "DataType": "String",
            "Operand": "Up to Date",
            "MoreInfoUrl": "https://kb.mit.edu/confluence/pages/viewpage.action?pageId=152584581#:~:text=By%20default%2C%20Sophos%20Anti%2DVirus,as%20detailed%20%E2%80%93%20is%20not%20necessary.",
            "RemediationStrings": [
                {
                    "Language": "en_US",
                    "Title": "Sophos Antivirus definitions are not up to date.",
                    "Description": "Please update the Sophos Antivirus definitions to ensure you device is protected from malware."
                }
            ]
        }
    ]
}

Deploying Custom Compliance
#

We’ve now got both a PowerShell script and JSON file, so the last steps are to throw these into Microsoft Intune.

First off we need to add the PowerShell script in the Compliance Scripts section.

Compliance Script
Compliance Policy script in Microsoft Intune.

Following this, we can now create our Custom Compliance policy for Windows; select the previously created PowerShell script, and we’ll need to upload the JSON file created earlier.

Custom Compliance
Compliance Policy in Microsoft Intune.

Once we’ve got this in place, we can now assign the Compliance Policy.

Checking Compliance State
#

With the Custom Compliance policy deployed, and waiting a little while for devices to start reporting back, we can check on the status of the devices it has been assigned to.

We have some good devices:

Good Compliance
Microsoft Intune Compliance check with good results.

Some not so good:

Poor Compliance
Microsoft Intune Compliance check with poor results.

Some truly problematic:

Bad Compliance
Microsoft Intune Compliance check with bad results.

With this Compliance Policy now in place, we not only get a view of the device estate, but can integrate Compliance into Conditional Access Policies, so it’s not a bad situation to be in at all.

Summary
#

Custom Compliance does seem like a bit of an effort, and it currently has minimal operators for the JSON check, an inability to add more than one PowerShell script to a single policy, an eight hour wait for the Compliance state to update after remediation, and a few other limitations, but for those organisations that aren’t fully in bed with Microsoft when it comes down to endpoint protection products, it does offer a way to extend compliance to these solutions.


Related

Automating Endpoint Privilege Management Policies with PowerShell
Intune Windows 10 and later Security Settings Catalog Endpoint Privilege Management Graph API PowerShell Automation Endpoint Security
Updating Apple Operating System Compliance Policies
Intune iOS/iPadOS macOS Compliance Software Updates Graph API PowerShell Automation
Co-Managing Windows Autopilot Hybrid Join Devices
Intune Configuration Manager Windows 10 and later Windows Autopilot Remediation Scripts PowerShell Hybrid Entra Join Co-management