From 9f6c803b4f0780e9e8261a50b5e41b5233444bf7 Mon Sep 17 00:00:00 2001 From: Marcos Oviedo Date: Thu, 6 Apr 2023 17:26:33 -0300 Subject: [PATCH] Adding fix to not use COM installer object (#9661) This relates to #9576 --- ...fails-to-install-on-legacy-windows-servers | 1 + orbit/pkg/packaging/windows_templates.go | 385 +++++++++++------- 2 files changed, 238 insertions(+), 148 deletions(-) create mode 100644 orbit/changes/bug-9576-orbit-fails-to-install-on-legacy-windows-servers diff --git a/orbit/changes/bug-9576-orbit-fails-to-install-on-legacy-windows-servers b/orbit/changes/bug-9576-orbit-fails-to-install-on-legacy-windows-servers new file mode 100644 index 0000000000..f9e38d6962 --- /dev/null +++ b/orbit/changes/bug-9576-orbit-fails-to-install-on-legacy-windows-servers @@ -0,0 +1 @@ +* Orbit now installs propery on Windows Server 2012 and 2016 environments with legacy Orbit or Osquery previously installed diff --git a/orbit/pkg/packaging/windows_templates.go b/orbit/pkg/packaging/windows_templates.go index 8c113bce16..6a6339898f 100644 --- a/orbit/pkg/packaging/windows_templates.go +++ b/orbit/pkg/packaging/windows_templates.go @@ -205,7 +205,17 @@ var windowsPSInstallerUtils = template.Must(template.New("").Option("missingkey= [switch] $help = $false ) -$ErrorActionPreference = "SilentlyContinue" +#ErrorActionPreference valid values are as follows: +# Break: Enter the debugger when an error occurs or when an exception is raised. +# Continue: (Default) Displays the error message and continues executing. +# Ignore: Suppresses the error message and continues to execute the command. The Ignore value is intended for per-command use, not for use as saved preference. Ignore isn't a valid value for the $ErrorActionPreference variable. +# Inquire: Displays the error message and asks you whether you want to continue. +# SilentlyContinue: No effect. The error message isn't displayed and execution continues without interruption. +# Stop: Displays the error message and stops executing. In addition to the error generated, the Stop value generates an ActionPreferenceStopException object to the error stream. +# Suspend: Automatically suspends a workflow job to allow for further investigation. After investigation, the workflow can be resumed. The Suspend value is intended for per-command use, not for use as saved preference. Suspend isn't a valid value for the $ErrorActionPreference variable. + +$ErrorActionPreference = "Continue" + $code = @" using Microsoft.Win32; @@ -410,214 +420,293 @@ function Do-Help { Write-Host " -uninstallOsquery Uninstall Osquery" Write-Host " -uninstallOrbit Uninstall Orbit" Write-Host " -stopOrbit Stop Orbit" - Write-Host "" Write-Host " -help Shows this help screen" Exit 1 } +# borrowed from Jeffrey Snover http://blogs.msdn.com/powershell/archive/2006/12/07/resolve-error.aspx +function Resolve-Error-Detailed($ErrorRecord = $Error[0]) { + $error_message = "========== ErrorRecord:{0}ErrorRecord.InvocationInfo:{1}Exception:{2}" + $formatted_errorRecord = $ErrorRecord | format-list * -force | out-string + $formatted_invocationInfo = $ErrorRecord.InvocationInfo | format-list * -force | out-string + $formatted_exception = "" + $Exception = $ErrorRecord.Exception + for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException)) { + $formatted_exception += ("$i" * 70) + "-----" + $formatted_exception += $Exception | format-list * -force | out-string + $formatted_exception += "-----" + } + + return $error_message -f $formatted_errorRecord, $formatted_invocationInfo, $formatted_exception +} + #Stops Osquery service and related processes function Stop-Osquery { $kServiceName = "osqueryd" # Stop Service - Stop-Service -Name $kServiceName - Start-Sleep -Milliseconds 1000 + Stop-Service -Name $kServiceName -ErrorAction "Continue" + Start-Sleep -Milliseconds 1000 # Ensure that no process left running - Get-Process -Name $kServiceName | Stop-Process -Force + Get-Process -Name $kServiceName -ErrorAction "SilentlyContinue" | Stop-Process -Force } #Stops Orbit service and related processes function Stop-Orbit { # Stop Service - Stop-Service -Name "Fleet osquery" + Stop-Service -Name "Fleet osquery" -ErrorAction "Continue" Start-Sleep -Milliseconds 1000 # Ensure that no process left running - Get-Process -Name "orbit" | Stop-Process -Force - Get-Process -Name "osqueryd" | Stop-Process -Force - Get-Process -Name "fleet-desktop" | Stop-Process -Force + Get-Process -Name "orbit" -ErrorAction "SilentlyContinue" | Stop-Process -Force + Get-Process -Name "osqueryd" -ErrorAction "SilentlyContinue" | Stop-Process -Force + Get-Process -Name "fleet-desktop" -ErrorAction "SilentlyContinue" | Stop-Process -Force Start-Sleep -Milliseconds 1000 } + #Revove Orbit footprint from registry and disk function Force-Remove-Orbit { - #Stoping Orbit - Stop-Orbit + try { - #Remove Service - $service = Get-WmiObject -Class Win32_Service -Filter "Name='Fleet osquery'" - $service.delete() + #Stoping Orbit + Stop-Orbit - #Removing Program files entries - $targetPath = $Env:Programfiles + "\\Orbit" - Remove-Item -LiteralPath $targetPath -Force -Recurse - - #Remove HKLM registry entries - Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse | Where-Object {($_.ValueCount -gt 0)} | ForEach-Object { - - # Filter for osquery entries - $properties = Get-ItemProperty $_.PSPath | Where-Object {($_.DisplayName -eq "Fleet osquery")} - if ($properties) { - - #Remove Registry Entries - $regKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + $_.PSChildName - Get-Item $regKey | Remove-Item -Force - - return + #Remove Service + $service = Get-WmiObject -Class Win32_Service -Filter "Name='Fleet osquery'" + if ($service) { + $service.delete() | Out-Null } - } + + #Removing Program files entries + $targetPath = $Env:Programfiles + "\\Orbit" + Remove-Item -LiteralPath $targetPath -Force -Recurse -ErrorAction "Continue" + + #Remove HKLM registry entries + Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse -ErrorAction "SilentlyContinue" | Where-Object {($_.ValueCount -gt 0)} | ForEach-Object { + + # Filter for osquery entries + $properties = Get-ItemProperty $_.PSPath -ErrorAction "SilentlyContinue" | Where-Object {($_.DisplayName -eq "Fleet osquery")} + if ($properties) { + + #Remove Registry Entries + $regKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + $_.PSChildName + + Get-Item $regKey -ErrorAction "SilentlyContinue" | Remove-Item -Force -ErrorAction "SilentlyContinue" + + return + } + } + } + catch { + Write-Host "There was a problem running Force-Remove-Orbit" -ForegroundColor Red + Write-Host "=====================================" + Write-Host "$(Resolve-Error-Detailed)" + Write-Host "=====================================" + return $false + } + + return $true } #Revove Osquery footprint from registry and disk function Force-Remove-Osquery { - #Stoping Osquery - Stop-Osquery + try { - #Remove Service - $service = Get-WmiObject -Class Win32_Service -Filter "Name='osqueryd'" - $service.delete() | Out-Null + #Stoping Osquery + Stop-Osquery - #Remove HKLM registry entries and disk footprint - Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse | Where-Object {($_.ValueCount -gt 0)} | ForEach-Object { - - # Filter for osquery entries - $properties = Get-ItemProperty $_.PSPath | Where-Object {($_.DisplayName -eq "osquery")} - if ($properties) { - - #Remove files from osquery location - if ($properties.InstallLocation){ - Remove-Item -LiteralPath $properties.InstallLocation -Force -Recurse - } - - #Remove Registry Entries - $regKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + $_.PSChildName - Get-Item $regKey | Remove-Item -Force - - return + #Remove Service + $service = Get-WmiObject -Class Win32_Service -Filter "Name='osqueryd'" + if ($service) { + $service.delete() | Out-Null } - } - #Remove user entries if present - [RegistryUtils]::RemoveOsqueryInstallationFromUserHives() + #Remove HKLM registry entries and disk footprint + Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -Recurse -ErrorAction "SilentlyContinue" | Where-Object {($_.ValueCount -gt 0)} | ForEach-Object { + + # Filter for osquery entries + $properties = Get-ItemProperty $_.PSPath -ErrorAction "SilentlyContinue" | Where-Object {($_.DisplayName -eq "osquery")} + if ($properties) { + + #Remove files from osquery location + if ($properties.InstallLocation){ + Remove-Item -LiteralPath $properties.InstallLocation -Force -Recurse -ErrorAction "SilentlyContinue" + } + + #Remove Registry Entries + $regKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + $_.PSChildName + Get-Item $regKey -ErrorAction "SilentlyContinue" | Remove-Item -Force -ErrorAction "SilentlyContinue" + + return + } + } + + #Remove user entries if present + [RegistryUtils]::RemoveOsqueryInstallationFromUserHives() + } + catch { + Write-Host "There was a problem running Force-Remove-Osquery" -ForegroundColor Red + Write-Host "=====================================" + Write-Host "$(Resolve-Error-Detailed)" + Write-Host "=====================================" + + return $false + } + + return $true } function Graceful-Product-Uninstall($productName) { - if (!$productName) { - Write-Host "Product name should be provided" -foregroundcolor Yellow - return $false - } + try { - # Grabbing the location of msiexec.exe - $targetBinPath = Resolve-Path "$env:windir\system32\msiexec.exe" - if (!(Test-Path $targetBinPath)) { - Write-Host "msiexec.exe cannot be located." -foregroundcolor Yellow - return $false - } + if (!$productName) { + Write-Host "Product name should be provided" -foregroundcolor Yellow + return $false + } - # Creating a COM instance of the WindowsInstaller.Installer COM object - $Installer = New-Object -ComObject WindowsInstaller.Installer - if (!$Installer) { - Write-Host "There was a problem retrieving the installed packages." -foregroundcolor Yellow - return $false - } - - # Enumerating the installed packages - $ProductEnumFlag = 7 #installed packaged enumeration flag - $InstallerProducts = $Installer.ProductsEx("", "", $ProductEnumFlag); - if (!$InstallerProducts) { - Write-Host "Installed packages cannot be retrieved." -foregroundcolor Yellow - return $false - } - - # Iterating over the installed packages results and checking for osquery package - ForEach ($Product in $InstallerProducts) { - - $ProductCode = $null - $VersionString = $null - $ProductPath = $null - - try { - $ProductCode = $Product.ProductCode() - $VersionString = $Product.InstallProperty("VersionString") - $ProductPath = $Product.InstallProperty("ProductName") - } - catch { } - - if ($ProductPath -like $productName) { - Write-Host "Graceful uninstall of $ProductPath version $VersionString." -foregroundcolor Cyan - $InstallProcess = Start-Process $targetBinPath -ArgumentList "/quiet /x $ProductCode" -PassThru -Verb RunAs -Wait - if ($InstallProcess.ExitCode -eq 0) { - return $true - } else { - Write-Host "There was an error uninstalling osquery. Error code was: $($InstallProcess.ExitCode)." -foregroundcolor Yellow - return $false + if ($productName -eq "Fleet osquery") { + Stop-Orbit + } elseif ($productName -eq "osquery") { + Stop-Osquery + } + + # Grabbing the location of msiexec.exe + $targetBinPath = Resolve-Path "$env:windir\system32\msiexec.exe" + if (!(Test-Path $targetBinPath)) { + Write-Host "msiexec.exe cannot be located." -foregroundcolor Yellow + return $false + } + + # Creating a COM instance of the WindowsInstaller.Installer COM object + $Installer = New-Object -ComObject WindowsInstaller.Installer + if (!$Installer) { + Write-Host "There was a problem retrieving the installed packages." -foregroundcolor Yellow + return $false + } + + # Enumerating the installed packages + $ProductEnumFlag = 7 #installed packaged enumeration flag + $InstallerProducts = $Installer.ProductsEx("", "", $ProductEnumFlag); + if (!$InstallerProducts) { + Write-Host "Installed packages cannot be retrieved." -foregroundcolor Yellow + return $false + } + + # Iterating over the installed packages results and checking for osquery package + ForEach ($Product in $InstallerProducts) { + + $ProductCode = $null + $VersionString = $null + $ProductPath = $null + + $ProductCode = $Product.ProductCode() + $VersionString = $Product.InstallProperty("VersionString") + $ProductPath = $Product.InstallProperty("ProductName") + + if ($ProductPath -like $productName) { + Write-Host "Graceful uninstall of $ProductPath version $VersionString." -foregroundcolor Cyan + $InstallProcess = Start-Process $targetBinPath -ArgumentList "/quiet /x $ProductCode" -PassThru -Verb RunAs -Wait + if ($InstallProcess.ExitCode -eq 0) { + return $true + } else { + Write-Host "There was an error uninstalling osquery. Error code was: $($InstallProcess.ExitCode)." -foregroundcolor Yellow + return $false + } } - } + } } + catch { + Write-Host "There was a problem running Graceful-Product-Uninstall" -ForegroundColor Red + Write-Host "=====================================" + Write-Host "$(Resolve-Error-Detailed)" + Write-Host "=====================================" + } return $false } function Main { - # Is Administrator check - if (-not (Test-Administrator)) { - Write-Host "Please run this script with Admin privileges!" -foregroundcolor Red - Exit -1 - } - # Help commands - if ($help) { - Do-Help - Exit -1 - } + try { + # Is Administrator check + if (-not (Test-Administrator)) { + Write-Host "Please run this script with Admin privileges!" -foregroundcolor Red + Exit -1 + } + + # Help commands + if ($help) { + Do-Help + Exit -1 + } + + if ($uninstallOsquery) { + Write-Host "About to uninstall Osquery." -foregroundcolor Yellow - if ($uninstallOsquery) { - Write-Host "About to uninstall Osquery." -foregroundcolor Yellow + #if (Graceful-Product-Uninstall("osquery")) { + if ($false) { + Force-Remove-Osquery #best effort action to ensure cleanup after graceful uninstall + Write-Host "Osquery was gracefully uninstalled." -foregroundcolor Cyan + Exit 0 + + } else { + if (Force-Remove-Osquery) { + Write-Host "Osquery was uninstalled." -foregroundcolor Cyan + Exit 0 + } else { + Write-Host "There was a problem uninstalling Osquery" -foregroundcolor Cyan + Exit -1 + } + } + + } elseif ($uninstallOrbit) { + Write-Host "About to uninstall Orbit." -foregroundcolor Yellow + + #if (Graceful-Product-Uninstall("Fleet osquery")) { + if ($false) { + Force-Remove-Orbit #best effort action to ensure cleanup after graceful uninstall + Write-Host "Orbit was gracefully uninstalled." -foregroundcolor Cyan + Exit 0 + + } else { + if (Force-Remove-Orbit) { + Write-Host "Orbit was uninstalled." -foregroundcolor Cyan + Exit 0 + } else { + Write-Host "There was a problem uninstalling Orbit" -foregroundcolor Cyan + Exit -1 + } + } + + } elseif ($stopOrbit) { + Write-Host "About to stop Orbit and remove it from system." -foregroundcolor Yellow + + Stop-Orbit + + Write-Host "Orbit was stopped." -foregroundcolor Cyan + Exit 0 - Stop-Osquery - - if (Graceful-Product-Uninstall("osquery")) { - Write-Host "Osquery was gracefully uninstalled." -foregroundcolor Cyan } else { - Force-Remove-Osquery - Write-Host "Osquery was uninstalled." -foregroundcolor Cyan + Write-Host "Invalid option selected: please see -help for usage details." -foregroundcolor Red + Do-Help + Exit -1 } - Exit 0 - - } elseif ($uninstallOrbit) { - Write-Host "About to uninstall Orbit." -foregroundcolor Yellow - - Stop-Orbit - - if (Graceful-Product-Uninstall("Fleet osquery")) { - Force-Remove-Orbit - Write-Host "Orbit was gracefully uninstalled." -foregroundcolor Cyan - } else { - Force-Remove-Orbit - Write-Host "Orbit was uninstalled." -foregroundcolor Cyan - } - Exit 0 - } elseif ($stopOrbit) { - Write-Host "About to stop Orbit and remove it from system." -foregroundcolor Yellow - - Stop-Orbit - - Write-Host "Orbit was stopped." -foregroundcolor Cyan - Exit 0 - } else { - Write-Host "Invalid option selected: please see -help for usage details." -foregroundcolor Red - Do-Help + } catch { + Write-Host "There was a problem running installer entry point logic" -ForegroundColor Red + Write-Host "=====================================" + Write-Host "$(Resolve-Error-Detailed)" + Write-Host "=====================================" Exit -1 - } }