(196) Microsoft Intune - Uninstall/Reinstall App with removing Root CA certs
- Mr B SOE way
- 7 days ago
- 5 min read
Applications like GlobalProtect when installed will deploy Root CA certificates (when setup properly), what if a customer wants to remove the Root CA certificates along with an uninstall of GlobalProtect then re-install with another version of GlobalProtect, this is what can be done to achieve this.
What the application will do overall:
Kills GlobalProtect process
Run fast reference package to uninstall
Remove certs based on thumbprints
Sleeps for 30 seconds
Then installs GlobalProtect 6.3.3
What will we need to do first is to get the thumbnail print of those certificates, running in PowerShell ISE (Without administrator rights): Get-ChildItem -Path Cert:LocalMachine\Root, this will generate:

Make a copy of those thumbprint IDs as we will need to place it in the Install.ps1 script.
$pname = "GlobalProtect"
$Pversion = "6.3.3"
$fastPackageReference = (Get-Package $pname).FastPackageReference
$msiExecCommand = "MsiExec.exe /X `"$fastPackageReference`" /qn"
#region Config
$AppName = $PName + "_" + $Pversion + "_UninstallPackage"
$client = "MrBSOEWay"
$logPath = "$env:ProgramData\$client\logs"
$logFile = "$logPath\$appName.log"
$installer = $PName + "_" + $Pversion + "_Uninstaller.log"
#endregion
#region Logging
if (!(Test-Path -Path $logPath)) {
New-Item -Path $logPath -ItemType Directory -Force | Out-Null
}
Start-Transcript -Path $logFile -Force
Write-Host "Uninstalling $PName $Pversion"
#Kill processes
Stop-Process -Name "PanGPA" -Force
$process = (Start-Process -FilePath "MsiExec.exe" -ArgumentList "/X `"{9B5DB5BD-96A1-4830-8C73-E53204374E17}`" /qn" -Wait -PassThru)
#Sleep for 3 seconds
Start-Sleep 3
#Remove certs
Remove-Item -Path "Cert:\LocalMachine\Root\DE0A181E42E6FFCBF4DEDFCCE9BF1ECE06437551" -Force
Remove-Item -Path "Cert:\LocalMachine\Root\7CC55538F5ADB0BE0487E893D2CA37ACF871AFCB" -Force
#Sleep for 30 seconds
Start-Sleep 30
#Installing app
Copy-Item ".\GlobalInstaller" "C:\" -Recurse -Force
#This is kicking the install fromm this location because of Airlock whitelisting limitations in client's environment.
#Region Functions
function Get-MsiInformation {
[CmdletBinding(SupportsShouldProcess = $true,
PositionalBinding = $false,
ConfirmImpact = 'Medium')]
[Alias("gmsi")]
Param(
[parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = "Provide the path to an MSI")]
[ValidateNotNullOrEmpty()]
[System.IO.FileInfo[]]$Path,
[parameter(Mandatory = $false)]
[ValidateSet( "ProductCode", "Manufacturer", "ProductName", "ProductVersion", "ProductLanguage" )]
[string[]]$Property = ( "ProductCode", "Manufacturer", "ProductName", "ProductVersion", "ProductLanguage" )
)
Begin {
# Do nothing for prep
}
Process {
ForEach ( $P in $Path ) {
if ($pscmdlet.ShouldProcess($P, "Get MSI Properties")) {
try {
Write-Verbose -Message "Resolving file information for $P"
$MsiFile = Get-Item -Path $P
Write-Verbose -Message "Executing on $P"
# Read property from MSI database
$WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
$MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $WindowsInstaller, @($MsiFile.FullName, 0))
# Build hashtable for retruned objects properties
$PSObjectPropHash = [ordered]@{File = $MsiFile.FullName }
ForEach ( $Prop in $Property ) {
Write-Verbose -Message "Enumerating Property: $Prop"
$Query = "SELECT Value FROM Property WHERE Property = '$( $Prop )'"
$View = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($Query))
$View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
$Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
$Value = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)
# Return the value to the Property Hash
$PSObjectPropHash.Add($Prop, $Value)
}
# Build the Object to Return
$Object = @( New-Object -TypeName PSObject -Property $PSObjectPropHash )
# Commit database and close view
$MSIDatabase.GetType().InvokeMember("Commit", "InvokeMethod", $null, $MSIDatabase, $null)
$View.GetType().InvokeMember("Close", "InvokeMethod", $null, $View, $null)
$MSIDatabase = $null
$View = $null
}
catch {
Write-Error -Message $_.Exception.Message
}
finally {
Write-Output -InputObject @( $Object )
}
} # End of ShouldProcess If
} # End For $P in $Path Loop
}
End {
# Run garbage collection and release ComObject
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($WindowsInstaller) | Out-Null
[System.GC]::Collect()
}
}
<#
End of Function
#>
Try {
#Read file info
#$exe = (Get-ChildItem -Recurse -Path "C:\GlobalInstaller\.exe").FullName
#Add support for multiple exes. In that case the one containing setup.exe will be used.
if ($exe.count -gt 1) { $exe = $exe | Where-Object { $_ -like "*Setup.exe" } }
if ($exe) {
$file = $exe
$info = (Get-ItemProperty $file).VersionInfo
$PName = ($info.ProductName -replace '\s+', '')
$Pversion = ($info.ProductVersion -replace '\s+', '')
}
if (!($exe)) {
$file = (Get-ChildItem -Path "C:\GlobalInstaller\GlobalProtect64.msi").FullName
$msi = Get-MsiInformation -Path $file
$PName = ($msi.ProductName -replace '\s+', '')
$Pversion = ($msi.ProductVersion -replace '\s+', '')
}
#region Config
$AppName = $PName + "_" + $Pversion + "_Package"
$client = "MrBSOEWay"
$logPath = "$env:ProgramData\$client\logs"
$logFile = "$logPath\$appName.log"
$installer = $PName + "_" + $Pversion + "_Installer.log"
#endregion
#region Logging
if (!(Test-Path -Path $logPath)) {
New-Item -Path $logPath -ItemType Directory -Force | Out-Null
}
Start-Transcript -Path $logFile -Force
Write-Host "Installing $PName $Pversion"
#$mst = (Get-ChildItem -Recurse -Path ".\*.mst").Name
if ($mst) {
$process = (Start-Process -FilePath $file -ArgumentList TRANSFORMS="$mst", /qn, /norestart, "/l*v $logPath\$installer" -Wait -PassThru).ExitCode
}
Else {
$process = (Start-Process -FilePath $file -ArgumentList /qn, /norestart, "/l*v $logPath\$installer" -Wait -PassThru).ExitCode
Start-Sleep -seconds 02
reg import "C:\GlobalInstaller\GlobalProtectPortal.reg"
}
Start-Sleep -Second 03
Remove-item "C:\GlobalInstaller" -Recurse -Force
}
catch {
$errorMsg = $_.Exception.Message
}
finally {
if ($errorMsg) {
Write-Warning $errorMsg
Stop-Transcript
throw $errorMsg
Exit $process
}
else {
Write-Host "Script completed successfully.."
Stop-Transcript
Exit $process
}
}
Write-Host "Script completed successfully.."
Stop-Transcript
To break it down from lines 1 to 35, we use Fast Reference to remove GlobalProtect Version 6.3.3, but before we do that we have to stop the process from running which is PanGPA. We don’t want to stop the service. Once the service has stopped running, we execute the command to uninstall it - we allow up to 3 seconds for it to do whatever it needs to do before.
With removing the thumbnails for those Root certs, we use the “ReplaceWithThumbNailID1” and “ReplaceWithThumbNailID2” with whatever was gathered when running Get-ChildItem -Path Cert:LocalMachine\Root
Remove-Item -Path "Cert:\LocalMachine\Root\ReplaceWithThumbNailID1" -Force
Remove-Item -Path "Cert:\LocalMachine\Root\ReplaceWithThumbNailID2" -Force
$pname = "GlobalProtect"
$Pversion = "6.3.3"
$fastPackageReference = (Get-Package $pname).FastPackageReference
$msiExecCommand = "MsiExec.exe /X `"$fastPackageReference`" /qn"
#region Config
$AppName = $PName + "_" + $Pversion + "_UninstallPackage"
$client = "MrBSOEWay"
$logPath = "$env:ProgramData\$client\logs"
$logFile = "$logPath\$appName.log"
$installer = $PName + "_" + $Pversion + "_Uninstaller.log"
#endregion
#region Logging
if (!(Test-Path -Path $logPath)) {
New-Item -Path $logPath -ItemType Directory -Force | Out-Null
}
Start-Transcript -Path $logFile -Force
Write-Host "Uninstalling $PName $Pversion"
#Kill processes from running
Stop-Process -Name "PanGPA" -Force
$process = (Start-Process -FilePath "MsiExec.exe" -ArgumentList "/X `"{9B5DB5BD-96A1-4830-8C73-E53204374E17}`" /qn" -Wait -PassThru)
#Sleep for 3 seconds
Start-Sleep 3
#Remove certs
Remove-Item -Path "Cert:\LocalMachine\Root\ReplaceWithThumbNailID1" -Force
Remove-Item -Path "Cert:\LocalMachine\Root\ReplaceWithThumbNailID2" -Force
#Sleep for 30 seconds
Start-Sleep 30For detect.ps1 - as we are re-installing GlobalProtect 6.3.3 again, this would not work with as the detect script will kick in and “Go hey this installed”
try {
$appname = 'GlobalProtect';
$output = 'Detected';
$Newversion = [System.Version]'6.3.3';
$Currentversion = ((Get-Package -Name $appname -ErrorAction SilentlyContinue).version)
if ($Currentversion.count -gt 1 ) {
if ([System.Version]$Currentversion[0] -ge $Newversion) {
return $output
}
}
else {
if ([System.Version]$Currentversion -ge $Newversion) {
return $output
}
}
}
catch { exit }I added another detection script to check for the log on line 4 where $LogPath
try {
$appname = 'GlobalProtect'
$output = 'Detected'
$Newversion = [System.Version]'6.3.3'
$LogPath = "C:\ProgramData\MrBSOEWay\logs\GlobalProtect_6.3.3_Installer.log"
# 1. Check if the specific Installer Log exists
$LogExists = Test-Path -Path $LogPath
# 2. Get the current installed version
$Package = Get-Package -Name $appname -ErrorAction SilentlyContinue
$Currentversion = $Package.Version
if ($Currentversion) {
# Handle cases where multiple versions might be returned (array)
$LatestInstalled = ($Currentversion | ForEach-Object { [System.Version]$_ } | Sort-Object -Descending)[0]
# 3. Success Criteria: Version is >= 6.3.3 AND the log file exists
if ($LatestInstalled -ge $Newversion -and $LogExists) {
return $output
}
}
}
catch {
# Exit with non-zero if you want the detection to fail on error
exit 1
}The overall package should look like this:

Where GlobalInstaller folder shows:




Comments