Public/Invoke-TechCareUpdate.ps1
|
function Invoke-TechCareUpdate { <# .SYNOPSIS Instala actualizaciones de Windows (incluidos drivers) y de winget. .DESCRIPTION Usa la API COM de Windows Update, que viene en el sistema, para no imponer ninguna dependencia obligatoria. Si PSWindowsUpdate esta presente lo avisa como alternativa mas rica, pero no lo requiere. Despues ejecuta winget upgrade si winget existe. Nunca reinicia el equipo: si una actualizacion lo pide, queda como recomendacion en el informe. .PARAMETER SkipWindowsUpdate Omite Windows Update y pasa directamente a winget. .PARAMETER SkipWinget Omite winget y solo aplica Windows Update. .EXAMPLE Invoke-TechCareUpdate -WhatIf Muestra que se buscaria e instalaria, sin descargar nada. .EXAMPLE Invoke-TechCareUpdate -SkipWinget -Confirm:$false Solo Windows Update, sin tocar las aplicaciones de terceros. .EXAMPLE Invoke-TechCareUpdate -SkipWindowsUpdate -Confirm:$false Solo actualiza aplicaciones con winget, util cuando WSUS controla el SO. .OUTPUTS PSCustomObject .LINK https://github.com/earbona23/TechCare #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [OutputType([pscustomobject])] param( [switch]$SkipWindowsUpdate, [switch]$SkipWinget ) if (-not (Test-TechCarePlatform)) { throw $script:PlatformMessage } $findings = New-Object -TypeName System.Collections.ArrayList $actions = New-Object -TypeName System.Collections.ArrayList $recs = New-Object -TypeName System.Collections.ArrayList $status = 'Green' $mod = 'Update' if (-not (Test-TechCareAdmin)) { [void]$findings.Add('Sin privilegios de administrador: Update omitido.') [void]$recs.Add('Relanzar como administrador para aplicar actualizaciones.') Write-TechCareLog -ModuleName $mod -Level Warning -Message 'Update omitido por falta de privilegios.' return Add-TechCareResult -Module $mod -Status 'Gray' -Findings $findings.ToArray() -Recommendations $recs.ToArray() } if (-not $SkipWindowsUpdate) { if ($null -eq (Get-Command -Name 'Get-WindowsUpdate' -ErrorAction SilentlyContinue)) { [void]$recs.Add('Opcional: Install-Module PSWindowsUpdate da un flujo mas rico. TechCare no lo necesita.') } if ($PSCmdlet.ShouldProcess('Windows Update', 'Buscar e instalar actualizaciones de software y drivers')) { Write-Progress -Activity 'TechCare' -Status 'Buscando actualizaciones' -PercentComplete 25 try { $svc = Get-Service -Name 'wuauserv' -ErrorAction SilentlyContinue if ($null -ne $svc -and $svc.Status -ne 'Running') { # El servicio puede estar parado por una limpieza anterior; sin # el, la busqueda COM falla con un error poco descriptivo. Start-Service -Name 'wuauserv' -ErrorAction SilentlyContinue } $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $collection = New-Object -TypeName System.Collections.ArrayList # Software y Driver van en dos busquedas porque el criterio Type no # admite una disyuncion en la misma consulta. foreach ($criteria in @("IsInstalled=0 and IsHidden=0 and Type='Software'", "IsInstalled=0 and IsHidden=0 and Type='Driver'")) { $found = $searcher.Search($criteria) foreach ($u in $found.Updates) { [void]$collection.Add($u) } } [void]$findings.Add(('Actualizaciones disponibles: {0}' -f $collection.Count)) foreach ($u in $collection) { [void]$findings.Add(' - ' + $u.Title) } if ($collection.Count -gt 0) { $toInstall = New-Object -ComObject Microsoft.Update.UpdateColl foreach ($u in $collection) { try { if (-not $u.EulaAccepted) { $u.AcceptEula() } [void]$toInstall.Add($u) } catch { $status = 'Yellow' [void]$findings.Add(('No se pudo preparar "{0}": {1}' -f $u.Title, $_.Exception.Message)) } } if ($toInstall.Count -gt 0) { Write-Progress -Activity 'TechCare' -Status 'Descargando actualizaciones' -PercentComplete 45 $downloader = $session.CreateUpdateDownloader() $downloader.Updates = $toInstall [void]$downloader.Download() Write-Progress -Activity 'TechCare' -Status 'Instalando actualizaciones' -PercentComplete 65 $installer = $session.CreateUpdateInstaller() $installer.Updates = $toInstall $installer.IsForced = $false $installResult = $installer.Install() for ($i = 0; $i -lt $toInstall.Count; $i++) { $ur = $installResult.GetUpdateResult($i) # ResultCode 2 es orcSucceeded; 3 es succeeded with errors. if ($ur.ResultCode -eq 2) { [void]$actions.Add('Instalada: ' + $toInstall.Item($i).Title) } else { $status = 'Yellow' [void]$findings.Add(('Fallo (ResultCode {0}): {1}' -f $ur.ResultCode, $toInstall.Item($i).Title)) } } if ($installResult.RebootRequired) { [void]$recs.Add('Windows Update requiere reinicio. TechCare no reinicia: hazlo en ventana acordada.') } } } } catch { $status = 'Yellow' [void]$findings.Add('Error consultando o aplicando Windows Update: ' + $_.Exception.Message) Write-TechCareLog -ModuleName $mod -Level Error -Message 'Fallo en Windows Update.' -ErrorText $_.Exception.Message } } } if (-not $SkipWinget) { $winget = Get-Command -Name 'winget' -ErrorAction SilentlyContinue if ($null -eq $winget) { [void]$findings.Add('winget no esta disponible en este equipo.') [void]$recs.Add('Instalar App Installer desde Microsoft Store para habilitar winget.') } elseif ($PSCmdlet.ShouldProcess('Aplicaciones de winget', 'winget upgrade --all --silent')) { Write-Progress -Activity 'TechCare' -Status 'winget upgrade' -PercentComplete 85 $r = Invoke-TechCareProcess -FilePath $winget.Source -ArgumentList @( 'upgrade', '--all', '--silent', '--accept-package-agreements', '--accept-source-agreements', '--disable-interactivity' ) if ($r.ExitCode -eq 0) { [void]$actions.Add('winget upgrade --all completado.') } else { $status = 'Yellow' [void]$findings.Add(('winget devolvio codigo {0}; revisa el log para el paquete concreto.' -f $r.ExitCode)) } Write-TechCareLog -ModuleName $mod -Level Detail -Message 'Salida de winget registrada.' -ErrorText $r.Output } } Write-Progress -Activity 'TechCare' -Status 'Actualizaciones completadas' -Completed return Add-TechCareResult -Module $mod -Status $status -Findings $findings.ToArray() -Actions $actions.ToArray() -Recommendations $recs.ToArray() } |