Category Archives: Intune

Understanding the ‘Correct’ File Version used for Intune Win32 App Detection Method

Depending on the developer for an application, various & differing version numbers can be added into the metadata of an application/executable/binary which can sometimes be confusing when creating manual detecton rules as part of an Intune Win32 App.

A good example of this is ‘CMTrace.exe’ from Microsoft. The screenshot below shows the ‘Details’ tab of ‘CMTrace.exe’ file properties with ‘File version’ highlighted:

You can see from the screenshot that the ‘File Version’ displayed is ‘5.0.9068.1000′ which is what I initially used for my Detection Rule as part of a CMTrace Win32 App in Intune:

Unfortunately this resulted in a continuous loop of ‘CMTrace.exe’ being reinstalled as it wasn’t being detected correctly by Intune – this was visible in the log file below:

C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\IntuneManagementExtension.log. 

Turns out that Intune actually uses a different property of the file metadata to determine the version number of a file. This is documented in the Microsoft article below:

https://learn.microsoft.com/en-us/mem/intune/apps/apps-win32-troubleshoot#detecting-the-win32-app-file-version-by-using-powershell

Two quick & easy ways to retrieve the correct ‘Version’ value to use in a Detection Rule are shown in the PowerShell commands below. Both commands are querying the same file in the same location on a reference computer i.e., ‘CMTrace.exe’ in the ‘C:\Windows’ directory:

[System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Windows\cmtrace.exe").FileVersion

(Get-Item -Path "C:\Windows\CMTrace.exe").VersionInfo.FileVersion

The value returned from these queries was ‘5.00.9068.1000′.

The additional zero in the version number was the difference that broke the configured Detection Rule.

Using the correct value returned by either of these two PowerShell commands (they are both returning the same information from .NET) will match the value retrieved by Intune when checking if the Win32 App is installed or not.

This will result in the Win32 App being installing once and then correctly being detected as installed thereafter i.e., the Detection Rule will now successfully work as expected.

/ JC

Intune | Force Microsoft Edge Update to Latest Version During Windows Autopilot

Quick and simple post today. Had a customer deploying Windows 10 IoT Enterprise LTSC 2021 (yes I am aware that IoT Enterprise and LTSC are not officially supported for Windows Autopilot at time of writing but it works fine so… ) and there was a requirement to update the version of Edge included in this version of Windows so that it was at a version which supported some of the more recent Intune Configuration Profile policy settings. Should this not happen then the policies would not apply until such time as Edge updated itself which may be some time after a user had logged into the device.

To accomplish this I wrapped the following PowerShell script into a Win32 app and had it configured as a Blocking application on the Enrolment Status Page (ESP) being used for Autopilot.

The result is Microsoft Edge updating to the latest available version before any users log in for the first time.

<#
.DESCRIPTION
    PowerShell script to force update of Microsoft Edge Stable
.EXAMPLE
    PowerShell.exe -ExecutionPolicy ByPass -File <ScriptName>.ps1
.NOTES
    VERSION     AUTHOR              CHANGE
    1.0         Jonathan Conway     Initial script creation
#>

$Exe = Get-ChildItem -Path "${env:ProgramFiles(x86)}\Microsoft\EdgeUpdate\MicrosoftEdgeUpdate.exe"
$Arguments = "/silent /install appguid={56EB18F8-B008-4CBD-B6D2-8C97FE7E9062}&appname=Microsoft%20Edge&needsadmin=True"
return (Start-Process $($Exe.FullName) -ArgumentList $Arguments -NoNewWindow -PassThru -Wait).ExitCode

Be sure to configure the detection method according to your environment. For me, I set this to a version of “Greater than or equal to: 100.0.0000.00” to detect the installation but you may want to use a higher version number depending on your own circumstances:

Let me know if this works for you (or if you have any issues with the script) in the comments.

/ JC

Intune Proactive Remediation: Detect & Remove User-installed Instances of Zoom

Had a requirement to detect and remove any user installations of Zoom (i.e. installed using standard user permissions and located in the user profile) via Intune. The supported route for uninstalling Zoom is use a Zoom-provided tool called ‘CleanZoom.exe’ so the script checks for that tool being present and if not, downloads and extracts it directly from Zoom before running the tool to remove any user installations of Zoom. Also needed a log file to show when this has been done from the client (this can obviously be removed if not needed).

Proactive Remediations to the rescue again!

Detection:

<#
.DESCRIPTION
	Proactive Remediation | Detection
.EXAMPLE
	PowerShell.exe -ExecutionPolicy ByPass -File <ScriptName>.ps1
.NOTES
	VERSION     AUTHOR              CHANGE
    1.0         Jonathan Conway     Initial script creation
#>

# Discovery
try {
    # Run Test and store as variable
    $Test = Get-ChildItem -Path "C:\Users\" -Filter "Zoom.exe" -Recurse -Force -ErrorAction SilentlyContinue

    # Check where test is compliant or not - if no instances of Zoom are discovered then mark as 'Compliant' and exit with 0
    if ($null -eq $Test) {
        Write-Output "Compliant"
        exit 0
    }
    # If instances of Zoom are discovered then mark as 'Non Compliant' and exit with 1
    else {
        Write-Warning "Non Compliant"
        exit 1
    }
}

catch {
    # If any errors occur then return 'Non Compliant'
    Write-Warning "Non Compliant"
    exit 1
}

Remediation:

<#
.DESCRIPTION
	Proactive Remediation | Remediation
.EXAMPLE
	PowerShell.exe -ExecutionPolicy ByPass -File <ScriptName>.ps1
.NOTES
	VERSION     AUTHOR              CHANGE
    1.0         Jonathan Conway     Initial script creation
#>

# Logging
$LogPath = "C:\Support\Zoom\"
Start-Transcript -Path $LogPath\ZoomCleanup.log -Append -NoClobber

# Variables
$CleanZoomTool = "C:\Support\Zoom\CleanZoom.exe"

# Check to see if 'C:\Support\Zoom' exists
$CheckZoomFolder = Test-Path -Path "C:\Support\Zoom\" -PathType Container

# If 'C:\Support\Zoom' folder does not exist then create it
if ($CheckZoomFolder -eq $false) {

	# Create folder
	Write-Output "'C:\Support\Zoom' folder does not exist - creating it"
	New-Item -Path "C:\Support" -Name "Zoom" -ItemType "Directory" -Force

}
else {
	Write-Output "'C:\Support\Zoom' folder exists - continuing"
}

# Check if CleanZoom.exe exists on the device
$CheckZoomClean = Test-Path -Path $CleanZoomTool -PathType "Leaf"

# If CleanZoom.exe does not exist on the device - download from Zoom website and extract locally
if ($CheckZoomClean -eq $false) {

	Write-Output "'C:\Support\Zoom\CleanZoom.exe' does not exist - downloading and extracting it"
	Invoke-WebRequest -Uri "https://assets.zoom.us/docs/msi-templates/CleanZoom.zip" -OutFile "C:\Support\Zoom\CleanZoom.zip"
	Expand-Archive -Path "C:\Support\Zoom\CleanZoom.zip" -DestinationPath "C:\Support\Zoom" -Force
	Remove-Item -Path "C:\Support\Zoom\CleanZoom.zip" -Force

}
else {
	Write-Output "'C:\Support\Zoom\CleanZoom.exe' exists - continuing"
}

try {
	# Run CleanZoom.exe to remove any installed instances of Zoom client in User Profiles
	Write-Output "Running CleanZoom.exe to remove Zoom instances from User Profile areas"
	Start-Process -FilePath $CleanZoomTool -ArgumentList "/silent"
	exit 0
}
catch {
	Write-Output "CleanZoom.exe failed to run"
	exit 1
}

Stop-Transcript

/ JC

Intune Proactive Remediation: BitLocker Key Escrow to Azure AD After MCM OSD Task Sequence

Recently had a customer requirement to encrypt Windows 10 devices using a MCM Task Sequence and then have the Recovery Keys escrowed into AAD once an Intune Drive Encryption policy was applied via Co-management workload shift (Endpoint Protection).

By default, Windows will escrow to where you tell it in the Task Sequence and not escrow into AAD. In my case the Task Sequence was storing the Recovery Key into on-prem Active Directory.

The Discovery script checks Event Viewer for an Event 845 including the text “was backed up successfully to your Azure AD” having been logged in the last 7 days (this can obviously be amended to suit individual requirements).

If non-compliant then the Remediation script forces the key to be escrowed using the ‘BackupToAAD-BitLockerKeyProtector’ PowerShell cmdlet.

Detection:

<#
.DESCRIPTION
    Script to check for BitLocker Key escrow into Azure AD
.EXAMPLE
    PowerShell.exe -ExecutionPolicy ByPass -File <ScriptName>.ps1
.NOTES
    VERSION     AUTHOR              CHANGE
    1.0         Jonathan Conway     Initial script creation
#>

# Check for Event 845 in BitLocker API Management Event Log over last 7 days - if contains text "was backed up successfully to your Azure AD" then Detection is complete
try {
    $Result = Get-WinEvent -FilterHashTable @{LogName = "Microsoft-Windows-BitLocker/BitLocker Management"; StartTime = (Get-Date).AddDays(-7) } | Where-Object { ($_.Id -eq "845" -and $_.Message -match "was backed up successfully to your Azure AD") } | Format-Table -Property "Message"
    $ID = $Result | Measure-Object

    if ($ID.Count -ge 1) {
        Write-Output "BitLocker Recovery Key escrow to Azure AD succeeded = Compliant"
        exit 0
    }

    # If Event is not detected then mark as 'Non Compliant' and exit with 1
    else {
        Write-Warning "BitLocker Escrow Event Missing = Non Compliant"
        exit 1
    }
}

catch {
    Write-Warning "An error occurred = Non Compliant"
    exit 1
}

Remediation:

<#
.DESCRIPTION
    Script to remediate BitLocker Key escrow into Azure AD
.EXAMPLE
    PowerShell.exe -ExecutionPolicy ByPass -File <ScriptName>.ps1
.NOTES
	VERSION     AUTHOR              CHANGE
    1.0         Jonathan Conway     Initial script creation
#>

# Escrow BitLocker Recovery Key for OSDrive into Azure AD
$BitLockerVolume = Get-BitLockerVolume -MountPoint $env:SystemRoot
$RecoveryPasswordKeyProtector = $BitLockerVolume.KeyProtector | Where-Object { $_.KeyProtectorType -like "RecoveryPassword" }
BackupToAAD-BitLockerKeyProtector -MountPoint $BitLockerVolume.MountPoint -KeyProtectorId $RecoveryPasswordKeyProtector.KeyProtectorId -ErrorAction SilentlyContinue

/ JC