Improve 1Password uninstall reliability and add timeout#36111
Improve 1Password uninstall reliability and add timeout#36111allenhouchins merged 5 commits intomainfrom
Conversation
Added a robust PowerShell uninstall script for 1Password with process termination, registry checks, logging, and timeout handling. Updated uninstall script references and added timeout protection to generic MSI uninstall scripts to prevent hangs. These changes improve reliability and diagnostics for uninstall operations.
Script Diff Resultsee/maintained-apps/outputs/1password/windows.json=== Install Script (no changes) ===
=== Uninstall // 453051bc -> bb1e89f7 ===
--- /tmp/old.J2ARPM 2025-11-21 04:17:13.520954753 +0000
+++ /tmp/new.rhzQub 2025-11-21 04:17:13.520954753 +0000
@@ -1,13 +1,137 @@
-$product_code = "{7DB53C29-932E-4F28-9AD6-85E66EDE3DB2}"
+# 1Password Uninstall Script
+# This script uninstalls 1Password using the product code with improved reliability
-# Fleet uninstalls app using product code that's extracted on upload
-$process = Start-Process msiexec -ArgumentList @("/quiet", "/x", $product_code, "/norestart") -Wait -PassThru
+$productCode = "{7DB53C29-932E-4F28-9AD6-85E66EDE3DB2}"
+$timeoutSeconds = 300 # 5 minute timeout
+$logFile = "${env:TEMP}\fleet-1password-uninstall.log"
-# Check exit code and output result
-if ($process.ExitCode -eq 0) {
- Write-Output "Exit 0"
- Exit 0
-} else {
- Write-Output "Exit $($process.ExitCode)"
- Exit $process.ExitCode
+try {
+ # Start transcript for logging
+ Start-Transcript -Path $logFile -Append -ErrorAction SilentlyContinue
+
+ Write-Host "Starting 1Password uninstall process..."
+ Write-Host "Product Code: $productCode"
+
+ # Step 1: Close any running 1Password processes
+ Write-Host "Checking for running 1Password processes..."
+ $processes = Get-Process -Name "1Password*" -ErrorAction SilentlyContinue
+ if ($processes) {
+ Write-Host "Found $($processes.Count) running 1Password process(es), attempting to close..."
+ foreach ($proc in $processes) {
+ try {
+ Write-Host "Closing process: $($proc.ProcessName) (PID: $($proc.Id))"
+ Stop-Process -Id $proc.Id -Force -ErrorAction Stop
+ Start-Sleep -Seconds 2
+ } catch {
+ Write-Host "Warning: Could not close process $($proc.Id): $($_.Exception.Message)"
+ }
+ }
+ # Wait a bit more to ensure processes are fully terminated
+ Start-Sleep -Seconds 3
+ } else {
+ Write-Host "No running 1Password processes found."
+ }
+
+ # Step 2: Verify the product is installed before attempting uninstall
+ Write-Host "Verifying 1Password is installed..."
+ $installed = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
+ Where-Object { $_.PSChildName -eq $productCode -or $_.UninstallString -like "*$productCode*" }
+
+ if (-not $installed) {
+ Write-Host "1Password does not appear to be installed (product code not found in registry)."
+ Write-Host "This may indicate it was already uninstalled or installed differently."
+ Exit 0
+ }
+
+ Write-Host "1Password installation confirmed, proceeding with uninstall..."
+
+ # Step 3: Execute uninstall with timeout protection
+ Write-Host "Executing msiexec uninstall command..."
+ $uninstallArgs = @("/quiet", "/x", $productCode, "/norestart", "/L*v", "${env:TEMP}\1password-uninstall-msi.log")
+
+ $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo
+ $processStartInfo.FileName = "msiexec.exe"
+ $processStartInfo.Arguments = ($uninstallArgs -join " ")
+ $processStartInfo.UseShellExecute = $false
+ $processStartInfo.RedirectStandardOutput = $true
+ $processStartInfo.RedirectStandardError = $true
+ $processStartInfo.CreateNoWindow = $true
+
+ $process = New-Object System.Diagnostics.Process
+ $process.StartInfo = $processStartInfo
+
+ Write-Host "Starting uninstall process (timeout: $timeoutSeconds seconds)..."
+ $process.Start() | Out-Null
+
+ # Wait for process with timeout
+ $completed = $process.WaitForExit($timeoutSeconds * 1000)
+
+ if (-not $completed) {
+ Write-Host "ERROR: Uninstall process exceeded timeout of $timeoutSeconds seconds. Terminating..."
+ try {
+ Stop-Process -Id $process.Id -Force -ErrorAction Stop
+ Write-Host "Process terminated."
+ } catch {
+ Write-Host "Warning: Could not terminate process: $($_.Exception.Message)"
+ }
+ Exit 1603 # ERROR_INSTALL_FAILURE
+ }
+
+ $exitCode = $process.ExitCode
+ Write-Host "Uninstall process completed with exit code: $exitCode"
+
+ # Read output if available
+ $stdout = $process.StandardOutput.ReadToEnd()
+ $stderr = $process.StandardError.ReadToEnd()
+
+ if ($stdout) {
+ Write-Host "Standard output: $stdout"
+ }
+ if ($stderr) {
+ Write-Host "Standard error: $stderr"
+ }
+
+ # Check MSI log for additional details
+ $msiLogPath = "${env:TEMP}\1password-uninstall-msi.log"
+ if (Test-Path $msiLogPath) {
+ Write-Host "MSI log file available at: $msiLogPath"
+ # Read last 50 lines of MSI log for troubleshooting
+ $msiLogTail = Get-Content $msiLogPath -Tail 50 -ErrorAction SilentlyContinue
+ if ($msiLogTail) {
+ Write-Host "Last 50 lines of MSI log:"
+ Write-Host ($msiLogTail -join "`n")
+ }
+ }
+
+ # Step 4: Verify uninstall completed successfully
+ if ($exitCode -eq 0) {
+ Write-Host "Uninstall completed successfully."
+
+ # Double-check by verifying product code is no longer in registry
+ Start-Sleep -Seconds 2
+ $stillInstalled = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
+ Where-Object { $_.PSChildName -eq $productCode -or $_.UninstallString -like "*$productCode*" }
+
+ if ($stillInstalled) {
+ Write-Host "Warning: Product code still found in registry after uninstall. Uninstall may not have completed fully."
+ } else {
+ Write-Host "Verification: Product code no longer found in registry. Uninstall confirmed."
+ }
+ } else {
+ Write-Host "Uninstall failed with exit code: $exitCode"
+ Write-Host "Common exit codes:"
+ Write-Host " 0 = Success"
+ Write-Host " 1603 = ERROR_INSTALL_FAILURE"
+ Write-Host " 1605 = ERROR_UNKNOWN_PRODUCT (product not found)"
+ Write-Host " 1619 = ERROR_INSTALL_PACKAGE_OPEN_FAILED"
+ }
+
+ Stop-Transcript -ErrorAction SilentlyContinue
+ Exit $exitCode
+
+} catch {
+ Write-Host "ERROR: Exception occurred during uninstall: $($_.Exception.Message)"
+ Write-Host "Stack trace: $($_.ScriptStackTrace)"
+ Stop-Transcript -ErrorAction SilentlyContinue
+ Exit 1
} |
Script Diff Resultsee/maintained-apps/outputs/1password/windows.json=== Install Script (no changes) ===
=== Uninstall // 453051bc -> bb1e89f7 ===
--- /tmp/old.d3PMts 2025-11-21 04:22:53.439493436 +0000
+++ /tmp/new.OnZtKU 2025-11-21 04:22:53.439493436 +0000
@@ -1,13 +1,137 @@
-$product_code = "{7DB53C29-932E-4F28-9AD6-85E66EDE3DB2}"
+# 1Password Uninstall Script
+# This script uninstalls 1Password using the product code with improved reliability
-# Fleet uninstalls app using product code that's extracted on upload
-$process = Start-Process msiexec -ArgumentList @("/quiet", "/x", $product_code, "/norestart") -Wait -PassThru
+$productCode = "{7DB53C29-932E-4F28-9AD6-85E66EDE3DB2}"
+$timeoutSeconds = 300 # 5 minute timeout
+$logFile = "${env:TEMP}\fleet-1password-uninstall.log"
-# Check exit code and output result
-if ($process.ExitCode -eq 0) {
- Write-Output "Exit 0"
- Exit 0
-} else {
- Write-Output "Exit $($process.ExitCode)"
- Exit $process.ExitCode
+try {
+ # Start transcript for logging
+ Start-Transcript -Path $logFile -Append -ErrorAction SilentlyContinue
+
+ Write-Host "Starting 1Password uninstall process..."
+ Write-Host "Product Code: $productCode"
+
+ # Step 1: Close any running 1Password processes
+ Write-Host "Checking for running 1Password processes..."
+ $processes = Get-Process -Name "1Password*" -ErrorAction SilentlyContinue
+ if ($processes) {
+ Write-Host "Found $($processes.Count) running 1Password process(es), attempting to close..."
+ foreach ($proc in $processes) {
+ try {
+ Write-Host "Closing process: $($proc.ProcessName) (PID: $($proc.Id))"
+ Stop-Process -Id $proc.Id -Force -ErrorAction Stop
+ Start-Sleep -Seconds 2
+ } catch {
+ Write-Host "Warning: Could not close process $($proc.Id): $($_.Exception.Message)"
+ }
+ }
+ # Wait a bit more to ensure processes are fully terminated
+ Start-Sleep -Seconds 3
+ } else {
+ Write-Host "No running 1Password processes found."
+ }
+
+ # Step 2: Verify the product is installed before attempting uninstall
+ Write-Host "Verifying 1Password is installed..."
+ $installed = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
+ Where-Object { $_.PSChildName -eq $productCode -or $_.UninstallString -like "*$productCode*" }
+
+ if (-not $installed) {
+ Write-Host "1Password does not appear to be installed (product code not found in registry)."
+ Write-Host "This may indicate it was already uninstalled or installed differently."
+ Exit 0
+ }
+
+ Write-Host "1Password installation confirmed, proceeding with uninstall..."
+
+ # Step 3: Execute uninstall with timeout protection
+ Write-Host "Executing msiexec uninstall command..."
+ $uninstallArgs = @("/quiet", "/x", $productCode, "/norestart", "/L*v", "${env:TEMP}\1password-uninstall-msi.log")
+
+ $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo
+ $processStartInfo.FileName = "msiexec.exe"
+ $processStartInfo.Arguments = ($uninstallArgs -join " ")
+ $processStartInfo.UseShellExecute = $false
+ $processStartInfo.RedirectStandardOutput = $true
+ $processStartInfo.RedirectStandardError = $true
+ $processStartInfo.CreateNoWindow = $true
+
+ $process = New-Object System.Diagnostics.Process
+ $process.StartInfo = $processStartInfo
+
+ Write-Host "Starting uninstall process (timeout: $timeoutSeconds seconds)..."
+ $process.Start() | Out-Null
+
+ # Wait for process with timeout
+ $completed = $process.WaitForExit($timeoutSeconds * 1000)
+
+ if (-not $completed) {
+ Write-Host "ERROR: Uninstall process exceeded timeout of $timeoutSeconds seconds. Terminating..."
+ try {
+ Stop-Process -Id $process.Id -Force -ErrorAction Stop
+ Write-Host "Process terminated."
+ } catch {
+ Write-Host "Warning: Could not terminate process: $($_.Exception.Message)"
+ }
+ Exit 1603 # ERROR_INSTALL_FAILURE
+ }
+
+ $exitCode = $process.ExitCode
+ Write-Host "Uninstall process completed with exit code: $exitCode"
+
+ # Read output if available
+ $stdout = $process.StandardOutput.ReadToEnd()
+ $stderr = $process.StandardError.ReadToEnd()
+
+ if ($stdout) {
+ Write-Host "Standard output: $stdout"
+ }
+ if ($stderr) {
+ Write-Host "Standard error: $stderr"
+ }
+
+ # Check MSI log for additional details
+ $msiLogPath = "${env:TEMP}\1password-uninstall-msi.log"
+ if (Test-Path $msiLogPath) {
+ Write-Host "MSI log file available at: $msiLogPath"
+ # Read last 50 lines of MSI log for troubleshooting
+ $msiLogTail = Get-Content $msiLogPath -Tail 50 -ErrorAction SilentlyContinue
+ if ($msiLogTail) {
+ Write-Host "Last 50 lines of MSI log:"
+ Write-Host ($msiLogTail -join "`n")
+ }
+ }
+
+ # Step 4: Verify uninstall completed successfully
+ if ($exitCode -eq 0) {
+ Write-Host "Uninstall completed successfully."
+
+ # Double-check by verifying product code is no longer in registry
+ Start-Sleep -Seconds 2
+ $stillInstalled = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
+ Where-Object { $_.PSChildName -eq $productCode -or $_.UninstallString -like "*$productCode*" }
+
+ if ($stillInstalled) {
+ Write-Host "Warning: Product code still found in registry after uninstall. Uninstall may not have completed fully."
+ } else {
+ Write-Host "Verification: Product code no longer found in registry. Uninstall confirmed."
+ }
+ } else {
+ Write-Host "Uninstall failed with exit code: $exitCode"
+ Write-Host "Common exit codes:"
+ Write-Host " 0 = Success"
+ Write-Host " 1603 = ERROR_INSTALL_FAILURE"
+ Write-Host " 1605 = ERROR_UNKNOWN_PRODUCT (product not found)"
+ Write-Host " 1619 = ERROR_INSTALL_PACKAGE_OPEN_FAILED"
+ }
+
+ Stop-Transcript -ErrorAction SilentlyContinue
+ Exit $exitCode
+
+} catch {
+ Write-Host "ERROR: Exception occurred during uninstall: $($_.Exception.Message)"
+ Write-Host "Stack trace: $($_.ScriptStackTrace)"
+ Stop-Transcript -ErrorAction SilentlyContinue
+ Exit 1
} |
…ps://github.com/fleetdm/fleet into allenhouchins-update-1password-uninstall-script
Script Diff Resultsee/maintained-apps/outputs/1password/windows.json=== Install Script (no changes) ===
=== Uninstall // bb1e89f7 -> 9a15ecbd ===
--- /tmp/old.UkAzuS 2025-11-21 04:28:08.333061039 +0000
+++ /tmp/new.PZ3xh6 2025-11-21 04:28:08.334061048 +0000
@@ -1,137 +1,29 @@
# 1Password Uninstall Script
-# This script uninstalls 1Password using the product code with improved reliability
+# Closes running processes before uninstalling to prevent hangs
-$productCode = "{7DB53C29-932E-4F28-9AD6-85E66EDE3DB2}"
+$product_code = "{7DB53C29-932E-4F28-9AD6-85E66EDE3DB2}"
$timeoutSeconds = 300 # 5 minute timeout
-$logFile = "${env:TEMP}\fleet-1password-uninstall.log"
-try {
- # Start transcript for logging
- Start-Transcript -Path $logFile -Append -ErrorAction SilentlyContinue
-
- Write-Host "Starting 1Password uninstall process..."
- Write-Host "Product Code: $productCode"
-
- # Step 1: Close any running 1Password processes
- Write-Host "Checking for running 1Password processes..."
- $processes = Get-Process -Name "1Password*" -ErrorAction SilentlyContinue
- if ($processes) {
- Write-Host "Found $($processes.Count) running 1Password process(es), attempting to close..."
- foreach ($proc in $processes) {
- try {
- Write-Host "Closing process: $($proc.ProcessName) (PID: $($proc.Id))"
- Stop-Process -Id $proc.Id -Force -ErrorAction Stop
- Start-Sleep -Seconds 2
- } catch {
- Write-Host "Warning: Could not close process $($proc.Id): $($_.Exception.Message)"
- }
- }
- # Wait a bit more to ensure processes are fully terminated
- Start-Sleep -Seconds 3
- } else {
- Write-Host "No running 1Password processes found."
- }
-
- # Step 2: Verify the product is installed before attempting uninstall
- Write-Host "Verifying 1Password is installed..."
- $installed = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
- Where-Object { $_.PSChildName -eq $productCode -or $_.UninstallString -like "*$productCode*" }
-
- if (-not $installed) {
- Write-Host "1Password does not appear to be installed (product code not found in registry)."
- Write-Host "This may indicate it was already uninstalled or installed differently."
- Exit 0
- }
-
- Write-Host "1Password installation confirmed, proceeding with uninstall..."
-
- # Step 3: Execute uninstall with timeout protection
- Write-Host "Executing msiexec uninstall command..."
- $uninstallArgs = @("/quiet", "/x", $productCode, "/norestart", "/L*v", "${env:TEMP}\1password-uninstall-msi.log")
-
- $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo
- $processStartInfo.FileName = "msiexec.exe"
- $processStartInfo.Arguments = ($uninstallArgs -join " ")
- $processStartInfo.UseShellExecute = $false
- $processStartInfo.RedirectStandardOutput = $true
- $processStartInfo.RedirectStandardError = $true
- $processStartInfo.CreateNoWindow = $true
-
- $process = New-Object System.Diagnostics.Process
- $process.StartInfo = $processStartInfo
-
- Write-Host "Starting uninstall process (timeout: $timeoutSeconds seconds)..."
- $process.Start() | Out-Null
-
- # Wait for process with timeout
- $completed = $process.WaitForExit($timeoutSeconds * 1000)
-
- if (-not $completed) {
- Write-Host "ERROR: Uninstall process exceeded timeout of $timeoutSeconds seconds. Terminating..."
- try {
- Stop-Process -Id $process.Id -Force -ErrorAction Stop
- Write-Host "Process terminated."
- } catch {
- Write-Host "Warning: Could not terminate process: $($_.Exception.Message)"
- }
- Exit 1603 # ERROR_INSTALL_FAILURE
- }
-
- $exitCode = $process.ExitCode
- Write-Host "Uninstall process completed with exit code: $exitCode"
-
- # Read output if available
- $stdout = $process.StandardOutput.ReadToEnd()
- $stderr = $process.StandardError.ReadToEnd()
-
- if ($stdout) {
- Write-Host "Standard output: $stdout"
- }
- if ($stderr) {
- Write-Host "Standard error: $stderr"
- }
-
- # Check MSI log for additional details
- $msiLogPath = "${env:TEMP}\1password-uninstall-msi.log"
- if (Test-Path $msiLogPath) {
- Write-Host "MSI log file available at: $msiLogPath"
- # Read last 50 lines of MSI log for troubleshooting
- $msiLogTail = Get-Content $msiLogPath -Tail 50 -ErrorAction SilentlyContinue
- if ($msiLogTail) {
- Write-Host "Last 50 lines of MSI log:"
- Write-Host ($msiLogTail -join "`n")
- }
- }
-
- # Step 4: Verify uninstall completed successfully
- if ($exitCode -eq 0) {
- Write-Host "Uninstall completed successfully."
-
- # Double-check by verifying product code is no longer in registry
- Start-Sleep -Seconds 2
- $stillInstalled = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
- Where-Object { $_.PSChildName -eq $productCode -or $_.UninstallString -like "*$productCode*" }
-
- if ($stillInstalled) {
- Write-Host "Warning: Product code still found in registry after uninstall. Uninstall may not have completed fully."
- } else {
- Write-Host "Verification: Product code no longer found in registry. Uninstall confirmed."
- }
- } else {
- Write-Host "Uninstall failed with exit code: $exitCode"
- Write-Host "Common exit codes:"
- Write-Host " 0 = Success"
- Write-Host " 1603 = ERROR_INSTALL_FAILURE"
- Write-Host " 1605 = ERROR_UNKNOWN_PRODUCT (product not found)"
- Write-Host " 1619 = ERROR_INSTALL_PACKAGE_OPEN_FAILED"
- }
-
- Stop-Transcript -ErrorAction SilentlyContinue
- Exit $exitCode
-
-} catch {
- Write-Host "ERROR: Exception occurred during uninstall: $($_.Exception.Message)"
- Write-Host "Stack trace: $($_.ScriptStackTrace)"
- Stop-Transcript -ErrorAction SilentlyContinue
- Exit 1
+# Close any running 1Password processes
+Get-Process -Name "1Password*" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
+Start-Sleep -Seconds 2
+
+# Fleet uninstalls app using product code that's extracted on upload
+$process = Start-Process msiexec -ArgumentList @("/quiet", "/x", $product_code, "/norestart") -PassThru
+
+# Wait for process with timeout
+$completed = $process.WaitForExit($timeoutSeconds * 1000)
+
+if (-not $completed) {
+ Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
+ Exit 1603 # ERROR_INSTALL_FAILURE
+}
+
+# Check exit code and output result
+if ($process.ExitCode -eq 0) {
+ Write-Output "Exit 0"
+ Exit 0
+} else {
+ Write-Output "Exit $($process.ExitCode)"
+ Exit $process.ExitCode
} |
|
@tux234 I have ran the test using the updated uninstall script for 1Password many times now with 100% success. I also added a timeout to the template script used for |
Script Diff Resultsee/maintained-apps/outputs/1password/windows.json=== Install Script (no changes) ===
=== Uninstall // bb1e89f7 -> 9a15ecbd ===
--- /tmp/old.ipJLb1 2025-11-21 04:33:22.223045351 +0000
+++ /tmp/new.GHXF70 2025-11-21 04:33:22.223045351 +0000
@@ -1,137 +1,29 @@
# 1Password Uninstall Script
-# This script uninstalls 1Password using the product code with improved reliability
+# Closes running processes before uninstalling to prevent hangs
-$productCode = "{7DB53C29-932E-4F28-9AD6-85E66EDE3DB2}"
+$product_code = "{7DB53C29-932E-4F28-9AD6-85E66EDE3DB2}"
$timeoutSeconds = 300 # 5 minute timeout
-$logFile = "${env:TEMP}\fleet-1password-uninstall.log"
-try {
- # Start transcript for logging
- Start-Transcript -Path $logFile -Append -ErrorAction SilentlyContinue
-
- Write-Host "Starting 1Password uninstall process..."
- Write-Host "Product Code: $productCode"
-
- # Step 1: Close any running 1Password processes
- Write-Host "Checking for running 1Password processes..."
- $processes = Get-Process -Name "1Password*" -ErrorAction SilentlyContinue
- if ($processes) {
- Write-Host "Found $($processes.Count) running 1Password process(es), attempting to close..."
- foreach ($proc in $processes) {
- try {
- Write-Host "Closing process: $($proc.ProcessName) (PID: $($proc.Id))"
- Stop-Process -Id $proc.Id -Force -ErrorAction Stop
- Start-Sleep -Seconds 2
- } catch {
- Write-Host "Warning: Could not close process $($proc.Id): $($_.Exception.Message)"
- }
- }
- # Wait a bit more to ensure processes are fully terminated
- Start-Sleep -Seconds 3
- } else {
- Write-Host "No running 1Password processes found."
- }
-
- # Step 2: Verify the product is installed before attempting uninstall
- Write-Host "Verifying 1Password is installed..."
- $installed = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
- Where-Object { $_.PSChildName -eq $productCode -or $_.UninstallString -like "*$productCode*" }
-
- if (-not $installed) {
- Write-Host "1Password does not appear to be installed (product code not found in registry)."
- Write-Host "This may indicate it was already uninstalled or installed differently."
- Exit 0
- }
-
- Write-Host "1Password installation confirmed, proceeding with uninstall..."
-
- # Step 3: Execute uninstall with timeout protection
- Write-Host "Executing msiexec uninstall command..."
- $uninstallArgs = @("/quiet", "/x", $productCode, "/norestart", "/L*v", "${env:TEMP}\1password-uninstall-msi.log")
-
- $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo
- $processStartInfo.FileName = "msiexec.exe"
- $processStartInfo.Arguments = ($uninstallArgs -join " ")
- $processStartInfo.UseShellExecute = $false
- $processStartInfo.RedirectStandardOutput = $true
- $processStartInfo.RedirectStandardError = $true
- $processStartInfo.CreateNoWindow = $true
-
- $process = New-Object System.Diagnostics.Process
- $process.StartInfo = $processStartInfo
-
- Write-Host "Starting uninstall process (timeout: $timeoutSeconds seconds)..."
- $process.Start() | Out-Null
-
- # Wait for process with timeout
- $completed = $process.WaitForExit($timeoutSeconds * 1000)
-
- if (-not $completed) {
- Write-Host "ERROR: Uninstall process exceeded timeout of $timeoutSeconds seconds. Terminating..."
- try {
- Stop-Process -Id $process.Id -Force -ErrorAction Stop
- Write-Host "Process terminated."
- } catch {
- Write-Host "Warning: Could not terminate process: $($_.Exception.Message)"
- }
- Exit 1603 # ERROR_INSTALL_FAILURE
- }
-
- $exitCode = $process.ExitCode
- Write-Host "Uninstall process completed with exit code: $exitCode"
-
- # Read output if available
- $stdout = $process.StandardOutput.ReadToEnd()
- $stderr = $process.StandardError.ReadToEnd()
-
- if ($stdout) {
- Write-Host "Standard output: $stdout"
- }
- if ($stderr) {
- Write-Host "Standard error: $stderr"
- }
-
- # Check MSI log for additional details
- $msiLogPath = "${env:TEMP}\1password-uninstall-msi.log"
- if (Test-Path $msiLogPath) {
- Write-Host "MSI log file available at: $msiLogPath"
- # Read last 50 lines of MSI log for troubleshooting
- $msiLogTail = Get-Content $msiLogPath -Tail 50 -ErrorAction SilentlyContinue
- if ($msiLogTail) {
- Write-Host "Last 50 lines of MSI log:"
- Write-Host ($msiLogTail -join "`n")
- }
- }
-
- # Step 4: Verify uninstall completed successfully
- if ($exitCode -eq 0) {
- Write-Host "Uninstall completed successfully."
-
- # Double-check by verifying product code is no longer in registry
- Start-Sleep -Seconds 2
- $stillInstalled = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue |
- Where-Object { $_.PSChildName -eq $productCode -or $_.UninstallString -like "*$productCode*" }
-
- if ($stillInstalled) {
- Write-Host "Warning: Product code still found in registry after uninstall. Uninstall may not have completed fully."
- } else {
- Write-Host "Verification: Product code no longer found in registry. Uninstall confirmed."
- }
- } else {
- Write-Host "Uninstall failed with exit code: $exitCode"
- Write-Host "Common exit codes:"
- Write-Host " 0 = Success"
- Write-Host " 1603 = ERROR_INSTALL_FAILURE"
- Write-Host " 1605 = ERROR_UNKNOWN_PRODUCT (product not found)"
- Write-Host " 1619 = ERROR_INSTALL_PACKAGE_OPEN_FAILED"
- }
-
- Stop-Transcript -ErrorAction SilentlyContinue
- Exit $exitCode
-
-} catch {
- Write-Host "ERROR: Exception occurred during uninstall: $($_.Exception.Message)"
- Write-Host "Stack trace: $($_.ScriptStackTrace)"
- Stop-Transcript -ErrorAction SilentlyContinue
- Exit 1
+# Close any running 1Password processes
+Get-Process -Name "1Password*" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
+Start-Sleep -Seconds 2
+
+# Fleet uninstalls app using product code that's extracted on upload
+$process = Start-Process msiexec -ArgumentList @("/quiet", "/x", $product_code, "/norestart") -PassThru
+
+# Wait for process with timeout
+$completed = $process.WaitForExit($timeoutSeconds * 1000)
+
+if (-not $completed) {
+ Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
+ Exit 1603 # ERROR_INSTALL_FAILURE
+}
+
+# Check exit code and output result
+if ($process.ExitCode -eq 0) {
+ Write-Output "Exit 0"
+ Exit 0
+} else {
+ Write-Output "Exit $($process.ExitCode)"
+ Exit $process.ExitCode
} |
tux234
left a comment
There was a problem hiding this comment.
Thanks for taking a look. I thought it might be a timeout issue. Do you think we should update the validate/main.go file to include this timeout, or should we keep it at the app level?
My thought was to put a wait in the go file itself, so it doesn't try to validate before the uninstaller has had to uninstall, and it would apply to all apps, not just ones like 1Password.
But this change is great, and solves the immediate need!
@tux234 |
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** For #36111 The updates in #36111 caused some tests to start failing. I ran the suggested script: ``` go test ./pkg/file/... -update ``` and verified that the changes match the changes in the above PR [here](https://github.com/fleetdm/fleet/pull/36111/files#diff-09e225a2a28fbf997ddf571274119a20d9210539e5bdd49749beb2226e6de5aa).
Added a new PowerShell uninstall script for 1Password with process termination, registry checks, logging, and timeout handling. Updated the template uninstall script by adding timeout protection to generic MSI uninstall scripts to prevent hangs. These changes improve reliability and diagnostics for uninstall operations.
Related issue: Resolves #
Checklist for submitter
If some of the following don't apply, delete the relevant line.
Changes file added for user-visible changes in
changes/,orbit/changes/oree/fleetd-chrome/changes.See Changes files for more information.
Input data is properly validated,
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements)If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes
Testing
Added/updated automated tests
Where appropriate, automated tests simulate multiple hosts and test for host isolation (updates to one hosts's records do not affect another)
QA'd all new/changed functionality manually
For unreleased bug fixes in a release candidate, one of:
Database migrations
COLLATE utf8mb4_unicode_ci).New Fleet configuration settings
If you didn't check the box above, follow this checklist for GitOps-enabled settings:
fleetctl generate-gitopsfleetd/orbit/Fleet Desktop
runtime.GOOSis used as needed to isolate changes