Skip to content

Improve 1Password uninstall reliability and add timeout#36111

Merged
allenhouchins merged 5 commits intomainfrom
allenhouchins-update-1password-uninstall-script
Nov 21, 2025
Merged

Improve 1Password uninstall reliability and add timeout#36111
allenhouchins merged 5 commits intomainfrom
allenhouchins-update-1password-uninstall-script

Conversation

@allenhouchins
Copy link
Copy Markdown
Member

@allenhouchins allenhouchins commented Nov 21, 2025

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/ or ee/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

For unreleased bug fixes in a release candidate, one of:

  • Confirmed that the fix is not expected to adversely impact load test results
  • Alerted the release DRI if additional load testing is needed

Database migrations

  • Checked schema for all modified table for columns that will auto-update timestamps during migration.
  • Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects.
  • Ensured the correct collation is explicitly set for character columns (COLLATE utf8mb4_unicode_ci).

New Fleet configuration settings

  • Setting(s) is/are explicitly excluded from GitOps

If you didn't check the box above, follow this checklist for GitOps-enabled settings:

  • Verified that the setting is exported via fleetctl generate-gitops
  • Verified the setting is documented in a separate PR to the GitOps documentation
  • Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional)
  • Verified that any relevant UI is disabled when GitOps mode is enabled

fleetd/orbit/Fleet Desktop

  • Verified compatibility with the latest released version of Fleet (see Must rule)
  • If the change applies to only one platform, confirmed that runtime.GOOS is used as needed to isolate changes
  • Verified that fleetd runs on macOS, Linux and Windows
  • Verified auto-update works from the released version of component to the new version (see tools/tuf/test)

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.
@github-actions
Copy link
Copy Markdown
Contributor

Script Diff Results

ee/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
 }

@github-actions
Copy link
Copy Markdown
Contributor

Script Diff Results

ee/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
 }

@github-actions
Copy link
Copy Markdown
Contributor

Script Diff Results

ee/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
 }

@allenhouchins allenhouchins requested a review from tux234 November 21, 2025 04:30
@allenhouchins
Copy link
Copy Markdown
Member Author

@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 msi packages so it won't hang indefinitely and we can capture errors more quickly.

@github-actions
Copy link
Copy Markdown
Contributor

Script Diff Results

ee/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
 }

Copy link
Copy Markdown
Contributor

@tux234 tux234 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

@allenhouchins
Copy link
Copy Markdown
Member Author

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 main.go pulls the scripts in from pkg/file/scripts/uninstall_msi.ps1 or pkg/file/scripts/uninstall_msi_with_upgrade_code.ps1 so with this PR, the timeout is baked in for all uninstall scripts going forward.

@allenhouchins allenhouchins marked this pull request as ready for review November 21, 2025 04:43
@allenhouchins allenhouchins merged commit 895be7b into main Nov 21, 2025
6 of 7 checks passed
@allenhouchins allenhouchins deleted the allenhouchins-update-1password-uninstall-script branch November 21, 2025 04:45
iansltx pushed a commit that referenced this pull request Nov 21, 2025
<!-- 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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants