Understanding custom Intune compliance policies

A new feature within Intune, currently in Preview is the ability to create a custom compliance policy to give a bit more granular control over what is allowed to connect.

You could require certain apps to be installed (or not installed), a minimum BIOS version, or a particular service needs to be running. If it can be queried with PowerShell, it can be used.

There are plenty of excellent examples on the web so for this post I’m going to run through how it works to hopefully make it easier to understand.

At a top level, there are two files, a powershell script and a JSON file. The PowerShell runs against the device to grab the information you are looking for. The JSON file is the reference point for the script, in the background, the output from the PowerShell (also in JSON format) is compared to the reference file and passes through or flags as non-compliant.

The PowerShell script

Starting with the PowerShell script, the thing to note here is it has to output as a single line JSON file with the last line of the script creating the JSON

return $hash | ConvertTo-Json -Compress

The $hash variable can, however contain an array of values.

As a very basic example, let’s say that you only want to allow Dell computers with 8Gb RAM and you DON’T want any machines with Steam installed

First up, we need to detect the manufacturer and total RAM:

$biosinfo = Get-CimInstance -ClassName Win32_ComputerSystem
$manufacturer = $biosinfo.Manufacturer
$RAM = $biosinfo.TotalPhysicalMemory

Now, clearly we need to pass these back in a reasonable fashion, trim the RAM to exact Gb and make sure the manufacturer is just listed as Dell

#Check if it's a Dell
if ($manufacturer -like "*Dell*") {
    $manufacturer = "Dell"
}
else {
    $manufacturer = "Unknown"
}

$RAM =  ($RAM / 1024 / 1024)
$RAM = [math]::Round($RAM, 0)

Now, let’s check if Steam is installed. Normally I would do a quick get-wmiobject, but for some reason Steam doesn’t display in there so we’ll query the registry instead:

#Look for Steam
$InstalledSoftware = Get-ChildItem "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
if ($InstalledSoftware -like "*Steam*") {
    $steam = "Detected"
}
else {
    $steam = "Not Detected"
}

Now we have our three variables, we need to put it into a one-liner JSON, we’ll need to remember the headings used for the JSON later:

$hash = @{ Manufacturer = $manufacturer; RAM = $RAM; Steam = $steam}
return $hash | ConvertTo-Json -Compress

The final powershell script looks like this:

##Get BIOS Info
$biosinfo = Get-CimInstance -ClassName Win32_ComputerSystem
#Manufacturer
$manufacturer = $biosinfo.Manufacturer
#Total RAM
$RAM = $biosinfo.TotalPhysicalMemory

#Check if it's a Dell
if ($manufacturer -like "*Dell*") {
    $manufacturer = "Dell"
}
else {
    $manufacturer = "Unknown"
}

#Tidy the RAM
$RAM =  ($RAM / 1024 / 1024)
$RAM = [math]::Round($RAM, 0)

#Look for Steam
$InstalledSoftware = Get-ChildItem "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
if ($InstalledSoftware -like "*Steam*") {
    $steam = "Detected"
}
else {
    $steam = "Not Detected"
}

$hash = @{ Manufacturer = $manufacturer; RAM = $RAM; Steam = $steam}
return $hash | ConvertTo-Json -Compress

JSON File

Now we have our output on the machine, we need something to compare it to, the JSON file, these are fairly straight forward, give it the heading, what it needs to be an then an error message should it be non-compliant (plus a more info URL if wanted)

For the manufacturer:

    { 
       "SettingName":"Manufacturer",
       "Operator":"IsEquals",
       "DataType":"String",
       "Operand":"Dell",
       "MoreInfoUrl":"https://andrewstaylor.com",
       "RemediationStrings":[ 
          { 
             "Language":"en_US",
             "Title":"This machine is not a Dell.",
             "Description": "We only support Dell devices, please contact us for more information."
          }
       ]
    }

As you can see, we use the setting name from the hash and make sure it equals Dell as a string. The JSON uses standard Boolean logic, this link from Microsoft shows the supported types, operators, languages and required settings

Putting our example together:

{
"Rules":[ 
    { 
       "SettingName":"Manufacturer",
       "Operator":"IsEquals",
       "DataType":"String",
       "Operand":"Dell",
       "MoreInfoUrl":"https://andrewstaylor.com",
       "RemediationStrings":[ 
          { 
             "Language":"en_US",
             "Title":"This machine is not a Dell.",
             "Description": "We only support Dell devices, please contact us for more information."
          }
       ]
    },
    { 
       "SettingName":"RAM",
       "Operator":"GreaterEquals",
       "DataType":"int64",
       "Operand":8,
       "MoreInfoUrl":"https://andrewstaylor.com",
       "RemediationStrings":[ 
          { 
             "Language": "en_US",
             "Title": "Insufficient RAM.",
             "Description": "Please arrange for your machine to be replaced, or RAM upgraded to 8Gb Minimum"
          }
       ]
    },
    { 
       "SettingName":"Steam",
       "Operator":"IsEquals",
       "DataType":"String",
       "Operand":"Not Detected",
       "MoreInfoUrl":"https://andrewstaylor.com",
       "RemediationStrings":[ 
          { 
             "Language": "en_US",
             "Title": "Unsupported Application Detected",
             "Description": "Steam has been detected on your device, please uninstall and try again."
          }
       ]
    }
 ]
}

Creating the Policy in Intune

Now we have our scripts, we need to create the policy, but first, we have to add the scripts!

Navigate to Endpoint Security – Device Compliance – Scripts

Add the script like you would any PowerShell script:

Paste in the script, in this case it needs to run in the System context and 64-bit:

Navigate to Devices – Compliance Policies – New Policy

Select Windows 10 and give it a name

Select Custom Compliance

Select the Script we just uploaded

Then upload your JSON and it will detect the settings:

Click Next and run through the usual processes you would follow on a compliance policy.

If you want to use my examples, the code is on Github

14 thoughts on “Understanding custom Intune compliance policies”

  1. Hey Andrew,

    Wonder if you can help me with this. I am receiving the following error: 65009(Invalid json for the discovered setting)

    JSON:
    {
    “Rules”:[
    {
    “SettingName”:”Huntress”,
    “Operator”:”IsEquals”,
    “DataType”:”String”,
    “Operand”:”Not Detected”,
    “MoreInfoUrl”:”https://twistedfish.com”,
    “RemediationStrings”:[
    {
    “Language”: “en_US”,
    “Title”: “Huntress Agent Not Detected”,
    “Description”: “Huntress has not been detected on your device, please contact IT support.”
    }
    ]
    }
    ]
    }

    Discovery script:
    #Look for Huntress
    $InstalledSoftware = Get-ChildItem “HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall”
    if ($InstalledSoftware -like “*Huntress*”) {
    $Huntress = “Detected”
    }
    else {
    $Huntress = “Not Detected”
    }

    $hash = @{Huntress = $Huntress}
    return $hash | ConvertTo-Json -Compress

    In company portal it does show as Huntress Agent not detected.

    When I run the discovery script manually, it does return the correct value.

    Reply
        • If anyone else comes across this, I fixed it by adding the not detected variable first:

          Discovery script:
          #Look for Huntress
          $huntress = “Not Detected”
          $InstalledSoftware = Get-ChildItem “HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall”
          if ($InstalledSoftware -like “*Huntress*”) {
          $Huntress = “Detected”
          }
          else {
          $Huntress = “Not Detected”
          }

          $hash = @{Huntress = $Huntress}
          return $hash | ConvertTo-Json -Compress

          Reply
  2. Hi,

    I’ve followed this exactly but am getting the errors:

    Manufacturer
    Error
    65007(Script returned failure)
    RAM
    Error
    65007(Script returned failure)
    Steam
    Error
    65007(Script returned failure)

    I cannot figure out why this is happening?

    Reply
  3. You can use {ActualValue} to pull down resulting value. I’m using it with Linux custom compliance, but it should work for Windows. e.g.:

    {
    “Language”: “en_US”,
    “Title”: “Manufacturer: {ActualValue}”,
    “Description”: “We only support Dell devices, but you are using {ActualValue}.”
    }

    Reply
  4. Hi Andrew, thanks for this. This is great
    How can I add a variable from PowerShell to the description in json? something like below. Is the syntax correct?

    “Description”: “We only support Dell devices, but you are using $manufacturer.”

    Reply
    • Hi Anup,
      The JSON is static data only, you could pass the variable to it, but as the JSON just does a pass/fail, it wouldn’t be able to display it
      Worth feeding back to Microsoft though as a feature request

      Reply

Leave a Comment