Public/Invoke-TechCare.ps1
|
function Invoke-TechCare { <# .SYNOPSIS Orquesta una pasada de mantenimiento completa sobre el equipo local. .DESCRIPTION Ejecuta los modulos del preset indicado en un orden fijo: diagnosticar, reparar, actualizar, limpiar, rendimiento, seguridad e informe. Un modulo que falla queda registrado en rojo y la pasada continua, porque dejar el equipo a medias es peor que terminar con un apartado incompleto. VISTA PREVIA POR DEFECTO. Sin -Apply cada modulo se ejecuta bajo -WhatIf, asi que el equipo no se modifica: lo que obtienes es el diagnostico, la lista de lo que se haria y el informe. Con -Apply se exige sesion elevada, se crea un punto de restauracion y se avisa si el equipo esta gestionado. Nunca reinicia, nunca borra aplicaciones integradas y nunca desactiva Defender ni Windows Update. .PARAMETER MaintenanceProfile Preset a ejecutar: Basic, Standard o Full. Por defecto Standard. Acepta los alias -Preset y -Profile. No se llama -Profile a secas porque $Profile es una variable automatica de PowerShell y declararla como parametro es un defecto conocido. .PARAMETER Modules Modulos concretos, en cualquier orden: TechCare los reordena al orden canonico. Excluyente con -MaintenanceProfile por conjunto de parametros. .PARAMETER Apply Aplica los cambios. Sin este modificador nada se modifica. .PARAMETER Force No pregunta en equipos gestionados ni cuando falla el punto de restauracion. Necesario para una ejecucion desatendida. .PARAMETER ReportPath Carpeta donde se escriben el informe y el log. .PARAMETER TechnicianName Nombre del tecnico para la cabecera del informe. .PARAMETER CompanyName Nombre de la empresa para la cabecera del informe. .PARAMETER ContactInfo Contacto para la cabecera del informe. .PARAMETER PowerPlan Plan de energia a activar durante el modulo Performance. .PARAMETER SkipRestorePoint No crea punto de restauracion antes de aplicar. Usalo solo si el equipo tiene otra copia de seguridad. .EXAMPLE Invoke-TechCare Vista previa con el preset Standard: no modifica nada y deja informe. .EXAMPLE Invoke-TechCare -MaintenanceProfile Standard -Apply Pasada real. Exige consola elevada y crea punto de restauracion. .EXAMPLE Invoke-TechCare -Modules Repair,Cleanup -Apply -Force Solo reparar y limpiar, sin preguntas, para una intervencion desatendida. .EXAMPLE Invoke-TechCare -MaintenanceProfile Full -Apply -ReportPath C:\Reportes -TechnicianName 'Eduard Arbona' Todo, con informe de marca en una carpeta concreta. .EXAMPLE Invoke-TechCare -MaintenanceProfile Full -WhatIf No escribe ni el informe: -WhatIf se propaga a todos los modulos. .OUTPUTS PSCustomObject[] .LINK https://github.com/earbona23/TechCare #> [CmdletBinding(DefaultParameterSetName = 'Preset', SupportsShouldProcess, ConfirmImpact = 'Medium')] [OutputType([pscustomobject[]])] param( [Parameter(ParameterSetName = 'Preset', Position = 0)] [Alias('Preset', 'Profile')] [ValidateSet('Basic', 'Standard', 'Full')] [string]$MaintenanceProfile, [Parameter(ParameterSetName = 'Modules', Mandatory)] [ValidateSet('Diagnose', 'Repair', 'Update', 'Cleanup', 'Performance', 'Security', 'Report')] [string[]]$Modules, [switch]$Apply, [switch]$Force, [ValidateNotNullOrEmpty()] [string]$ReportPath, [AllowEmptyString()] [string]$TechnicianName = '', [AllowEmptyString()] [string]$CompanyName = '', [AllowEmptyString()] [string]$ContactInfo = '', [ValidateSet('Balanced', 'HighPerformance', 'PowerSaver')] [string]$PowerPlan, [switch]$SkipRestorePoint ) if (-not (Test-TechCarePlatform)) { throw $script:PlatformMessage } # El estado se reinicia aqui para que dos pasadas en la misma consola no # acumulen los resultados de la primera en el informe de la segunda. Initialize-TechCareState $preset = $MaintenanceProfile if ($PSCmdlet.ParameterSetName -eq 'Modules') { $selected = @($Modules) $preset = 'Custom' } else { if (-not $preset) { $preset = 'Standard' } $selected = @(Get-TechCareProfile -MaintenanceProfile $preset) } $order = @('Diagnose', 'Repair', 'Update', 'Cleanup', 'Performance', 'Security', 'Report') $selected = @($selected | Sort-Object -Property { $order.IndexOf($_) }) $script:State.Preset = $preset $script:State.Apply = [bool]$Apply $prereq = Test-TechCarePrerequisite $script:State.Admin = $prereq.IsAdmin $script:State.Managed = $prereq.IsManaged $script:State.ManagedBy = $prereq.ManagedReasons if ($Apply -and -not $prereq.IsAdmin) { throw ("Se requieren privilegios de administrador para aplicar cambios. Relanza con:`n " + $prereq.RelaunchCommand) } if (-not $prereq.IsSupportedWindows) { Write-TechCareLog -Level Warning -Message ('Version de Windows no reconocida como 10 u 11 (build {0}). Los modulos pueden comportarse distinto.' -f $prereq.OSBuild) } # Carpeta de la pasada: el log se escribe linea a linea desde el principio, # de modo que una sesion interrumpida a mitad de DISM deje rastro en disco. $runFolder = $ReportPath if (-not $runFolder) { $runFolder = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ('TechCare\' + $script:State.RunId) } $script:State.ReportPath = $runFolder if (-not $WhatIfPreference) { try { if (-not (Test-Path -LiteralPath $runFolder)) { [void](New-Item -ItemType Directory -Path $runFolder -Force) } $script:State.LogPath = Join-Path -Path $runFolder -ChildPath 'TechCare-Log.txt' } catch { Write-TechCareLog -Level Warning -Message 'No se pudo preparar la carpeta de la pasada; el log solo quedara en memoria.' -ErrorText $_.Exception.Message } } # Preview es la decision central del modulo: si es cierto, TODOS los modulos # que escriben se invocan con -WhatIf y el equipo no se toca. Sin esto, una # pasada sin -Apply ejecutaria DISM y borraria los temporales de verdad. $preview = -not $Apply if ($Apply) { $actionText = 'Ejecutar TechCare y APLICAR cambios - preset {0}' -f $preset if (-not $PSCmdlet.ShouldProcess($prereq.OSCaption + ' / ' + [System.Environment]::MachineName, $actionText)) { $preview = $true } } if ($preview) { Write-TechCareLog -Message 'Modo VISTA PREVIA: no se modificara nada. Anade -Apply para aplicar.' -Level Warning } else { Write-TechCareLog -Message ('Modo APLICAR: preset {0}, ejecucion {1}.' -f $preset, $script:State.RunId) -Level Info } if ($prereq.IsManaged -and -not $preview) { Write-TechCareLog -Level Warning -Message 'Equipo gestionado centralmente:' foreach ($reason in $prereq.ManagedReasons) { Write-TechCareLog -Level Warning -Message (' - ' + $reason) } Write-TechCareLog -Level Warning -Message 'Los cambios pueden chocar con politicas corporativas y volver a aplicarse solos.' if (-not $Force) { # Test-TechCareConsent, no ShouldContinue a secas: en una sesion sin # entrada interactiva ShouldContinue lanza, y la excepcion mataria la # pasada con un error que no dice que falta -Force. if (-not (Test-TechCareConsent -Question 'Continuar en un equipo gestionado?' -Caller $PSCmdlet)) { Write-TechCareLog -Message 'Pasada cancelada por el tecnico.' -Level Warning return [pscustomobject[]]$script:State.Results.ToArray() } } } if (-not $preview -and -not $SkipRestorePoint) { $restore = New-TechCareRestorePoint -Description ('TechCare ' + $script:State.RunId) -Confirm:$false if ($restore.Created) { Write-TechCareLog -Level Success -Message ('Punto de restauracion creado via {0}.' -f $restore.Method) } else { Write-TechCareLog -Level Warning -Message 'No se pudo crear el punto de restauracion.' -ErrorText $restore.Message if (-not $Force) { if (-not (Test-TechCareConsent -Question 'Continuar sin punto de restauracion?' -Caller $PSCmdlet)) { Write-TechCareLog -Message 'Pasada cancelada por el tecnico.' -Level Warning return [pscustomobject[]]$script:State.Results.ToArray() } } } } elseif (-not $preview -and $SkipRestorePoint) { Write-TechCareLog -Level Warning -Message 'Punto de restauracion omitido por -SkipRestorePoint.' } $total = $selected.Count $index = 0 foreach ($moduleName in $selected) { $index++ $percent = [math]::Round((($index - 1) / $total) * 100) Write-Progress -Activity 'TechCare' -Status ('Modulo {0} de {1}: {2}' -f $index, $total, $moduleName) -PercentComplete $percent Write-TechCareLog -Message ('=== Modulo {0} ===' -f $moduleName) -Level Detail try { switch ($moduleName) { 'Diagnose' { [void](Get-TechCareDiagnostics) } 'Repair' { [void](Invoke-TechCareRepair -Confirm:$false -WhatIf:$preview) } 'Update' { [void](Invoke-TechCareUpdate -Confirm:$false -WhatIf:$preview) } 'Cleanup' { [void](Invoke-TechCareCleanup -Confirm:$false -WhatIf:$preview) } 'Performance' { # -PowerPlan se anade solo si el tecnico lo indico: pasarlo # vacio viola el ValidateSet y tumbaria el modulo entero. $perfArgs = @{ Confirm = $false; WhatIf = $preview } if ($PowerPlan) { $perfArgs['PowerPlan'] = $PowerPlan } [void](Get-TechCarePerformance @perfArgs) } 'Security' { [void](Get-TechCareSecurity -Apply:$Apply -Confirm:$false -WhatIf:$preview) } 'Report' { # El informe se escribe tambien en vista previa: es el # entregable de la revision. Solo -WhatIf explicito lo frena. $rep = New-TechCareReport -ReportPath $runFolder -TechnicianName $TechnicianName -CompanyName $CompanyName -ContactInfo $ContactInfo -Confirm:$false if ($rep.Written) { Write-TechCareLog -Level Success -Message ('Informe: ' + $rep.HtmlPath) } else { Write-TechCareLog -Level Warning -Message '-WhatIf activo: no se escribio el informe.' } } } } catch { Write-TechCareLog -Level Error -Message ('Fallo en el modulo {0}.' -f $moduleName) -ErrorText $_.Exception.Message [void](Add-TechCareResult -Module $moduleName -Status 'Red' -Findings @('Modulo interrumpido: ' + $_.Exception.Message)) } } Write-Progress -Activity 'TechCare' -Status 'Finalizado' -Completed return [pscustomobject[]]$script:State.Results.ToArray() } |