Using Custom Compliance to block outdated Windows OS automatically

In Intune, you can use enrollment restrictions or a standard compliance policy to block machines based on version numbers, but this means every time a new version is released, or a version goes EOL, you need to manually change the settings.

This is fine for a small environment, but what if you are supporting multiple customers?

With that in mind, I have a custom compliance policy to automate this for you. If you aren’t familiar with custom compliance policies, I have covered them before here

First, we need the Compliance Script and I’m going to use the official Microsoft release information:

https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information

https://learn.microsoft.com/en-us/windows/release-health/release-information

Sadly these don’t have an API, so I’m going to use RegEx instead

PowerShell Script

As you can see, the URL is different for Windows 10 and Windows 11 so we’ll grab that from a WMI query

$OSname = Get-WMIObject win32_operatingsystem | select Caption
if ($OSname -like "*Windows 10*") {
    $OSname = "Windows 10"
}
if ($OSname -like "*Windows 11*") {
    $OSname = "Windows 11"
}

Obviously we’ll need the version number as well

$OSVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name DisplayVersion).DisplayVersion

So now we have the OS version running on the machine, we need to find out what is the latest. You can see the full script below, as it’s the same for each OS, I’ll use Windows 11 here

Fortunately, the OS versions both have a class of “highlight” on the <tr> in the html code so we can look for that:

$url = "https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information"
$content = (Invoke-WebRequest -Uri $url -UseBasicParsing).content
[regex]$regex = "(?s)<tr class=.*?</tr>"
$tables = $regex.matches($content).groups.value

That’s going to give us the whole html <td> row so now we need to trim it

$tables = $tables.replace("<td>","")
$tables = $tables.replace("</td>","")
$tables = $tables.replace('<td align="left">',"")
$tables = $tables.replace('<tr class="highlight">',"")
$tables = $tables.replace("</tr>","")

Now we want to add them all to an array

$availableversions = @()
foreach ($table in $tables) {
    [array]$toArray = $table.Split("`n") | Where-Object {$_.Trim("")}
    $availableversions += ($toArray[0]).Trim()
}

Finally, I pick the top two for n-1 supported

$supportedversions = $availableversions | select-object -first 2

Now we simply need to check if our version is in the list

if ($OSVersion -in $supportedversions) {
    $OSsupported = "True"
}
else {
    $OSsupported = "False"
}

The full code to add to your compliance script is:

##Which OS
##Check if we are running Win10 or 11
$OSname = Get-WMIObject win32_operatingsystem | select Caption
if ($OSname -like "*Windows 10*") {
    $OSname = "Windows 10"
}
if ($OSname -like "*Windows 11*") {
    $OSname = "Windows 11"
}

##Which OS Version?
##Check which version number
$OSVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name DisplayVersion).DisplayVersion

if ($OSname -eq "Windows 11") {
##Windows 11
##Scrape the release information to find latest supported versions
$url = "https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information"
$content = (Invoke-WebRequest -Uri $url -UseBasicParsing).content
[regex]$regex = "(?s)<tr class=.*?</tr>"
$tables = $regex.matches($content).groups.value
$tables = $tables.replace("<td>","")
$tables = $tables.replace("</td>","")
$tables = $tables.replace('<td align="left">',"")
$tables = $tables.replace('<tr class="highlight">',"")
$tables = $tables.replace("</tr>","")

##Add each found version for array
$availableversions = @()
foreach ($table in $tables) {
    [array]$toArray = $table.Split("`n") | Where-Object {$_.Trim("")}
    $availableversions += ($toArray[0]).Trim()
}

##We want n-1 so grab the first two objects
$supportedversions = $availableversions | select-object -first 2

##Check if we are supported
if ($OSVersion -in $supportedversions) {
    $OSsupported = "True"
}
else {
    $OSsupported = "False"
}
}


if ($OSname -eq "Windows 10") {
    ##Windows 10
    ##Scrape the release information to find latest supported versions
    $url = "https://learn.microsoft.com/en-us/windows/release-health/release-information"
    $content = (Invoke-WebRequest -Uri $url -UseBasicParsing).content
    [regex]$regex = "(?s)<tr class=.*?</tr>"
    $tables = $regex.matches($content).groups.value
    $tables = $tables.replace("<td>","")
    $tables = $tables.replace("</td>","")
    $tables = $tables.replace('<td align="left">',"")
    $tables = $tables.replace('<tr class="highlight">',"")
    $tables = $tables.replace("</tr>","")
    
    ##Add each found version for array
    $availableversions = @()
    foreach ($table in $tables) {
        [array]$toArray = $table.Split("`n") | Where-Object {$_.Trim("")}
        $availableversions += ($toArray[0]).Trim()
    }

    ##We want n-1 so grab the first two objects
    $supportedversions = $availableversions | select-object -first 2
    
    ##Check if we are supported
    if ($OSVersion -in $supportedversions) {
        $OSsupported = "True"
    }
    else {
        $OSsupported = "False"
    }
    }
$hash = @{ 
    OSsupported = $OSsupported
}
return $hash | ConvertTo-Json -Compress

JSON Policy

Once we have the script, we now need a JSON file to verify compliance.

Here we are simply looking for a result of a string which says “True” and to make things a bit better for the user, I’ve included a link to Windows Update Troubleshooter for a bit of user self-service

{
    "Rules":[ 
        { 
           "SettingName":"OSsupported",
           "Operator":"IsEquals",
           "DataType":"String",
           "Operand":"True",
           "MoreInfoUrl":"https://support.microsoft.com/en-us/windows/windows-update-troubleshooter-19bc41ca-ad72-ae67-af3c-89ce169755dd",
           "RemediationStrings":[ 
              { 
                 "Language": "en_US",
                 "Title": "Unsupported Operating System.",
                 "Description": "Your operating system version is now out of support and requires updating"
              }
           ]
        }
     ]
    }

If you are using multiple settings, simply add more rules before the final “]”, comma-separated.

That’s it, you can now sit back and relax!

Leave a Comment