KACE Script - Trouble finding registry values
I am writing a script to uninstall all versions of Google Chrome (MSI and EXE installs) so that I can install Google Chrome Enterprise 64-bit. After banging my head against the wall I finally found a script over at Stackoverflow that helps accomplish this. Combined it with some other scripts I made to check if Chrome was running etc. It works great when run locally and accomplishes exactly what I need it to. When run through KACE however as an Online KScript, it seems to have trouble finding the objects in the registry. I have tried changing the logic numerous times as well as tried using HKLM32 and HKLM64 with no luck.
Going to post the script below in hopes that someone can tell me how to get it to work when pushed through KACE.
$chromeMSI = "$PSScriptRoot\chrome64ent.msi"
$chromeProcess = Get-Process -Name "chrome" -ErrorAction SilentlyContinue
function Show-MessageBox {
param (
[string]$Message,
[string]$Title,
[System.Windows.Forms.MessageBoxButtons]$Buttons,
[System.Windows.Forms.MessageBoxIcon]$Icon
)
[System.Windows.Forms.MessageBox]::Show($Message, $Title, $Buttons, $Icon)
}
if ($chromeProcess) {
$choice = Show-MessageBox -Message "A Google Chrome upgrade is pending. Do you want to upgrade it?" -Title "IT Department" -Buttons YesNo -Icon Question
if ($choice -eq "Yes") {
# Proceed with upgrade
} else {
exit 0
}
}
# We need to check both 32 and 64 bit registry paths
$regPaths =
"HKLM:\SOFTWARE\Wow6432node\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
# Checks for both 32bit and 64bit
$productCodes = @( $regPaths | Foreach-Object {
Get-ItemProperty "${_}\*" | Where-Object {
$_.DisplayName -eq 'Google Chrome'
}
} ).PSPath
# Run the uninstall string
$productCodes | ForEach-Object {
$keyName = ( Get-ItemProperty $_ ).PSChildName
# GUID check (if the previous key was not a product code we'll need a different removal strategy)
if ( $keyName -match '^{[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}}$' ) {
# Uninstalls silently and logs the process
$p = Start-Process -Wait msiexec -ArgumentList "/l*v ""$($pwd.FullName)/chromeuninst.log"" /X${keyName} /qn" -PassThru
# 0 means success, but 1638 means uninstallation is pending a reboot to complete
# This should still be considered a successful execution
$acceptableExitCodes = 0, 1638
}
else {
Write-Host "Stopping all running instances of chrome.exe, if any are running"
Get-Process chrome.exe -EA Ignore | Stop-Process -Force
# The registry key still has an uninstall string
# But cannot be silently removed
# So we will have to get creating and control the uninstall window with PowerShell
# We need to use the undocumented --force-uninstall parameter, added to below command
$uninstallString = "$(( Get-ItemProperty $_).UninstallString )) --force-uninstall"
# Break up the string into the executable and arguments so we can wait on it properly with Start-Process
$firstQuoteIdx = $uninstallString.IndexOf('"')
$secondQuoteIdx = $uninstallString.IndexOf('"', $firstQuoteIdx + 1)
$setupExe = $uninstallString[$firstQuoteIdx..$secondQuoteIdx] -join ''
$setupArgs = $uninstallString[( $secondQuoteIdx + 1 )..$uninstallString.Length] -join ''
Write-Host "Uninstallation command: ${setupExe} ${setupArgs}"
$p = Start-Process -Wait -FilePath $setupExe -ArgumentList $setupArgs -PassThru
# Acceptable Exit Codes
$acceptableExitCodes = 0, 19
}
if ( $p.ExitCode -notin $acceptableExitCodes ) {
Write-Error "Program exited with $($p.ExitCode)"
$p.Dispose()
exit $p.ExitCode
}
}
# Install Google Chrome using the MSI
Start-Process -FilePath "msiexec.exe" -ArgumentList "/i $chromeMSI /qn" -NoNewWindow -Wait
# Wait for installation to complete
Start-Sleep -Seconds 30
# Display notification using a NotifyIcon
Add-Type -AssemblyName System.Windows.Forms
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
$notifyIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -id $PID).Path)
$notifyIcon.Text = "Google Chrome Upgrade"
$notifyIcon.Visible = $true
$notifyIcon.ShowBalloonTip(5000, "IT Dept", "Google Chrome upgrade is complete!", [System.Windows.Forms.ToolTipIcon]::Info)
# Wait for user interaction (optional)
Start-Sleep -Seconds 10
# Clean up
$notifyIcon.Dispose()
Answers (2)
Kace agent is 32 bit, so it can only see the 32 bit area (Wow6432Node) of the registry by default. (v14 will have a 64 bit agent)
You wouldn't specify Wow6432Node because it's assumed. Does this work?
$regPaths =
"HKLM64:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
-or-
"HKLM64:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM32:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
Thank you for your quick reply Amber.
Neither of those paths work. I have seen the documentation by Quest stating that those paths need to be used as well. However, when you try to run anything using a PowerShell script as a dependency using those paths the KACE script logs return something along the lines of "path not found". I do know that when using the built in registry check scripting functions that you must use HKLM64:\ or else it will not find that path.
From what I can tell, it is finding the registry paths correctly, it is just failing to return any values from the keys themselves using commands like 'Get-ItemProperty'. After reading another post on ITninja (lost the link) I ended up having it launch PowerShell using the path below which helped to get it pull values from the fields in the registry keys.
After it started pulling some values I went back and rewrote the script to get rid of a lot of the pipelines and that seemed to correct any other errors I was getting. Never could get the message box to populate from the Show-MessageBox function when PowerShell was launched from this path. Ended up throwing it out.
Below is my revised code that appears to be working.
# Define registry paths for 32-bit and 64-bit installations of Google Chrome
$chrome32bit = "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
$chrome64bit = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*"
# Define the registry paths for the array
$regPaths = @(
"HKLM:\SOFTWARE\Wow6432node\Microsoft\Windows\CurrentVersion\Uninstall",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
)
# Initialize log file path
$logFilePath = "C:\tempFolder\chromeUpgrade.log"
function Write-Log($Message) {
$TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[${TimeStamp}] $Message"
$LogMessage | Out-File -FilePath $logFilePath -Append
}
function CheckChromeInstallation {
# Check 32-bit installations
$installed32 = Get-ChildItem $chrome32bit
$installed32Props = @()
foreach ($item in $installed32) {
$props = Get-ItemProperty $item.PSPath
$installed32Props += $props
}
$installed32Chrome = $null
foreach ($prop in $installed32Props) {
if ($prop.DisplayName -like "Google Chrome") {
$installed32Chrome = $prop
break
}
}
# Check 64-bit installations
$installed64 = Get-ChildItem $chrome64bit
$installed64Props = @()
foreach ($item in $installed64) {
$props = Get-ItemProperty $item.PSPath
$installed64Props += $props
}
$installed64Chrome = $null
foreach ($prop in $installed64Props) {
if ($prop.DisplayName -like "Google Chrome") {
$installed64Chrome = $prop
break
}
}
if ($installed32Chrome -ne $null) {
Write-Log "Google Chrome 32-bit is installed."
return "32-bit"
}
if ($installed64Chrome -ne $null) {
Write-Log "Google Chrome 64-bit is installed."
return "64-bit"
}
Write-Log "Google Chrome is not installed."
return "Not-Installed"
}
CheckChromeInstallation
function CheckChromeInstallSuccess {
# Check 32-bit installations
$installed32 = Get-ChildItem $chrome32bit
$installed32Props = @()
foreach ($item in $installed32) {
$props = Get-ItemProperty $item.PSPath
$installed32Props += $props
}
$installed32Chrome = $null
foreach ($prop in $installed32Props) {
if ($prop.DisplayName -like "Google Chrome") {
$installed32Chrome = $prop
break
}
}
# Check 64-bit installations
$installed64 = Get-ChildItem $chrome64bit
$installed64Props = @()
foreach ($item in $installed64) {
$props = Get-ItemProperty $item.PSPath
$installed64Props += $props
}
$installed64Chrome = $null
foreach ($prop in $installed64Props) {
if ($prop.DisplayName -like "Google Chrome") {
$installed64Chrome = $prop
break
}
}
if ($installed32Chrome -ne $null) {
Write-Log "Google Chrome 32-bit is installed."
return "32-bit"
}
if ($installed64Chrome -ne $null) {
Write-Log "Google Chrome 64-bit is installed."
return "64-bit"
}
Write-Log "Google Chrome is not installed."
return "Not-Installed"
}
# Define an array to store product codes
$productCodes = @()
# Iterate over each registry path
foreach ($regPath in $regPaths) {
# Get item properties for the current registry path
$itemProperties = Get-ItemProperty "${regPath}\*"
# Filter item properties where DisplayName is 'Google Chrome'
foreach ($itemProperty in $itemProperties) {
if ($itemProperty.DisplayName -eq 'Google Chrome') {
# Add the PSPath of the matching item to the productCodes array
$productCodes += $itemProperty.PSPath
}
}
}
# Iterate over each product code
foreach ($productCode in $productCodes) {
# Get the key name
$keyName = (Get-ItemProperty $productCode).PSChildName
# Check if the key name matches a GUID
if ($keyName -match '^{[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}}$') {
# Uninstall using msiexec
Write-Log "Uninstalling Google Chrome using MSIExec..."
$p = Start-Process -Wait msiexec -ArgumentList "/X${keyName} /qn" -PassThru
$acceptableExitCodes = 0, 1638
} else {
# Stop all running instances of chrome.exe
Write-Log "Stopping all running instances of chrome.exe, if any are running..."
$chromeProcesses = Get-Process -Name "chrome.exe" -ErrorAction Ignore
foreach ($process in $chromeProcesses) {
Stop-Process -InputObject $process -Force
Write-Log "Stopped process $($process.Id) - $($process.Name)"
}
# Get the uninstall string
$uninstallString = (Get-ItemProperty $productCode).UninstallString + " --force-uninstall"
# Extract executable and arguments
$firstQuoteIdx = $uninstallString.IndexOf('"')
$secondQuoteIdx = $uninstallString.IndexOf('"', $firstQuoteIdx + 1)
$setupExe = $uninstallString.Substring(0, $secondQuoteIdx + 1)
$setupArgs = $uninstallString.Substring($secondQuoteIdx + 1).Trim()
Write-Log "Uninstall command: ${setupExe} ${setupArgs}"
# Run the uninstallation command
Write-Log "Running uninstall command..."
$p = Start-Process -Wait -FilePath $setupExe -ArgumentList $setupArgs -PassThru
$acceptableExitCodes = 0, 19
}
# Check exit code
if ($p.ExitCode -notin $acceptableExitCodes) {
Write-Error "Program exited with $($p.ExitCode)"
Write-Log "Error: Program exited with $($p.ExitCode)"
$p.Dispose()
Write-Log "Google Chrome failed to uninstall correctly."
# Define the file path where you want to create the TXT file
$filePath = "C:\tempFolder\BADchromeBread.txt"
# Get the current date in a desired format
$currentDate = Get-Date -Format "yyyy-MM-dd"
# Create the content for the TXT file including the creation date
$fileContent = "BAD Google Chrome Breadcrumb for 64-bit Update FAIL created on: $currentDate"
# Write the content to the TXT file, overwriting if it already exists
$fileContent | Set-Content -Path $filePath -Force
# Write the content to the TXT file, overwriting if it already exists
$fileContent | Set-Content -Path $filePath -Force
# Write the content to the TXT file, overwriting if it already exists
$fileContent | Set-Content -Path $filePath -Force
# Display notification using a NotifyIcon
Add-Type -AssemblyName System.Windows.Forms
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
$notifyIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -id $PID).Path)
$notifyIcon.Text = "Google Chrome Upgrade"
$notifyIcon.Visible = $true
$notifyIcon.ShowBalloonTip(5000, "IT Dept", "Google Chrome failed to uninstall. Please contact Help Desk!", [System.Windows.Forms.ToolTipIcon]::Info)
# Wait for user interaction (optional)
Start-Sleep -Seconds 10
# Clean up
$notifyIcon.Dispose()
exit $p.ExitCode
}
}
Write-Log "Checking if Google Chrome uninstalled correctly."
$chromeStatus = CheckChromeInstallation
if ($chromeStatus -eq "Not-Installed"){
# Install Google Chrome using the MSI
Write-Log "Google Chrome 64-bit install beginning."
Start-Process -FilePath "msiexec.exe" -ArgumentList "/i $chromeMSI /qn" -NoNewWindow -Wait
Write-Log "Google Chrome 64-bit installation finished."
Write-Log "Checking if install was successful."
# Wait for installation to complete
Start-Sleep -Seconds 20
$chromeStatus2 = CheckChromeInstallSuccess
if ($chromeStatus2 -eq "64-bit"){
Write-Log "Google Chrome was installed successfully!"
Write-Log "Creating breadcrum file at C:\tempFolder\chromeBread.txt"
# Define the file path where you want to create the TXT file
$filePath = "C:\tempFolder\chromeBread.txt"
# Get the current date in a desired format
$currentDate = Get-Date -Format "yyyy-MM-dd"
# Create the content for the TXT file including the creation date
$fileContent = "Google Chrome Breadcrumb for 64-bit Update created on: $currentDate"
# Write the content to the TXT file, overwriting if it already exists
$fileContent | Set-Content -Path $filePath -Force
# Write the content to the TXT file, overwriting if it already exists
$fileContent | Set-Content -Path $filePath -Force
# Define the file path to check
$filePath2 = "C:\tempFolder\BADchromeBread.txt"
Write-log "Checking for BADchromeBread.txt"
# Check if the file exists
if (Test-Path $filePath2) {
# If the file exists, delete it
Remove-Item $filePath2 -Force
Write-Log "BADchromeBread detected. Deleting '$filePath2'"
} else {
Write-Log "File '$filePath2' does not exist."
}
Write-Log "Successful execution!!"
# Display notification using a NotifyIcon
Add-Type -AssemblyName System.Windows.Forms
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
$notifyIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -id $PID).Path)
$notifyIcon.Text = "Google Chrome Upgrade"
$notifyIcon.Visible = $true
$notifyIcon.ShowBalloonTip(5000, "IT Dept", "Google Chrome upgrade is complete!", [System.Windows.Forms.ToolTipIcon]::Info)
# Wait for user interaction (optional)
Start-Sleep -Seconds 10
# Clean up
$notifyIcon.Dispose()
} else {
Write-Log "Google Chrome failed to install."
# Define the file path where you want to create the TXT file
$filePath = "C:\tempFolder\BADchromeBread.txt"
# Get the current date in a desired format
$currentDate = Get-Date -Format "yyyy-MM-dd"
# Create the content for the TXT file including the creation date
$fileContent = "BAD Google Chrome Breadcrumb for 64-bit Update FAIL created on: $currentDate"
# Write the content to the TXT file, overwriting if it already exists
$fileContent | Set-Content -Path $filePath -Force
# Write the content to the TXT file, overwriting if it already exists
$fileContent | Set-Content -Path $filePath -Force
# Write the content to the TXT file, overwriting if it already exists
$fileContent | Set-Content -Path $filePath -Force
# Display notification using a NotifyIcon
Add-Type -AssemblyName System.Windows.Forms
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
$notifyIcon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Process -id $PID).Path)
$notifyIcon.Text = "Google Chrome Upgrade"
$notifyIcon.Visible = $true
$notifyIcon.ShowBalloonTip(5000, "IT Dept", "Google Chrome failed to install. Please Contact Help Desk!", [System.Windows.Forms.ToolTipIcon]::Info)
# Wait for user interaction (optional)
Start-Sleep -Seconds 10
# Clean up
$notifyIcon.Dispose()
}
}
Comments:
-
Glad you figured it out. Personally I avoid using Kace built in scripting because there is some weirdness for sure, so I always just package whatever script I'm writing (batch, powershell, etc) into an executable and call the exe in the Kace script. Then I don't have to worry about what oddness the Kace Agent is going to do. - AmberSDNB 7 months ago