There are still many applications that cannot be installed via MSI or a simple installation routine. This is where the win32 app comes into play in Intune (Microsoft Endpoint Manager). In Intune, this allows us to deal with routines and processes in a script and then check the installation with another script (custom detection script) or predefined detection rules (MSI, EXE, file or registry key).
In the past few years, I have accumulated a few different variations of detection scripts, which I am trying to collect here.
Use the button above to find my current collection of custom detection scripts for Intune win32 apps. It is important that you only use one of the blocks for your detection rule. If you want to know how a win32 app is structured in general and how I handle it, you can find it here (my take on win32 apps - Intune) one of my past blog posts.
You can currently find the following detection rules in my GitHub repository:
- Detection of an EXE or a file
- Validation file with specific content (e.g. versioning)
- Existing EXE with exact target version
- or higher version
- and additional registry key
- Registry key comparison
- Detection of a scheduled task
- Detection of a scheduled task including version
Custom Detection Scripts in Intune: Best Practices
Before diving into custom detection scripts, it's essential to understand one key principle: always use Intune's built-in detection options whenever they meet your needs.
Built-in options, like checking for registry keys or file existence, are faster, more reliable, and better integrated with the Intune service. They reduce complexity and potential points of failure.
Use a custom script only when:
- OR logic is required (e.g., detecting one of multiple possible registry keys).
- You need to combine registry checks with additional logic or conditions.
- The built-in options cannot cover your specific scenario.
For cases involving single registry keys or AND conditions across multiple keys, always prefer Intune's built-in detection rules. Here's why:
- Single Keys: Intune's built-in registry check is straightforward and reliable.
- AND Conditions: Intune's built-in detection supports multiple checks that must all be true, making it perfect for this scenario.
Now that we've established when custom scripts are necessary, let's explore their implementation.
Explanation / example EXE with exact version
The recognition rules are all structured in such a way that the criteria are defined first:
$ProgramPath = "C:\Program Files\XXXXX\XXXXXX.exe"
$ProgramVersion_target = '1.0.2'
$ProgramVersion_current = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($ProgramPath).FileVersion
The definitions are then compared or checked and if they apply, "Found it!" issued. This signals Intune that the package is installed successfully. Alternatively, you can end the script with 'exit 0'.
if($ProgramVersion_current -eq $ProgramVersion_target){
Write-Host "Found it!"
}
Using Registry Keys in Custom Detection Scripts
Single Registry Key Detection
For detecting a single registry key, use Intune's built-in functionality whenever possible. However, if you need a custom script (e.g., to combine this with another condition), here's an example:
$RegPath = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{SCLOUD00-3E57-3F0B-9332-48A0A5671812}'
$Version = '2022,01,19'
$RegContent = Get-ItemProperty -Path $RegPath
if($RegContent.Version -eq $Version){
Write-Host "Found it!"
}
Multiple Registry Keys with OR Logic
When you need to check if any one of several registry keys exists and matches the criteria, a custom script is necessary. Here's how to implement this:
# Array of registry paths and expected versions
$RegistryChecks = @(
@{ Path = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{SCLOUD00-3E57-3F0B-9332-48A0A5671812}'; Version = '2022,01,19' },
@{ Path = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{SCLOUD00-3E57-3F0B-9332-48A0A5671813}'; Version = '2023,05,01' }
)
# Variable to track if any key matches
$MatchFound = $false
foreach ($Check in $RegistryChecks) {
try {
$RegContent = Get-ItemProperty -Path $Check.Path -ErrorAction Stop
if ($RegContent.Version -eq $Check.Version) {
Write-Host "Found it at $($Check.Path) with version $($Check.Version)!"
$MatchFound = $true
break
}
} catch {
Write-Host "Registry key not found or inaccessible: $($Check.Path)"
}
}
if ($MatchFound) {
Write-Host "Found it!"
exit 0
} else {
Write-Host "No matching registry key found."
exit 1
}
This script ensures the detection succeeds if at least one registry key matches, which cannot be handled by Intune's built-in rules.
Multiple Registry Keys with AND Logic
For scenarios where all keys must match, Intune's built-in detection options are the better choice. You can define multiple registry key checks within the Intune console, each of which must pass for the detection to succeed.
However, if you combine these with other logic or non-registry-based conditions, you might need a script like this:
# Array of registry paths and expected versions
$RegistryChecks = @(
@{ Path = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{SCLOUD00-3E57-3F0B-9332-48A0A5671812}'; Version = '2022,01,19' },
@{ Path = 'HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{SCLOUD00-3E57-3F0B-9332-48A0A5671813}'; Version = '2023,05,01' }
)
# Variable to track overall match
$AllMatched = $true
foreach ($Check in $RegistryChecks) {
try {
$RegContent = Get-ItemProperty -Path $Check.Path -ErrorAction Stop
if ($RegContent.Version -ne $Check.Version) {
Write-Host "Version mismatch at $($Check.Path)."
$AllMatched = $false
break
}
} catch {
Write-Host "Registry key not found: $($Check.Path)"
$AllMatched = $false
break
}
}
if ($AllMatched) {
Write-Host "All registry keys match."
exit 0
} else {
Write-Host "Not all registry keys match."
exit 1
}
This script works when additional custom logic is required beyond Intune’s capabilities, but generally, you should avoid using a script for this scenario.
Do you have any other custom detection scripts in Intune that help you in your everyday life or which you are up against?
Let me know in the comments. I would like to add them to my collection.
Hello,
For the version checking, If EXE file is not installed or not a specific binaries, that does not work.
In this cas downloed by invoke-request.
I use this way :
$file="C:\TeamViewerQS.exe"
$MAJOR=$(Get-ChildItem $file | %{$_.VersionInfo} | Select *).FileVersionRaw.major
$MINOR= $(Get-ChildItem $file | %{$_.VersionInfo} | Select *).FileVersionRaw.Minor
# Major Minor Build Revision
# ----- ----- ----- --------
# 15 29 4 0
Good morning.
Do you know if the detection works after the app is installed?
I have created a detection rule based on the availability of packages in winget repository. It checks the version in the web repository and compares it with the version installed on a computer, but I am not sure if the app would check again the detection (for future versions) after the app has been installed successfully.
Regars
In this case the detection would work only once. If you want to update the winget apps this way, I recommend doing it with a proactive remediation script.
Or another way is to just check if the app is installed (regardless of the version) and to use a scheduled task to run the winget update command on a regular base.
Working on a way to create a proactive remediation to perform weekly restarts for Intune window 10 devices. We are wanting to restart each device every 5 days. Want to give the customer a toast notification to delay it for 2, 4, or 8 hours up to 2 times and on the 3rd they have to pick a restart time.
Thats a very good Idea!
I guess you already found this post to start: https://msendpointmgr.com/2020/06/25/endpoint-analytics-proactive-remediations/
Is the detection basically run as a system? Is this also the case when the WIN32 app is executed as a user?
thx
Christian
The detection always runs in the same context as the app installation itself.
This is my favourite and works for many apps.
$AppDisplayName = "My Application"
[System.Version]$ReferenceVersion = "7.01.17311.0"
$RegUninstallKeys = @(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$AppRegUninstallKey = Get-Item -Path $RegUninstallKeys | Where {$_.GetValue("DisplayName") -eq $AppDisplayName}
If ($AppRegUninstallKey)
{
If ($AppRegUninstallKey.Count -gt 1)
{
$AppRegUninstallKey = $AppRegUninstallKey | Sort DisplayVersion -Descending | Select -first 1
}
[System.Version]$InstalledVersion = $AppRegUninstallKey.GetValue("DisplayVersion")
If ($InstalledVersion -ge $ReferenceVersion)
{
Write-Output "Installed"
}
}