Demystifying Intune Custom App Detection Scripts

I’m sure everyone reading this has deployed many applications within Intune using the win32 format and most likely used an MSI code or a file/registry detection method to monitor for a completed install (and why wouldn’t you, they work perfectly)

Sometimes however, you might come across an application which is more tricky to detect, or you want to look for two things before you’re confident it’s installed ok. This is where a custom detection script comes into play.

As with my previous post on Proactive Remediations, Intune is looking for specific outputs for the script to return a success code.

In this case, it is looking for BOTH an exit with a 0 code and output on STDOUT. For those of you rapidly opening a new tab to google STDOUT, basically, it needs output of some sort, a simple:

write-output "hello world"
exit 0

Will work fine, so that is what we need the script to return at the end in order for it to be marked as successful.

Passing an exit code 1 or even an exit 0 without any output will flag as an install failure.

Now for some examples:

A basic file check (in case you’re feeling scripty)

$File = "C:\windows\system32\notepad.exe"
if (Test-Path $File) {
    write-output "Notepad detected, exiting"
    exit 0
}
else {
    exit 1
}

A registry key (checking both it exists and the value is correct, good for versioning)

$Path = "HKLM:\SOFTWARE\7-Zip"
$Name = "Path"
$Type = "STRING"
$Value = "C:\Program Files\7-Zip\"

Try {
    $Registry = Get-ItemProperty -Path $Path -Name $Name -ErrorAction Stop | Select-Object -ExpandProperty $Name
    If ($Registry -eq $Value){
        Write-Output "Detected"
       Exit 0
    } 
    Exit 1
} 
Catch {
    Exit 1
}

Now for the ones it can’t do out of the box!

Check if a service exists

$service = get-service -name "MozillaMaintenance"
if ($service) {
    write-output "MozillaMaintenance detected, exiting"
    exit 0
}
else {
    exit 1
}

Service exists and is running:

$service = get-service -name "MozillaMaintenance"
if ($service.Status -eq "Running") {
    write-output "MozillaMaintenance detected and running, exiting"
    exit 0
}
else {
   exit 1
}

Both file and service exist

$File = "C:\windows\system32\notepad.exe"
$service = get-service -name "MozillaMaintenance"
if (($service) -and (Test-Path $File)) {
    write-output "MozillaMaintenance and Notepad detected, exiting"
    exit 0
}
else {
    exit 1
}

File is of a certain version

$fileversion = (Get-Command C:\Windows\System32\notepad.exe).Version
$build = 19041
$detectedbuild = $fileversion.Build
if ($detectedbuild -eq $build) {
    write-output "Detected"
    exit 0
}
else {
    exit 1
}

Or even the file was last modified today

$filedate = (Get-Item C:\Windows\System32\notepad.exe).LastWriteTime
if ($filedate -gt (Get-Date).AddDays(-1)) {
    write-output "Detected"
    exit 0
}
else {
    exit 1
}

I hope that makes them easier to understand! Happy scripting…

19 thoughts on “Demystifying Intune Custom App Detection Scripts”

  1. Just a note. This code:
    $File = “C:\windows\system32\notepad.exe”
    if ($file) {
    write-output “Notepad detected, exiting”
    exit 0
    }
    else {
    exit 1
    }

    Does not check for the existance of a file. The $File string will always be true.

    You need:
    if (Test-Path $File) {…

    Reply
  2. We are trying to have a appx dependency and msix installing via powershell in a win32 app. How would I detect these are installed and correct version? Dont really see and folders. They are modern apps.

    Reply
  3. For the Version Check, would it be more beneficial to check the registered entry in the registry?

    Im thinking something like this:

    $APPINSTALL = Get-ItemProperty “HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*” | where {$_.DisplayName -like “*APPNAME*”}

    IF(Test-Path “$APPINSTALL .PSPath”)
    {
    IF($APPINSTALL .DisplayVersion -ge “3.0.279”)
    {
    write-host “Install Success”
    exit 0
    }

    ELSE
    {
    Write-Host “Installed Version Not Match, Upgrade Failed”
    exit 1
    }

    }

    ELSEIF(!(Test-Path “$APPINSTALL .PSPath”))
    {
    Write-Host “Install failed!”
    exit 1
    }

    Reply
  4. Thank you Andrew for this invaluable information.
    How about in a scenario where we are trying to update a Dell WD19 docking station firmware through Endpoint manager. In this scenario, the dock needs to be directly connected to the device and detected before it installs the firmware package.

    How can I create a simple custom detection rule in powershell, that would show either “yes” the docking station is attached proceed with the installation or “no” its not attached-and install when detected.

    Reply
    • Hi,
      If you can get the hardware ID for the dock, something like this should work:
      $dock = get-pnpdevice | where-object InstanceId -eq “USB\VID_14B0&PID_0184\5&21F3FF01&0&5”
      if ($status -eq “OK”) {
      ##Dock attached
      }
      else {
      ##Not attached
      }

      Replace the hardware ID with whatever you find for the dock, that’s just a random one I grabbed from device manager

      Reply
  5. What context do the scripts run in ? I have a couple of installers that will run in user space. Does the custom detection script follow the install context (System / User)

    Reply
  6. What would the JSON be for the following script you posted for a Custom Compliance Script?

    $File = “C:\windows\system32\notepad.exe”
    $service = get-service -name “MozillaMaintenance”
    if (($service) -and (Test-Path $File)) {
    write-output “MozillaMaintenance and Notepad detected, exiting”
    exit 0
    }
    else {
    exit 1
    }

    Reply
    • I’d split it into two so you can give the users a better reason for it, otherwise they’ll have to guess which it is:

      Powershell:
      $File = “C:\windows\system32\notepad.exe”
      $service = get-service -name “MozillaMaintenance”
      if ($service) {
      $mozillamaintenance = “True”
      }
      else {
      $mozillamaintenance = “False”
      }

      if ($File) {
      $fileexists = “True”
      }
      else {
      $fileexists = “False”
      }

      $hash = @{
      Mozilla = $mozillamaintenance
      Notepad = $fileexists
      }
      return $hash | ConvertTo-Json -Compress

      JSON:
      {
      “Rules”:[
      {
      “SettingName”:”Notepad”,
      “Operator”:”IsEquals”,
      “DataType”:”String”,
      “Operand”:”True”,
      “MoreInfoUrl”:”https://google.com”,
      “RemediationStrings”:[
      {
      “Language”: “en_US”,
      “Title”: “Notepad Missing”,
      “Description”: “Notepad is Missing”
      }
      ]
      },
      {
      “SettingName”:”Mozilla”,
      “Operator”:”IsEquals”,
      “DataType”:”String”,
      “Operand”:”True”,
      “MoreInfoUrl”:”https://google.com”,
      “RemediationStrings”:[
      {
      “Language”: “en_US”,
      “Title”: “Mozilla”,
      “Description”: “Mozilla Maintenance Service is Missing”
      }
      ]
      }
      ]
      }

      Reply

Leave a Comment