Private/Core/Install-ApplicationUpdate.ps1
|
function Install-ApplicationUpdate { <# .SYNOPSIS Installs an application update using winget .DESCRIPTION Handles the actual installation of an application update with pre/post scripts .PARAMETER AppId The winget app ID to update .PARAMETER AppConfig Optional managed application configuration .PARAMETER Force Force installation even if conflicting processes are running #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AppId, [Parameter()] [ManagedApplication]$AppConfig, [Parameter()] [switch]$Force ) $result = [InstallationResult]::new() $result.AppId = $AppId $result.Status = [InstallationStatus]::InProgress $startTime = Get-Date try { Write-PatchLog "Starting update for $AppId" -Type Info # Get app name $pkg = Get-WinGetPackage -Id $AppId -ErrorAction SilentlyContinue if ($pkg) { $result.AppName = $pkg.Name } else { $result.AppName = $AppId } # Check for conflicting processes if ($AppConfig -and $AppConfig.ConflictingProcesses.Count -gt 0) { $running = Test-ConflictingProcess -ProcessNames $AppConfig.ConflictingProcesses if ($running -and -not $Force) { $result.Status = [InstallationStatus]::Deferred $result.Message = "Conflicting processes running: $($AppConfig.ConflictingProcesses -join ', ')" $result.ExitCode = 1602 # User cancelled / deferred Write-PatchLog $result.Message -Type Warning return $result } elseif ($running -and $Force) { # Force close conflicting processes Write-PatchLog "Force closing conflicting processes for $AppId" -Type Warning foreach ($procName in $AppConfig.ConflictingProcesses) { $procNameClean = $procName -replace '\.exe$', '' Get-Process -Name $procNameClean -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue } Start-Sleep -Seconds 2 } } # Run pre-installation script if configured if ($AppConfig -and $AppConfig.PreScript) { Write-PatchLog "Running pre-install script for $AppId" -Type Info try { $scriptBlock = [ScriptBlock]::Create($AppConfig.PreScript) Invoke-Command -ScriptBlock $scriptBlock -ErrorAction Stop } catch { Write-PatchLog "Pre-install script failed: $_" -Type Warning } } # Build update parameters $updateParams = @{ Id = $AppId Mode = 'Silent' } if ($AppConfig -and $AppConfig.InstallArguments) { $updateParams['Override'] = $AppConfig.InstallArguments } # Perform the update Write-PatchLog "Installing update for $AppId" -Type Info $updateResult = Update-WinGetPackage @updateParams -ErrorAction Stop # Check result if ($updateResult.Status -eq 'Ok' -or $updateResult.RebootRequired) { $result.Status = [InstallationStatus]::Success $result.ExitCode = 0 $result.Message = "Successfully updated $($result.AppName)" if ($updateResult.RebootRequired) { $result.RebootRequired = $true $result.Message += " (reboot required)" } Write-PatchLog $result.Message -Type Info } else { $result.Status = [InstallationStatus]::Failed $result.ExitCode = 1 $result.Message = "Update failed for $($result.AppName): $($updateResult.Status)" Write-PatchLog $result.Message -Type Error } # Run post-installation script if configured if ($AppConfig -and $AppConfig.PostScript -and $result.Status -eq [InstallationStatus]::Success) { Write-PatchLog "Running post-install script for $AppId" -Type Info try { $scriptBlock = [ScriptBlock]::Create($AppConfig.PostScript) Invoke-Command -ScriptBlock $scriptBlock -ErrorAction Stop } catch { Write-PatchLog "Post-install script failed: $_" -Type Warning } } # Check if app config indicates reboot required if ($AppConfig -and $AppConfig.RequiresReboot) { $result.RebootRequired = $true } } catch { $result.Status = [InstallationStatus]::Failed $result.ExitCode = 1 $result.Message = "Update failed for $AppId : $_" Write-PatchLog $result.Message -Type Error } finally { $result.Duration = (Get-Date) - $startTime } return $result } function Install-MissingApplication { <# .SYNOPSIS Installs a missing application using winget .DESCRIPTION Handles the initial installation of an application that is not yet installed .PARAMETER AppId The winget app ID to install .PARAMETER AppConfig Optional managed application configuration .PARAMETER Force Force installation even if conflicting processes are running #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$AppId, [Parameter()] [ManagedApplication]$AppConfig, [Parameter()] [switch]$Force ) $result = [InstallationResult]::new() $result.AppId = $AppId $result.AppName = if ($AppConfig) { $AppConfig.Name } else { $AppId } $result.Status = [InstallationStatus]::InProgress $startTime = Get-Date try { Write-PatchLog "Starting initial installation for $AppId" -Type Info # Run pre-installation script if configured if ($AppConfig -and $AppConfig.PreScript) { Write-PatchLog "Running pre-install script for $AppId" -Type Info try { $scriptBlock = [ScriptBlock]::Create($AppConfig.PreScript) Invoke-Command -ScriptBlock $scriptBlock -ErrorAction Stop } catch { Write-PatchLog "Pre-install script failed: $_" -Type Warning } } # Build install parameters $installParams = @{ Id = $AppId Mode = 'Silent' } # Handle version pinning for exact mode - install specific version if ($AppConfig -and $AppConfig.VersionPinMode -eq 'exact' -and $AppConfig.PinnedVersion) { $installParams['Version'] = $AppConfig.PinnedVersion Write-PatchLog "Installing pinned version $($AppConfig.PinnedVersion) for $AppId" -Type Info } # Handle max version pinning - install up to max version if ($AppConfig -and $AppConfig.VersionPinMode -eq 'max' -and $AppConfig.PinnedVersion) { # For max pin, we install the latest version <= pinned version # WinGet will install latest by default, but we can specify --version to limit $installParams['Version'] = $AppConfig.PinnedVersion Write-PatchLog "Installing max version $($AppConfig.PinnedVersion) for $AppId" -Type Info } if ($AppConfig -and $AppConfig.InstallArguments) { $installParams['Override'] = $AppConfig.InstallArguments } # Perform the installation Write-PatchLog "Installing $AppId" -Type Info $installResult = Install-WinGetPackage @installParams -ErrorAction Stop # Check result if ($installResult.Status -eq 'Ok' -or $installResult.RebootRequired) { $result.Status = [InstallationStatus]::Success $result.ExitCode = 0 $result.Message = "Successfully installed $($result.AppName)" if ($installResult.RebootRequired) { $result.RebootRequired = $true $result.Message += " (reboot required)" } Write-PatchLog $result.Message -Type Info } else { $result.Status = [InstallationStatus]::Failed $result.ExitCode = 1 $result.Message = "Installation failed for $($result.AppName): $($installResult.Status)" Write-PatchLog $result.Message -Type Error } # Run post-installation script if configured if ($AppConfig -and $AppConfig.PostScript -and $result.Status -eq [InstallationStatus]::Success) { Write-PatchLog "Running post-install script for $AppId" -Type Info try { $scriptBlock = [ScriptBlock]::Create($AppConfig.PostScript) Invoke-Command -ScriptBlock $scriptBlock -ErrorAction Stop } catch { Write-PatchLog "Post-install script failed: $_" -Type Warning } } # Check if app config indicates reboot required if ($AppConfig -and $AppConfig.RequiresReboot) { $result.RebootRequired = $true } } catch { $result.Status = [InstallationStatus]::Failed $result.ExitCode = 1 $result.Message = "Installation failed for $AppId : $_" Write-PatchLog $result.Message -Type Error } finally { $result.Duration = (Get-Date) - $startTime } return $result } |