Verifying installed applications as part of the compliance of Windows devices

This week is focused on the installed applications on Windows devices. More specifically, this week is focused on making sure that Windows devices are compliant with a list of unapproved apps. There are many methods for making sure that users won’t or can’t install specific apps on their Windows device. That could be by simply making sure that users don’t have the permissions to install apps and lock down their Windows devices, but that could also be by verifying the installed apps on their Windows devices. This post will focus on the latter, by comparing the installed apps with a list of unapproved apps. That can be achieved by using custom compliance settings. A few months ago I wrote about working with custom compliance settings. That enables the ability to add custom scripting to device compliance policies. Custom scripting basically means that anything is possible, including a verification of the installed apps. That also means that the verification is performed locally on the Windows devices. This post will show how to leverage that custom compliance settings functionality with a small script to compare the installed apps with a predefined list of unapproved apps.

Important: At the moment of writing custom compliance is in public preview. The Microsoft Endpoint Manager admin center portal states that, once it becomes general available, it will be additional cost to the licensing options that include Microsoft Endpoint Manager or Microsoft Intune. However, the Microsoft Endpoint Manager Blog is recently (April 6, 2022) updated with a line that states that plans are changed and that it will be available in the Microsoft 365 and EMS E3/E5 license plans for Microsoft Intune.

Creating a PowerShell script to verify the installed applications

When looking at the configuration, the first action is to create a PowerShell script that will be used to get the installed apps and to compare those apps with a predefined list of unapproved apps. That enables the IT administrator to define a list (array) of unapproved apps. The idea is to get the displayname of the installed apps from the most common locations in the registry and to add those displaynames to a list (array). That list will be compared with the list of unapproved apps. Once a match is found, the script will return “Unapproved app installed” and when no match is found, the script will return “No unapproved apps installed“. That information is returned in a compressed single line JSON-format.

$arrUnapprovedApplications = @("Google Chrome")
$booUnapprovedApplications = $false

function Get-InstalledApps {
    if (![Environment]::Is64BitProcess) {
        $arrRegistryPaths = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
                              'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*')
    }
    else {
        $arrRegistryPaths = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
                              'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
                              'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
                              'HKCU:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*')
    }

    $arrUninstallRegistrations = @()
    foreach ($registryPath in $arrRegistryPaths) {
        if (Test-Path $registryPath) {
            $arrUninstallRegistrations += Get-ItemProperty $registryPath
        }
    }

    $arrInstalledApps = @()
    foreach ($uninstallRegistration in $arrUninstallRegistrations) {
        if ($uninstallRegistration.DisplayName -ne $null) {
            $arrInstalledApps += $uninstallRegistration.DisplayName
        }
    }
    return $arrInstalledApps
}

$arrInstalledApplications = Get-InstalledApps
 foreach ($installedApplication in $arrInstalledApplications) {
    foreach ($unApprovedApplication in $arrUnapprovedApplications) {
        if ($installedApplication -eq $unApprovedApplication) {
            $booUnapprovedApplications = $true
        }
    }
}

if ($booUnapprovedApplications) {
    $appStatus = @{"Installation status" = "Unapproved app installed"}
} 
else {
    $appStatus = @{"Installation status" = "No unapproved apps installed"}
}

return $appStatus | ConvertTo-Json -Compress 

Note: This example uses Google Chrome as an unapproved app, only because it’s currently installed on the test Windows device. That can be basically any app, or any list of apps.

Once the PowerShell script is finished, it can be added to Microsoft Intune. That action should be performed before the installation status can be used in the device compliance policy. The following five steps walk through that process.

  1. Open the Microsoft Endpoint Manager admin center portal and navigate to Endpoint security > Device compliance > Scripts
  2. On the Compliance policies | Scripts page,  click Add > Windows 10 and later
  3. On the Basics page, specify a Name and optionaly a Description and Publisher and click Next
  4. On the Settings page (as shown in Figure 1), specify the following information and click Next
  • Detection script: Copy the just written PowerShell script
  • Run this script using the logged on credentials: Select No to run the PowerShell script in SYSTEM context
  • Enforce script signature check: Select No to not perform a signature check on the PowerShell script
  • Run script in 64 bit PowerShell Host: Select Yes to run the PowerShell script in 64-bit
  1. On the Review + create page, verify the configuration of the PowerShell script and click Create

Note: Once the PowerShell script is added, it can be editted via Microsoft Endpoint Manager admin center portal.

Constructing a JSON-file to verify the status of installed applications

When the PowerShell script is finished and added to Microsoft Intune, the second action is to construct a JSON-file. That JSON-file can be used to define the app installation status information that the device compliance policy should verify. That includes information about the name of the setting and the value that it should comply with. Based on the idea of this post, which is to make sure that the Windows devices comply with the list of unapproved apps, that value should be “No unapproved apps installed“. Besides that, it also contains the properties to configure a message to the users. That message will be used once the Windows device is not compliant with the unapproved apps.

{
"Rules":[ 
    { 
       "SettingName":"Installation status",
       "Operator":"IsEquals",
       "DataType":"String",
       "Operand":" No unapproved apps installed",
       "MoreInfoUrl":"https://petervanderwoude.nl",
       "RemediationStrings":[ 
          { 
             "Language":"en_US",
             "Title":"Device is running apps that are not approved by the organisation.",
             "Description": "Please make sure that none of the not approved apps are installed on the device. Those apps are documented in the approved application policy on SharePoint."
          }
       ]
    }
 ]
}

Creating a device compliance policy to verify the status of installed applications

When the JSON-file is constructed, the third and last action is to create and configure a device compliance policy. That policy will be used to verify if the Windows devices are not running unapproved apps and if those devices comply with the company policies. That compliance information can be used for reporting purposes, but also for usage with Conditional Access to determine access to data and resources. The following nine steps walk through the process of creating a device compliance policy that includes (and focusses on) the custom compliance setting for the installed applications.

  1. Open the Microsoft Endpoint Manager admin center portal navigate to Endpoint security Device compliance
  2. On the Compliance policies | Policies blade, click Create Policy to open the Create a policy page
  3. On the Create a policy page, select Windows 10 and later with Platform and click Create
  4. On the Basics page, provide a valid name for the device compliance policy and click Next
  5. On the Compliance settings page, navigate to the Custom Compliance section (as shown in Figure 2), provide the following information and click Next
  • Custom compliance: Select Require to enable the custom compliance setting
  • Select your discovery script: Select the just uploaded PowerShell script
  • Upload and validate the JSON file with your custom compliance settings: Select the just constructed JSON file
  1. On the Actions for noncompliance page, leave the default configuration of Action on Mark device noncompliant with Schedule (days after noncompliance) on Immediately and click Next
  2. On the Scope tags page, configure the applicable scope tags and click Next
  3. On the Assignments page, configure the assignment by selecting the applicable group and click Next
  4. On the Review + create page, review the configuration and click Create

Note: Keep in mind that a device compliance policy only supports a single custom compliance setting. That means a single PowerShell script. For multiple settings, use single PowerShell script to detect multiple different settings.

Experiencing the results of the installed application verification

Now let’s end this post by having a look at the results of the custom compliance setting for the installed apps. From a user perspective the best information is shown in the Company Portal app, when a device is not compliant. That information is shown in the Devices section. Below on the left (in Figure 3) is an exerpt of the IntuneManagementExtension.log that shows the output of the created PowerShell script. That results in a noncompliant device, as shown below on the right (in Figure 3). That clearly shows the information that was provided in the constructed JSON-file.

More information

For more information about custom settings for compliance, refer to the following docs.

24 thoughts on “Verifying installed applications as part of the compliance of Windows devices”

  1. This looks very useful. I’ve just tried to run the powershell script manually to see what it reports but I get the following error.

    PS C:\intune> else {
    >> $appStatus = @{“Installation status” = “No unapproved apps installed”}
    >> }
    else : The term ‘else’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check
    the spelling of the name, or if a path was included, verify that the path is correct and try again.
    At line:1 char:1
    + else {
    + ~~~~
    + CategoryInfo : ObjectNotFound: (else:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    Is this my copying and pasting?

    Thanks
    Jacqui

    Reply
  2. I would like to modify this script to check for multiple required applications. I need the device to be marked as noncompliant if any one of the applications are not installed. Can you help me with that syntax?

    Reply
    • Andy,

      Did you ever get that figured out? Im trying to wrap my head around this as I want to do that same and make sure a few security products are installed otherwise mark it non-compliant. But my coding ability is pretty basic.

      Reply
        • What is the similar script/logic for this — to be able to look at multiple applications and if one of them are missing, return non-compliance? I’m having a little trouble trying to work that logic because currently when I have multiple $arrUnapprovedApplications, if one of them IS installed, and the others aren’t, the $booUnapprovedApplications is still returning $true.

          Reply
  3. Hi, thank you for the script. I have a question. How can I modify the code just to list all the applications founded as uncompliance at the end of the output. In order to have more details about what applications are present.

    Thank you

    Reply
      • Hi Peter,
        i would try to give to the script a list of commons apps that users could install on the pc (uTorrent, and others) and if the script will find them, it marks the PCs as uncompliance.
        Is it possibile?
        I’m not so skilled with powershell to reach the goal and i can’t find any thread on the web with the same question.

        Thank you,
        Federico

        Reply
  4. Can you please help me with the PowerShell to verify if the device has an approved application installed? If the app is installed..only then mark the device as compliant..else not

    Reply
  5. Hi Peter,
    great article! I only have an issue that the device never come back to the compliant state after I deinstall the unapproved application which I try as in the example with Google Chrome. If I run the PS script locally on the device he states “no unapproved apps installed” but in Intune/Company Portal the state still stays in non-compliant, also after more than 24 hours. Any ideas?
    Thanks,
    Michael

    Reply
      • Hi Peter,
        sorry for my late response but I didn’t check it each day and was last week busy. I see the following information in the AgentExecutor.log for the script with the corresponding id and the result “No unapproved apps installed” but the status in MS Intune for the device is still non-compliant.

        <![LOG[write output done. output = {"Installation status":"No unapproved apps installed"}

        Reply
  6. Hi,

    I use this script for our Cortex XDR app. The thing is the cortex is installed with its version like below. I just want it to check Cortex, not its version. What do I need to do for this?

    $arrUnapprovedApplications = @(“Cortex XDR 8.0.1.33809”)

    Reply

Leave a Reply to Jacqui Hurst Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.