Tareas programadas con PowerShell: Register-ScheduledTask

Tareas programadas con PowerShell: el mecanismo de un reloj antiguo, con su esfera de números romanos y sus engranajes, como una tarea programada con Register-ScheduledTask que se ejecuta sola a su hora

Las tareas programadas con PowerShell permiten que un script se ejecute solo, a la hora fijada, sin que nadie tenga que acordarse. Se guardan en el Programador de tareas de Windows, el mismo que usa el sistema para sus propias tareas. Sirven para limpiar carpetas temporales cada noche, enviar un informe de discos los lunes, copiar una carpeta al NAS o comprobar cada 15 minutos que un servicio sigue en marcha. Esta guía explica qué son y cómo crear una tarea programada con Register-ScheduledTask. También repasa los comandos de uso diario, la configuración que conviene cambiar y qué hacer cuando la tarea programada no se ejecuta, con siete escenarios listos para copiar.

Además, es material de consulta en dos niveles. Si usted no es de sistemas, las primeras secciones le explican qué automatiza su equipo de TI y por qué conviene hacerlo con PowerShell. En cambio, si es administrador, el resto trae los comandos, lo que medimos en un Windows 11 real y los errores con su causa. La guía continúa la serie de PowerShell del blog: la guía de PowerShell explica la consola y los scripts, y la de PowerShell remoto cómo ejecutar órdenes en otros equipos.

Qué son las tareas programadas con PowerShell

Windows trae un servicio que ejecuta programas a una hora o ante un suceso: el Programador de tareas. Casi todo el mundo lo conoce por su consola gráfica, taskschd.msc, pero también se maneja desde PowerShell con el módulo ScheduledTasks. Ese módulo trae 19 comandos y es el mismo en Windows PowerShell 5.1 y en PowerShell 7: lo comprobamos en las dos versiones.

Una tarea programada tiene cuatro piezas. Cada una se crea con su propio comando y, después, se juntan al registrar la tarea:

PiezaQué decideComando que la crea
AcciónQué programa se ejecuta y con qué argumentosNew-ScheduledTaskAction
DesencadenadorCuándo: una hora, cada día, al iniciar sesión, cada 15 minutosNew-ScheduledTaskTrigger
ConfiguraciónQué pasa con batería, si se repite, cuánto puede durarNew-ScheduledTaskSettingsSet
Entidad de seguridadCon qué cuenta y con qué permisos se ejecutaNew-ScheduledTaskPrincipal

Finalmente, el comando que junta las piezas y guarda la tarea en el Programador de tareas es Register-ScheduledTask. Además, según su referencia oficial, una tarea admite hasta 32 acciones, que se ejecutan en orden.

Usos comunes de las tareas programadas con PowerShell

Estas son las tareas programadas con PowerShell que más se repiten en una empresa y que más tiempo ahorran:

  • Limpieza. Borrar archivos de más de 30 días de una carpeta compartida, de los registros de una aplicación o de las descargas de un equipo de uso común.
  • Copias. Copiar cada noche una carpeta a un NAS o a otro disco con Robocopy, como explicamos en la guía de Robocopy.
  • Informes. Generar los lunes un CSV con el espacio libre de los discos, las cuentas sin uso o los equipos sin parches.
  • Vigilancia. Comprobar cada 15 minutos que un servicio sigue en marcha y arrancarlo si se detuvo.
  • Inicio de sesión. Preparar el entorno de trabajo cuando una persona entra en su equipo.
  • Mantenimiento. Reiniciar un servidor de madrugada, una vez a la semana, fuera del horario de trabajo.

Beneficios de crear tareas programadas con PowerShell

Una tarea se puede crear de tres maneras: con la consola gráfica, con el comando clásico schtasks o con PowerShell. Al fin y al cabo, las tres escriben en el mismo Programador de tareas, así que lo creado por una se ve y se cambia con las otras. Sin embargo, no permiten lo mismo:

CriterioConsola gráficaschtasksPowerShell
Repetir la misma tarea en 50 equiposEquipo por equipo, a manoSí, en un scriptSí, en un script y a distancia
Leer el resultado de todas las tareasFila por filaTexto que hay que interpretarObjetos que se filtran y se exportan
Cambiar la configuración avanzadaSíSolo una parteSí, con objetos o con XML
Dejar constancia de cómo se creóNoEl scriptEl script
Curva de aprendizajeLa más bajaMediaMedia

En resumen, el beneficio de las tareas programadas con PowerShell es el mismo que en el resto de la serie: lo que se hace con un script se puede repetir, revisar y documentar. Además, una tarea creada así se puede copiar a otro equipo en segundos.

Lo que medimos de las tareas programadas con PowerShell en Windows 11

Antes de escribir, creamos más de 80 tareas de prueba en un Windows 11 Pro en español, con Windows PowerShell 5.1.26100 y PowerShell 7.6.9, desde una consola sin elevar. Después las ejecutamos, leímos su resultado y las borramos al terminar. Esto es lo que vale la pena saber antes de empezar:

Qué comprobamosResultado
Configuración de fábrica de una tarea nuevaNo arranca con batería, se detiene si el equipo pasa a batería y se corta a las 72 horas
Una tarea con la configuración de fábrica, lanzada con el portátil en bateríaStart-ScheduledTask no da error; la tarea se queda en estado Queued (en cola) y no se ejecuta
Cuenta con la que se ejecuta si no se indica ningunaLa del usuario que la creó, con inicio de sesión interactivo: solo se ejecuta si esa persona tiene la sesión abierta
Registrar con -User SYSTEM, -RunLevel Highest, -AtStartup o -AtLogOn sin usuario, desde una consola sin elevarAcceso denegado.
-AtLogOn con el usuario actual, sin elevarFunciona
Registrar dos veces el mismo nombreNo se puede crear un archivo que ya existe. Con -Force la reemplaza
-At '10 p. m.', como escribe la hora Windows en españolError: no se reconoce como fecha. -At '22:00' funciona
-At '03/11/2026 07:30'La tarea queda para el 11 de marzo, no para el 3 de noviembre
-RepetitionDuration ([TimeSpan]::MaxValue)Error 0x80041318: El archivo XML de tarea contiene un valor con formato incorrecto o que está fuera de intervalo.
-Execute 'pwsh.exe' con PowerShell 7 instalado desde Microsoft StoreLa tarea falla con 0x80070002 (no encuentra el archivo); con la ruta completa funciona
Script con un error sin $ErrorActionPreference = 'Stop'La tarea termina con resultado 0, como si todo hubiera ido bien
Historial del Programador de tareasDesactivado de fábrica

Tres conclusiones para sus tareas programadas con PowerShell

De ahí salen tres conclusiones prácticas. Primera: en un portátil, una tarea con la configuración de fábrica no se ejecuta con batería y no avisa. Segunda: por sí solo, un resultado 0 no garantiza que el script hiciera su trabajo. Por último, las fechas y las rutas se escriben siempre completas y en un formato que no dependa del idioma.

Cómo crear una tarea programada en PowerShell con Register-ScheduledTask

Para crear una tarea programada con PowerShell bastan cuatro órdenes. Este ejemplo completo crea una tarea que ejecuta cada noche a las 22:00 un script de limpieza. Se puede copiar tal cual y cambiar la ruta del script:

$accion = New-ScheduledTaskAction -Execute 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' `
    -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Limpiar-Temporales.ps1"' `
    -WorkingDirectory 'C:\Scripts'
$disparador = New-ScheduledTaskTrigger -Daily -At '22:00'
$ajustes = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
    -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Hours 1)
Register-ScheduledTask -TaskName 'Limpiar temporales' -TaskPath '\Empresa\' `
    -Action $accion -Trigger $disparador -Settings $ajustes -Description 'Borra archivos de más de 30 días'

A continuación explicamos por partes el porqué de cada línea.

Paso 1: la acción

La acción dice qué se ejecuta. En las tareas programadas con PowerShell, el programa es PowerShell y el script va en los argumentos. Por otra parte, cuatro detalles evitan la mayoría de los fallos:

  • La ruta completa del programa. Con Windows PowerShell 5.1 es C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe. En cambio, con PowerShell 7 instalado con el MSI es C:\Program Files\PowerShell\7\pwsh.exe; si se instaló desde Microsoft Store, la ruta que da (Get-Command pwsh.exe).Source. De hecho, en nuestro equipo, con la versión de la tienda, el nombre suelto pwsh.exe hizo fallar todas las tareas.
  • -File con la ruta entre comillas dobles. Sin comillas, una ruta con espacios se corta en el primer espacio y la tarea falla.
  • -NoProfile. Así, la tarea no carga el perfil del usuario, que puede ser lento o cambiar el comportamiento del script.
  • -WorkingDirectory. En cambio, sin él la tarea arranca en C:\Windows\System32. Por ejemplo, lo medimos: un script que escribía un archivo con ruta relativa intentó crearlo allí, recibió acceso denegado y la tarea terminó con resultado 0.

El parámetro -ExecutionPolicy Bypass solo afecta a ese proceso y no cambia la directiva del equipo. Por lo tanto, hace falta si la directiva no permite scripts locales sin firmar; la guía de PowerShell explica las directivas de ejecución.

Paso 2: el desencadenador

El desencadenador dice cuándo. Por eso, la hora se escribe en formato de 24 horas, '22:00', y las fechas en formato año-mes-día, '2026-11-03 07:30'. Por ejemplo, medimos que '10 p. m.' da error y que '03/11/2026' se lee con el mes primero, sin importar que Windows esté en español. Además, más abajo hay una tabla con todos los desencadenadores.

Paso 3: la configuración

La configuración de fábrica está pensada para no gastar batería, y en un portátil eso significa que la tarea no se ejecuta. Por esa razón, el ejemplo añade cuatro ajustes. -AllowStartIfOnBatteries y -DontStopIfGoingOnBatteries dejan que se ejecute con batería. Luego, -StartWhenAvailable la ejecuta en cuanto se pueda si el equipo estaba apagado a la hora prevista. Por último, -ExecutionTimeLimit la corta a la hora en vez de a las 72 horas de fábrica.

Paso 4: registrar la tarea

Register-ScheduledTask guarda la tarea en el Programador de tareas. Además, conviene usar siempre una carpeta propia con -TaskPath, como \Empresa\, para que las tareas de la empresa no se mezclen con las de Windows: en nuestro equipo había 190 en \Microsoft\. Por otro lado, si no se indica cuenta, la tarea se ejecuta con la del usuario que la registra, y solo mientras esa persona tenga la sesión abierta. Más adelante, la sección de cuentas explica cómo cambiarlo.

Para comprobar que la tarea programada en PowerShell quedó bien, se ejecuta a mano y se lee el resultado:

Start-ScheduledTask -TaskName 'Limpiar temporales' -TaskPath '\Empresa\'
Get-ScheduledTaskInfo -TaskName 'Limpiar temporales' -TaskPath '\Empresa\' |
    Select-Object LastRunTime, LastTaskResult, NextRunTime

Un LastTaskResult de 0 significa que el script terminó sin error, y 267011 que la tarea aún no se ha ejecutado nunca. Si lo lee justo después de lanzarla, verá 267009: la tarea sigue en marcha, así que conviene esperar unos segundos y leerlo de nuevo. Asimismo, la tabla de códigos está más abajo.

Desencadenadores: cuándo se ejecuta la tarea

En las tareas programadas con PowerShell, New-ScheduledTaskTrigger cubre los casos habituales. Cada fila de la tabla es una línea que se puede copiar:

CuándoDesencadenador
Una sola vezNew-ScheduledTaskTrigger -Once -At '2026-11-03 07:30'
Cada día a las 22:00New-ScheduledTaskTrigger -Daily -At '22:00'
Cada 3 díasNew-ScheduledTaskTrigger -Daily -DaysInterval 3 -At '03:00'
Lunes y viernes a las 7:00New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday, Friday -At '07:00'
Un domingo de cada dosNew-ScheduledTaskTrigger -Weekly -WeeksInterval 2 -DaysOfWeek Sunday -At '03:00'
Al iniciar sesión el usuario actualNew-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"
Al arrancar el equipo (requiere elevar)New-ScheduledTaskTrigger -AtStartup
Cada 15 minutos, sin finNew-ScheduledTaskTrigger -Once -At '00:00' -RepetitionInterval (New-TimeSpan -Minutes 15)
Cada día, con un retraso aleatorio de hasta 30 minutosNew-ScheduledTaskTrigger -Daily -At '22:00' -RandomDelay (New-TimeSpan -Minutes 30)

Por cierto, los días de la semana van en inglés porque son valores de .NET: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday y Sunday. Por su parte, el retraso aleatorio sirve cuando 50 equipos tienen la misma tarea y no conviene que todos salgan a la red en el mismo minuto.

Repetir cada pocos minutos

Para repetir una tarea programada sin fin se indica el intervalo y se omite la duración. Por ejemplo, así se midió en el laboratorio: la tarea registrada a las 8:32 con -Once -At '00:00' y un intervalo de 15 minutos tenía su próxima ejecución a las 8:45. Sin embargo, muchos ejemplos de internet usan -RepetitionDuration ([TimeSpan]::MaxValue) para decir «sin fin», y en Windows 11 eso da el error 0x80041318. Por lo tanto, la solución es no poner la duración, o poner una concreta como (New-TimeSpan -Days 1).

Al iniciar sesión, con un minuto de espera

Una tarea programada al iniciar sesión compite con todo lo que arranca a la vez. Por eso, se le puede dar un respiro con la propiedad Delay del desencadenador, en el formato de duración de Windows:

$disparador = New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"
$disparador.Delay = 'PT1M'

PT1M es un minuto, PT30S treinta segundos y PT2H dos horas. En efecto, lo comprobamos: la tarea guardó el retraso de un minuto.

Cada mes: el desencadenador que falta

New-ScheduledTaskTrigger no tiene una opción mensual; lo comprobamos en las dos versiones de PowerShell. Por eso, para el primer día de cada mes se usa schtasks, que sí la tiene, y la tarea se administra después desde PowerShell como cualquier otra:

schtasks /Create /TN "\Empresa\Informe mensual" /TR "C:\Scripts\Informe-Mensual.cmd" /SC MONTHLY /D 1 /ST 07:00

Además, medimos un detalle: en una tarea mensual, la forma habitual de cambiarla, que es leerla, modificar el objeto y devolverlo con $tarea | Set-ScheduledTask, falla con El parámetro no es correcto. En cambio, Set-ScheduledTask con el nombre y -Action o -Settings sí funciona.

La configuración que conviene cambiar

New-ScheduledTaskSettingsSet decide el comportamiento de las tareas programadas con PowerShell. Estos son sus valores de fábrica, leídos en las dos versiones, y lo que recomendamos para una tarea de empresa:

AjusteDe fábricaRecomendadoParámetro
Arrancar con bateríaNoSí, en portátiles-AllowStartIfOnBatteries
Detenerse al pasar a bateríaSíNo, en portátiles-DontStopIfGoingOnBatteries
Ejecutar en cuanto se pueda si se perdió la horaNoSí-StartWhenAvailable
Tiempo máximo de ejecución72 horasLo que dure el script, con margen-ExecutionTimeLimit (New-TimeSpan -Hours 1)
Si ya se está ejecutandoNo iniciar otraIgual-MultipleInstances IgnoreNew
Reintentos si fallaNinguno3 cada 5 minutos, si tiene sentido-RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 5)
Solo con red disponibleNoSí, si usa la red-RunOnlyIfNetworkAvailable
Despertar el equipoNoSolo si se necesita-WakeToRun

Sobre todo, el límite de tiempo importa más de lo que parece. Es decir, una tarea colgada ocupa su lugar y, con IgnoreNew, impide que arranque la siguiente ejecución. De hecho, medimos los dos casos: un script de 40 segundos con un límite de 10 se cortó a los 10 segundos con el resultado 267014. Del mismo modo, un segundo Start-ScheduledTask mientras la tarea seguía en marcha no dio error ni abrió un segundo proceso.

Por último, para cambiar la configuración de una tarea que ya existe, se lee, se cambia y se guarda:

$tarea = Get-ScheduledTask -TaskName 'Limpiar temporales' -TaskPath '\Empresa\'
$tarea.Settings.DisallowStartIfOnBatteries = $false
$tarea.Settings.StopIfGoingOnBatteries = $false
$tarea | Set-ScheduledTask

Con qué cuenta se ejecuta la tarea programada

Ante todo, la cuenta decide qué puede hacer el script y si se ejecuta cuando nadie ha iniciado sesión. New-ScheduledTaskPrincipal la define, y la referencia de tipos de inicio de sesión explica cada modo:

CuentaSe ejecuta sin sesión abiertaAcceso a la red y a archivos cifrados
Usuario actual, interactivo (la de fábrica)NoSí
Usuario con contraseña guardada (-User y -Password)SíSí
Usuario sin contraseña guardada (-LogonType S4U)SíNo
SYSTEM (-UserId 'SYSTEM' -LogonType ServiceAccount)SíCon la cuenta del equipo
Cuenta de servicio administrada de grupo (gMSA)SíSí

Además, desde una consola sin elevar solo pudimos registrar tareas con la cuenta propia en modo interactivo. Con S4U, con SYSTEM y con -RunLevel Highest, la respuesta fue Acceso denegado., y la documentación de seguridad del Programador de tareas confirma que un proceso sin privilegios no puede registrar tareas como SYSTEM ni con el nivel más alto.

Para tareas de servidor, Microsoft documenta que las cuentas de servicio administradas de grupo pueden ejecutar tareas programadas. Como su contraseña la cambia Active Directory, no caduca ni queda escrita en ningún script. La guía de Active Directory PowerShell explica cómo administrar las cuentas del dominio.

En la práctica, una regla sencilla para elegir es esta. SYSTEM, para tareas del propio equipo que necesitan permisos de administrador, como reiniciar un servicio. Una cuenta de servicio, para tareas que leen o escriben en otros servidores. Y la cuenta del usuario, solo para tareas de su propio escritorio. Por otro lado, el parámetro -RunLevel Highest ejecuta la tarea con los permisos elevados de la cuenta, y registrarlo también exige una consola elevada.

Comandos de PowerShell más comunes para tareas programadas

Estos son los comandos que se usan a diario con las tareas programadas en PowerShell. Todos admiten -TaskName y -TaskPath y, por seguridad, conviene escribir siempre los dos para no tocar una tarea con el mismo nombre en otra carpeta:

ComandoPara qué sirve
Get-ScheduledTaskLista las tareas; admite comodines, como -TaskPath '\Empresa\*'
Get-ScheduledTaskInfoÚltima ejecución, último resultado y próxima ejecución
Start-ScheduledTaskEjecuta la tarea ahora, sin esperar a su hora
Stop-ScheduledTaskDetiene la ejecución en curso
Disable-ScheduledTask / Enable-ScheduledTaskDesactiva o reactiva la tarea sin borrarla
Set-ScheduledTaskCambia la acción, el desencadenador, la configuración o la cuenta
Unregister-ScheduledTaskBorra la tarea; con -Confirm:$false no pregunta
Export-ScheduledTaskDevuelve la tarea en XML, para guardarla o copiarla
New-ScheduledTaskMonta la tarea sin registrarla, para registrarla después

Por ejemplo, para cambiar la hora de una tarea y desactivarla durante un cierre de mes:

Set-ScheduledTask -TaskName 'Limpiar temporales' -TaskPath '\Empresa\' -Trigger (New-ScheduledTaskTrigger -Daily -At '21:30')
Disable-ScheduledTask -TaskName 'Limpiar temporales' -TaskPath '\Empresa\'
Enable-ScheduledTask -TaskName 'Limpiar temporales' -TaskPath '\Empresa\'

Asimismo, para borrar todas las tareas de una carpeta, después de revisar cuáles son:

Get-ScheduledTask -TaskPath '\Empresa\Pruebas\' | Unregister-ScheduledTask -Confirm:$false

Ejecutar un script de PowerShell como tarea programada

Sin duda, la pregunta más repetida sobre este tema es cómo ejecutar un script de PowerShell como tarea programada y saber si salió bien. En este caso, la clave es el código de salida: el número que el script devuelve al terminar y que el Programador de tareas guarda como último resultado. Por eso lo medimos con las dos versiones de PowerShell:

Cómo termina el scriptÚltimo resultado
Sin errores0
Con exit 3, lanzado con -File3
Con exit 3, lanzado con -Command "& 'script.ps1'"1: el número se pierde
Con exit 3, lanzado con -Command "& 'script.ps1'; exit $LASTEXITCODE"3
Con throw1
Con un error sin $ErrorActionPreference = 'Stop'0: la tarea parece correcta
Con un error y $ErrorActionPreference = 'Stop'1
El script no existe, o la ruta tiene espacios y va sin comillas0xFFFD0000 con Windows PowerShell 5.1 y 64 con PowerShell 7

Asimismo, la documentación de pwsh lo explica: con -File, el código de salida es el número de exit; con -Command, es 0 o 1 según si la última orden tuvo éxito. Por eso, en las tareas programadas con PowerShell recomendamos -File y un patrón fijo dentro del script: $ErrorActionPreference = 'Stop' al principio, un bloque try y catch, un registro propio y un exit con 0 o 1 al final.

Por otra parte, si el script escribe salida o errores que interesa conservar, Start-Transcript al principio guarda todo lo que pasa en un archivo de texto. De hecho, lo comprobamos: la transcripción recogió la salida y el error de una ejecución que el Programador de tareas daba por correcta.

Escenarios de tareas programadas con PowerShell

Para empezar, los siete escenarios usan la carpeta C:\Scripts para los scripts, C:\Scripts\Registros para los registros y la carpeta \Empresa\ del Programador de tareas. Además, cada script se prueba primero a mano, desde la consola, antes de programarlo.

1. Limpiar archivos de más de 30 días cada noche

Primero, el script sigue el patrón recomendado: se detiene ante cualquier error, deja una línea en su registro y devuelve 0 o 1.

# C:\Scripts\Limpiar-Temporales.ps1
$ErrorActionPreference = 'Stop'
$registro = 'C:\Scripts\Registros\limpieza.log'
$limite = (Get-Date).AddDays(-30)
try {
    $viejos = Get-ChildItem -Path 'D:\Compartida\Temporal' -File -Recurse | Where-Object LastWriteTime -lt $limite
    $viejos | Remove-Item -Force
    Add-Content -Path $registro -Value ('{0:yyyy-MM-dd HH:mm} - {1} archivos borrados' -f (Get-Date), @($viejos).Count)
    exit 0
} catch {
    Add-Content -Path $registro -Value ('{0:yyyy-MM-dd HH:mm} - ERROR: {1}' -f (Get-Date), $_.Exception.Message)
    exit 1
}

La tarea programada en PowerShell es la del ejemplo completo de más arriba. Antes de programarla, sin embargo, conviene ejecutar la línea de Get-ChildItem sola y revisar qué archivos devuelve: una tarea que borra se equivoca cada noche.

2. Informe semanal de espacio en disco

En este caso, el script deja un CSV con el espacio de cada disco. Además, el separador es el punto y coma, el que Excel espera en un equipo configurado en español de Colombia:

# C:\Scripts\Informe-Discos.ps1
$ErrorActionPreference = 'Stop'
$fecha = Get-Date -Format 'yyyy-MM-dd'
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
    Select-Object DeviceID,
        @{ n = 'TotalGB'; e = { [math]::Round($_.Size / 1GB, 1) } },
        @{ n = 'LibreGB'; e = { [math]::Round($_.FreeSpace / 1GB, 1) } },
        @{ n = 'LibrePct'; e = { [math]::Round(100 * $_.FreeSpace / $_.Size, 1) } } |
    Export-Csv -Path "C:\Scripts\Informes\discos-$fecha.csv" -NoTypeInformation -Encoding UTF8 -Delimiter ';'

Y la tarea, los lunes a las 7:00:

$accion = New-ScheduledTaskAction -Execute 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' `
    -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Informe-Discos.ps1"'
$disparador = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At '07:00'
Register-ScheduledTask -TaskName 'Informe de discos' -TaskPath '\Empresa\' -Action $accion -Trigger $disparador `
    -Settings (New-ScheduledTaskSettingsSet -StartWhenAvailable)

Con Windows PowerShell 5.1, -Encoding UTF8 escribe el archivo con la marca que Excel necesita para mostrar bien las tildes. En cambio, con PowerShell 7 hay que usar -Encoding utf8BOM, como medimos en la guía de Active Directory PowerShell.

3. Copia nocturna de una carpeta con Robocopy

En este caso, la acción no es PowerShell sino Robocopy, que se programa directamente:

$accion = New-ScheduledTaskAction -Execute 'C:\Windows\System32\Robocopy.exe' `
    -Argument '"D:\Compartida" "\\nas01\copias\compartida" /E /R:2 /W:5 /NP /LOG:C:\Scripts\Registros\robocopy.log'
$disparador = New-ScheduledTaskTrigger -Daily -At '23:00'
Register-ScheduledTask -TaskName 'Copia de la compartida' -TaskPath '\Empresa\' -Action $accion -Trigger $disparador `
    -Settings (New-ScheduledTaskSettingsSet -StartWhenAvailable -RunOnlyIfNetworkAvailable)

Hay dos avisos que vienen de la guía de Robocopy. Primero, con Robocopy, un último resultado de 1 significa que copió archivos sin errores, no que falló. Segundo, /MIR borra en el destino lo que ya no está en el origen, así que no conviene en una copia nocturna sin vigilancia. Una copia así no sustituye a un sistema de respaldo con versiones; la guía de tipos de backup explica la diferencia.

4. Comprobar un servicio cada 15 minutos

En este caso, el script arranca el servicio si se detuvo y lo anota. Arrancar un servicio exige permisos de administrador, así que la tarea se ejecuta como SYSTEM y se registra desde una consola elevada:

# C:\Scripts\Vigilar-Servicio.ps1
$nombre = 'Spooler'
$servicio = Get-Service -Name $nombre
if ($servicio.Status -ne 'Running') {
    Start-Service -Name $nombre
    Add-Content -Path 'C:\Scripts\Registros\servicio.log' -Value ('{0:yyyy-MM-dd HH:mm} - {1} estaba {2}; Start-Service ejecutado' -f (Get-Date), $nombre, $servicio.Status)
}
$accion = New-ScheduledTaskAction -Execute 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' `
    -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Vigilar-Servicio.ps1"'
$disparador = New-ScheduledTaskTrigger -Once -At '00:00' -RepetitionInterval (New-TimeSpan -Minutes 15)
$cuenta = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
$ajustes = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -ExecutionTimeLimit (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName 'Vigilar cola de impresión' -TaskPath '\Empresa\' -Action $accion `
    -Trigger $disparador -Principal $cuenta -Settings $ajustes

Ahora bien, una tarea así arregla el síntoma, no la causa. Por ejemplo, si el registro muestra que el servicio se cae a diario, el problema está en otra parte, y el visor de eventos suele decir dónde.

5. Preparar el equipo al iniciar sesión

Para el usuario actual, en cambio, no hace falta elevar. El retraso de un minuto deja que termine el arranque:

$accion = New-ScheduledTaskAction -Execute 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' `
    -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Al-Iniciar.ps1"'
$disparador = New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"
$disparador.Delay = 'PT1M'
Register-ScheduledTask -TaskName 'Al iniciar sesión' -TaskPath '\Empresa\' -Action $accion -Trigger $disparador `
    -Settings (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries)

6. Copiar una tarea a otro equipo

Las tareas programadas con PowerShell se guardan en XML y se registran igual en otro equipo:

Export-ScheduledTask -TaskName 'Limpiar temporales' -TaskPath '\Empresa\' |
    Out-File -FilePath 'C:\Scripts\limpiar-temporales.xml' -Encoding unicode
Register-ScheduledTask -TaskName 'Limpiar temporales' -TaskPath '\Empresa\' -Xml (Get-Content 'C:\Scripts\limpiar-temporales.xml' -Raw)

Eso sí, el XML lleva dentro la cuenta con la que se ejecuta. Lo medimos: al importarlo, la tarea conservó la cuenta original, y al cambiarla por SYSTEM en el XML, el registro sin elevar dio Acceso denegado. Por lo tanto, si el otro equipo usa otra cuenta, se indica con -User al registrar.

Además, los comandos del módulo también trabajan sobre otro equipo con -CimSession, que viaja por WinRM. La guía de PowerShell remoto explica cómo habilitarlo:

$sesion = New-CimSession -ComputerName 'SRV-ARCHIVOS'
Get-ScheduledTask -CimSession $sesion -TaskPath '\Empresa\' | Get-ScheduledTaskInfo
Remove-CimSession -CimSession $sesion

7. Revisar qué tareas fallan en un equipo

Por último, esta consulta de PowerShell lista las tareas programadas activas que no son de Windows y cuyo último resultado no es correcto:

Get-ScheduledTask | Where-Object { $_.TaskPath -notlike '\Microsoft\*' -and $_.State -ne 'Disabled' } |
    Get-ScheduledTaskInfo |
    Where-Object { $_.LastTaskResult -notin 0, 267009, 267011 } |
    Select-Object TaskPath, TaskName, LastRunTime, LastTaskResult, @{ n = 'Hex'; e = { '0x{0:X8}' -f $_.LastTaskResult } } |
    Format-Table -AutoSize

En el equipo donde escribimos esta guía, de 14 tareas activas fuera de \Microsoft\, instaladas por programas como OneDrive, Git u Opera, 5 tenían un último resultado distinto de 0. Una de ellas estaba en ejecución, con el 267009, y por eso la consulta lo excluye. Por otro lado, las tareas de Robocopy aparecen con un 1 que es correcto: hay que revisarlas aparte.

Cómo saber si una tarea se ejecutó: los códigos de resultado

Como vimos, el último resultado se lee con Get-ScheduledTaskInfo y llega en decimal. La lista oficial de códigos los da en hexadecimal, y esta tabla reúne los que vimos en las pruebas:

DecimalHexadecimalQué significa
00x0La acción terminó con código 0
10x1El script terminó con error, o Robocopy copió archivos sin errores
2670090x41301La tarea se está ejecutando
2670110x41303La tarea aún no se ha ejecutado nunca
2670140x41306La tarea se detuvo: por Stop-ScheduledTask o por el tiempo máximo
21479424020x80070002No se encontró el programa de la acción
42947706880xFFFD0000Windows PowerShell 5.1 no encontró el script de -File
640x40PowerShell 7 no encontró el script de -File

Activar el historial

El historial del Programador de tareas viene desactivado de fábrica. Lo comprobamos en el registro Microsoft-Windows-TaskScheduler/Operational de nuestro equipo. Por lo tanto, sin él no hay forma de saber qué pasó la noche anterior. Por suerte, se activa una vez, desde una consola elevada:

$registro = Get-WinEvent -ListLog 'Microsoft-Windows-TaskScheduler/Operational'
$registro.IsEnabled = $true
$registro.SaveChanges()

A continuación, estos son los eventos que más ayudan, con el texto que Windows en español da a cada uno:

EventoTexto
100El Programador de tareas inició la instancia de la tarea
102El Programador de tareas finalizó correctamente la instancia de la tarea
103 y 203El Programador de tareas no puede iniciar la instancia, o la acción, de la tarea
201El Programador de tareas completó correctamente la tarea, la instancia y la acción con el código devuelto
322No inició la tarea porque ya se está ejecutando otra instancia de la misma
329Cerró la instancia porque superó el tiempo asignado para ejecutarse
332No inició la tarea porque el usuario no tenía una sesión iniciada

Ojo con el 201: su texto dice «completó correctamente» y después da el código devuelto. La frase es la misma con cualquier código, así que lo que hay que leer es el número.

Y esta orden lee los últimos de una carpeta:

Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-TaskScheduler/Operational'; Id = 101, 103, 201, 203, 322, 329, 332 } -MaxEvents 200 |
    Where-Object Message -like '*\Empresa\*' |
    Select-Object TimeCreated, Id, Message

Si el historial sigue desactivado, la orden responde No se encontraron eventos que coincidan con los criterios de selección especificados. La guía del visor de eventos explica cómo leer estos registros con más detalle.

La tarea programada no se ejecuta: causas y soluciones

Cuando la tarea programada no se ejecuta, la causa casi siempre está en esta tabla, que vale tanto para las tareas programadas con PowerShell como para las creadas a mano. La primera columna es lo que se ve; la segunda, lo que medimos o lo que documenta Microsoft:

Lo que se veCausa probableSolución
Estado Queued y no arranca, en un portátilConfiguración de fábrica: no arranca con batería-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Funciona al ejecutarla a mano, pero no de nocheLa cuenta es interactiva y no había sesión abierta (evento 332)Ejecutarla con SYSTEM, con contraseña guardada o con una gMSA
No se ejecutó con el equipo apagadoSin -StartWhenAvailable, la ejecución perdida no se recuperaActivar -StartWhenAvailable
Resultado 0x80070002No encuentra el programa: nombre sin ruta, o PowerShell 7 de Microsoft StoreRuta completa en -Execute
Resultado 0xFFFD0000 o 64No encuentra el script, o la ruta tiene espacios sin comillasRuta completa entre comillas dobles después de -File
Resultado 0, pero el script no hizo nadaError sin Stop, o ruta relativa sin -WorkingDirectory$ErrorActionPreference = 'Stop', -WorkingDirectory y un registro propio
Resultado 267014Superó el tiempo máximoRevisar por qué tarda y ajustar -ExecutionTimeLimit
No arranca y hay un evento 322La ejecución anterior sigue en marchaBuscar por qué se cuelga y poner un tiempo máximo
Acceso denegado. al registrarlaSYSTEM, Highest, al arrancar o al iniciar sesión de cualquier usuario sin elevarAbrir PowerShell como administrador
Error 0x80041318 al registrarla-RepetitionDuration ([TimeSpan]::MaxValue)Omitir la duración
Funciona con una cuenta y falla con otraLa otra cuenta no tiene permiso sobre las carpetas o la redProbar el script con la misma cuenta de la tarea

schtasks y Register-ScheduledJob: las otras dos vías

Por su parte, schtasks es el comando clásico y sigue siendo útil: está en cualquier Windows, sirve desde CMD y tiene el desencadenador mensual que le falta a PowerShell. Además, su referencia oficial documenta todas las opciones. También medimos que hereda la misma configuración de batería que PowerShell y que schtasks /Run responde «CORRECTO» aunque la tarea se quede en cola. Asimismo, medimos un detalle que conviene evitar: schtasks /Change pide la contraseña de la cuenta, y si se deja en blanco advierte que la tarea podría no ejecutarse. Por eso, para cambiar tareas es mejor Set-ScheduledTask. La consulta detallada sale en español:

schtasks /Query /TN "\Empresa\Limpiar temporales" /V /FO LIST

Register-ScheduledJob crea trabajos programados de PowerShell: guarda el resultado de cada ejecución en disco para leerlo después con Receive-Job. Sin embargo, tiene dos límites medidos. Primero, solo existe en Windows PowerShell 5.1: en PowerShell 7, el módulo PSScheduledJob está en la lista de módulos bloqueados de la compatibilidad con Windows PowerShell. Además, exige una consola elevada: sin ella respondió que había que ejecutar Windows PowerShell como administrador. En consecuencia, para scripts nuevos recomendamos Register-ScheduledTask.

Recomendaciones para tareas programadas con PowerShell en la empresa

  • Una carpeta propia. Todas las tareas de la empresa en \Empresa\ o similar, para listarlas, copiarlas y revisarlas juntas.
  • Rutas completas. En -Execute, en -File y dentro del script, con -WorkingDirectory siempre.
  • Scripts con tildes, en UTF-8 con BOM. Windows PowerShell 5.1 lee un script sin BOM como ANSI y rompe las tildes, como medimos en la guía de PowerShell. Los ejemplos de esta guía evitan las tildes dentro de los scripts por esa razón.
  • Fechas sin ambigüedad. Horas como '22:00' y fechas como '2026-11-03'.
  • La configuración, revisada. Batería, -StartWhenAvailable y un tiempo máximo realista.
  • El script, honesto. $ErrorActionPreference = 'Stop', try y catch, un registro propio y exit 0 o exit 1.
  • La cuenta mínima. SYSTEM o una cuenta de servicio solo cuando hace falta; nunca la cuenta personal de un administrador con la contraseña guardada.
  • El historial, activado. Y una revisión periódica con la consulta del escenario 7.
  • Una prueba con la cuenta real. Ejecutar la tarea con Start-ScheduledTask y leer el resultado antes de darla por buena.
  • Los scripts, guardados. En una carpeta con permisos restringidos: quien pueda cambiar el script de una tarea que se ejecuta como SYSTEM tiene los permisos de SYSTEM.

Tareas programadas con orden y registro

Las tareas programadas con PowerShell ahorran horas de trabajo repetitivo, pero también fallan en silencio si nadie revisa su resultado. En KHARONTE automatizamos y vigilamos tareas en puestos de trabajo y servidores, y cada incidencia queda registrada en nuestra mesa de ayuda TI.

Por eso, si su empresa quiere delegar la operación diaria de sus equipos, nuestros servicios administrados de TI cubren el soporte, la infraestructura de TI y el monitoreo. Puede ver el detalle de nuestros servicios de TI para empresas.

Preguntas frecuentes

¿Cómo creo una tarea programada con PowerShell?

Se crean tres objetos, la acción con New-ScheduledTaskAction, el desencadenador con New-ScheduledTaskTrigger y la configuración con New-ScheduledTaskSettingsSet, y se registran con Register-ScheduledTask. El ejemplo completo está al principio de esta guía.

¿Por qué mi tarea programada no se ejecuta?

Las causas más comunes son tres: el portátil estaba en batería y la configuración de fábrica no lo permite, la cuenta era interactiva y no había sesión abierta, o la ruta del programa o del script no era completa. La tabla de causas y soluciones las reúne todas.

¿Cómo ejecuto un script de PowerShell al iniciar Windows?

Con New-ScheduledTaskTrigger -AtStartup y la cuenta SYSTEM, desde una consola elevada. Si basta con que se ejecute al iniciar sesión el usuario actual, -AtLogOn -User con su nombre no necesita elevar.

¿Cómo programo una tarea cada 5 minutos con PowerShell?

Con New-ScheduledTaskTrigger -Once -At '00:00' -RepetitionInterval (New-TimeSpan -Minutes 5), sin -RepetitionDuration. Además, conviene añadir un tiempo máximo de ejecución menor que el intervalo.

¿Las tareas programadas con PowerShell se ven en el Programador de tareas?

Sí. PowerShell, schtasks y la consola gráfica escriben en el mismo Programador de tareas, así que una tarea creada con cualquiera de los tres se ve y se cambia con los otros dos.

Hablemos de la TI de su empresa

Cuéntenos qué necesita su operación y le enviamos una propuesta por escrito. Cada línea del portafolio se contrata por separado.

Compartir este artículo

Últimas entradas

Escríbanos ahora