Windows Server 2025 / Cómo manejar errores de PowerShell y registrar cada ejecución

Para detectar fallos y registrar el resultado de cada ejecución de PowerShell, combine try, catch y finally con un manejo explícito de errores y un archivo de registro por ejecución. Que un comando termine no demuestra que toda la tarea administrativa haya tenido éxito; el registro debe permitir reconocer resultados parciales.

Haga visibles los fallos

Este ejercicio lee un archivo y escribe un registro sin cambiar roles, servicios ni red. En un script real de instalación o administración de cuentas también hay que registrar qué cambios quedaron tras un fallo intermedio. Entrar en catch no revierte automáticamente las acciones anteriores.

Algunos errores permiten que PowerShell continúe y, de forma predeterminada, no activan catch. Estar dentro de try no basta: -ErrorAction Stop convierte los errores no terminantes compatibles en errores terminantes que se pueden capturar. La documentación sobre manejo de errores de PowerShell explica la distinción.

Prepare la entrada y el script completo

Cree primero C:\LabScripts y guarde en C:\LabScripts\input.txt una única línea con el texto literal lab ready. Guarde todo el código siguiente como C:\LabScripts\Read-LabFile.ps1, comprobando las extensiones .txt y .ps1. Use UTF-8; si añade caracteres no ASCII al script, seleccione UTF-8 con marca de orden de bytes (BOM), según la guía de codificación. Ejecútelo con Windows PowerShell 5.1 y permisos para leer la entrada y crear o escribir C:\LabLogs. El script guarda un registro JSON (JavaScript Object Notation), un formato de datos estructurados, con un nombre basado en fecha, hora e identificador corto.

param(
    [Parameter(Mandatory=$true)]
    [string]$InputPath
)
$logDirectory = 'C:\LabLogs'
New-Item -ItemType Directory -Path $logDirectory -Force -ErrorAction Stop | Out-Null
$runLabel = (Get-Date -Format 'yyyyMMdd-HHmmss') + '-' + ([guid]::NewGuid().ToString('N').Substring(0,6))
$logPath = Join-Path $logDirectory ($runLabel + '.json')
$result = [ordered]@{
    StartedAt = (Get-Date).ToString('o')
    ComputerName = $env:COMPUTERNAME
    InputPath = $InputPath
    Success = $false
    LineCount = $null
    ErrorId = $null
    ErrorMessage = $null
}
$exitStatus = 0
try {
    $lines = @(Get-Content -LiteralPath $InputPath -ErrorAction Stop)
    $result.LineCount = $lines.Count
    $result.Success = $true
}
catch {
    $result.ErrorId = $_.FullyQualifiedErrorId
    $result.ErrorMessage = $_.Exception.Message
    $exitStatus = 1
    Write-Warning $result.ErrorMessage
}
finally {
    $result.FinishedAt = (Get-Date).ToString('o')
    $result | ConvertTo-Json | Set-Content -LiteralPath $logPath -Encoding UTF8 -ErrorAction Stop
}
Write-Output $logPath
exit $exitStatus

Compare el éxito y el fallo en las mismas condiciones

Ejecute las líneas desde una consola de PowerShell: cada powershell.exe inicia un proceso secundario, de modo que exit termina ese proceso y permite seguir trabajando en la consola original. Primero ejecute la invocación del archivo existente y consulte inmediatamente su $LASTEXITCODE; después repita el par con missing-input.txt, que debe estar realmente ausente. No elimine la entrada válida ni modifique un archivo del sistema para provocar el fallo:

powershell.exe -NoProfile -File 'C:\LabScripts\Read-LabFile.ps1' -InputPath 'C:\LabScripts\input.txt'
$LASTEXITCODE
powershell.exe -NoProfile -File 'C:\LabScripts\Read-LabFile.ps1' -InputPath 'C:\LabScripts\missing-input.txt'
$LASTEXITCODE

Con una entrada de una sola línea, la primera ejecución debe devolver 0 y generar un JSON con Success=true y LineCount=1, sin detalles de error. La entrada inexistente debe devolver 1, Success=false y LineCount=null, con ErrorId y ErrorMessage informados; el mensaje puede variar según el idioma. Abra con un editor de texto el archivo de la ruta impresa por cada ejecución y compruebe InputPath, ComputerName, StartedAt y FinishedAt. Compare ambos registros, sin confundirlos con un archivo antiguo de la carpeta.

Si una directiva impide ejecutar el archivo, cumpla los requisitos de ejecución y firma de la organización. No debilite la configuración global para este ejemplo.

Comprenda cada sección del manejo de errores

Sección Propósito en este script
try Leer el archivo y registrar su número de líneas
catch Registrar el identificador y el mensaje del error capturado; elegir un código de salida de fallo
finally Intentar guardar el resultado tanto si la lectura funciona como si falla

La referencia de try, catch y finally de Microsoft explica el manejo de errores terminantes. En este script, crear C:\LabLogs ocurre antes de try: si falla esa preparación, su catch no la captura. finally intenta guardar el resultado, pero no garantiza que haya espacio o permiso de escritura. Success refleja la lectura; confirme también que se haya creado un JSON válido. La escritura del registro usa -ErrorAction Stop. Revise cualquier fallo al guardarlo; la ausencia del archivo no demuestra que la tarea se haya completado sin errores.

Los programas externos devuelven sus propios códigos de salida. En Windows PowerShell 5.1, un código distinto de cero no activa catch automáticamente. Consulte $LASTEXITCODE justo después del programa y use throw para generar un error terminante cuando corresponda. Otro programa externo puede reemplazar ese valor; -ErrorAction Stop no convierte por sí solo los códigos de salida externos en excepciones.

Defina criterios para repetir tareas con seguridad

  • Registre el servidor, el destino y las horas de inicio y fin.
  • Consulte el estado existente para evitar creaciones duplicadas o sobrescrituras no deseadas.
  • Anote por separado los pasos completados y los que fallaron.
  • Mantenga contraseñas, tokens y contenido innecesario de archivos de usuario fuera de los registros.
  • Si implementa una reversión, documente qué puede deshacer y cualquier fallo de recuperación.

Vuelva a utilizar la ruta de input.txt y repita la ejecución para comprobar un nuevo código 0 y un JSON con Success=true y LineCount=1. Conserve los registros necesarios antes de retirar únicamente los archivos temporales identificados de este ejercicio. Añadir SilentlyContinue a todos los comandos oculta errores y no sustituye su gestión.