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:
| Pieza | Qué decide | Comando que la crea |
|---|---|---|
| Acción | Qué programa se ejecuta y con qué argumentos | New-ScheduledTaskAction |
| Desencadenador | Cuándo: una hora, cada día, al iniciar sesión, cada 15 minutos | New-ScheduledTaskTrigger |
| Configuración | Qué pasa con batería, si se repite, cuánto puede durar | New-ScheduledTaskSettingsSet |
| Entidad de seguridad | Con qué cuenta y con qué permisos se ejecuta | New-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:
| Criterio | Consola gráfica | schtasks | PowerShell |
|---|---|---|---|
| Repetir la misma tarea en 50 equipos | Equipo por equipo, a mano | Sí, en un script | Sí, en un script y a distancia |
| Leer el resultado de todas las tareas | Fila por fila | Texto que hay que interpretar | Objetos que se filtran y se exportan |
| Cambiar la configuración avanzada | Sí | Solo una parte | Sí, con objetos o con XML |
| Dejar constancia de cómo se creó | No | El script | El script |
| Curva de aprendizaje | La más baja | Media | Media |
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é comprobamos | Resultado |
|---|---|
| Configuración de fábrica de una tarea nueva | No 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ía | Start-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 ninguna | La 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 elevar | Acceso denegado. |
-AtLogOn con el usuario actual, sin elevar | Funciona |
| Registrar dos veces el mismo nombre | No se puede crear un archivo que ya existe. Con -Force la reemplaza |
-At '10 p. m.', como escribe la hora Windows en español | Error: 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 Store | La 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 tareas | Desactivado 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 esC:\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 sueltopwsh.exehizo fallar todas las tareas. -Filecon 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 enC:\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ándo | Desencadenador |
|---|---|
| Una sola vez | New-ScheduledTaskTrigger -Once -At '2026-11-03 07:30' |
| Cada día a las 22:00 | New-ScheduledTaskTrigger -Daily -At '22:00' |
| Cada 3 días | New-ScheduledTaskTrigger -Daily -DaysInterval 3 -At '03:00' |
| Lunes y viernes a las 7:00 | New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday, Friday -At '07:00' |
| Un domingo de cada dos | New-ScheduledTaskTrigger -Weekly -WeeksInterval 2 -DaysOfWeek Sunday -At '03:00' |
| Al iniciar sesión el usuario actual | New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME" |
| Al arrancar el equipo (requiere elevar) | New-ScheduledTaskTrigger -AtStartup |
| Cada 15 minutos, sin fin | New-ScheduledTaskTrigger -Once -At '00:00' -RepetitionInterval (New-TimeSpan -Minutes 15) |
| Cada día, con un retraso aleatorio de hasta 30 minutos | New-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:
| Ajuste | De fábrica | Recomendado | Parámetro |
|---|---|---|---|
| Arrancar con batería | No | Sí, en portátiles | -AllowStartIfOnBatteries |
| Detenerse al pasar a batería | Sí | No, en portátiles | -DontStopIfGoingOnBatteries |
| Ejecutar en cuanto se pueda si se perdió la hora | No | Sí | -StartWhenAvailable |
| Tiempo máximo de ejecución | 72 horas | Lo que dure el script, con margen | -ExecutionTimeLimit (New-TimeSpan -Hours 1) |
| Si ya se está ejecutando | No iniciar otra | Igual | -MultipleInstances IgnoreNew |
| Reintentos si falla | Ninguno | 3 cada 5 minutos, si tiene sentido | -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 5) |
| Solo con red disponible | No | Sí, si usa la red | -RunOnlyIfNetworkAvailable |
| Despertar el equipo | No | Solo 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:
| Cuenta | Se ejecuta sin sesión abierta | Acceso a la red y a archivos cifrados |
|---|---|---|
| Usuario actual, interactivo (la de fábrica) | No | Sí |
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:
| Comando | Para qué sirve |
|---|---|
Get-ScheduledTask | Lista las tareas; admite comodines, como -TaskPath '\Empresa\*' |
Get-ScheduledTaskInfo | Última ejecución, último resultado y próxima ejecución |
Start-ScheduledTask | Ejecuta la tarea ahora, sin esperar a su hora |
Stop-ScheduledTask | Detiene la ejecución en curso |
Disable-ScheduledTask / Enable-ScheduledTask | Desactiva o reactiva la tarea sin borrarla |
Set-ScheduledTask | Cambia la acción, el desencadenador, la configuración o la cuenta |
Unregister-ScheduledTask | Borra la tarea; con -Confirm:$false no pregunta |
Export-ScheduledTask | Devuelve la tarea en XML, para guardarla o copiarla |
New-ScheduledTask | Monta 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 errores | 0 |
Con exit 3, lanzado con -File | 3 |
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 throw | 1 |
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 comillas | 0xFFFD0000 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:
| Decimal | Hexadecimal | Qué significa |
|---|---|---|
| 0 | 0x0 | La acción terminó con código 0 |
| 1 | 0x1 | El script terminó con error, o Robocopy copió archivos sin errores |
| 267009 | 0x41301 | La tarea se está ejecutando |
| 267011 | 0x41303 | La tarea aún no se ha ejecutado nunca |
| 267014 | 0x41306 | La tarea se detuvo: por Stop-ScheduledTask o por el tiempo máximo |
| 2147942402 | 0x80070002 | No se encontró el programa de la acción |
| 4294770688 | 0xFFFD0000 | Windows PowerShell 5.1 no encontró el script de -File |
| 64 | 0x40 | PowerShell 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:
| Evento | Texto |
|---|---|
| 100 | El Programador de tareas inició la instancia de la tarea |
| 102 | El Programador de tareas finalizó correctamente la instancia de la tarea |
| 103 y 203 | El Programador de tareas no puede iniciar la instancia, o la acción, de la tarea |
| 201 | El Programador de tareas completó correctamente la tarea, la instancia y la acción con el código devuelto |
| 322 | No inició la tarea porque ya se está ejecutando otra instancia de la misma |
| 329 | Cerró la instancia porque superó el tiempo asignado para ejecutarse |
| 332 | No 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 ve | Causa probable | Solución |
|---|---|---|
Estado Queued y no arranca, en un portátil | Configuración de fábrica: no arranca con batería | -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries |
| Funciona al ejecutarla a mano, pero no de noche | La 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 apagado | Sin -StartWhenAvailable, la ejecución perdida no se recupera | Activar -StartWhenAvailable |
| Resultado 0x80070002 | No encuentra el programa: nombre sin ruta, o PowerShell 7 de Microsoft Store | Ruta completa en -Execute |
| Resultado 0xFFFD0000 o 64 | No encuentra el script, o la ruta tiene espacios sin comillas | Ruta completa entre comillas dobles después de -File |
| Resultado 0, pero el script no hizo nada | Error sin Stop, o ruta relativa sin -WorkingDirectory | $ErrorActionPreference = 'Stop', -WorkingDirectory y un registro propio |
| Resultado 267014 | Superó el tiempo máximo | Revisar por qué tarda y ajustar -ExecutionTimeLimit |
| No arranca y hay un evento 322 | La ejecución anterior sigue en marcha | Buscar por qué se cuelga y poner un tiempo máximo |
Acceso denegado. al registrarla | SYSTEM, Highest, al arrancar o al iniciar sesión de cualquier usuario sin elevar | Abrir PowerShell como administrador |
| Error 0x80041318 al registrarla | -RepetitionDuration ([TimeSpan]::MaxValue) | Omitir la duración |
| Funciona con una cuenta y falla con otra | La otra cuenta no tiene permiso sobre las carpetas o la red | Probar 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-Filey dentro del script, con-WorkingDirectorysiempre. - 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,
-StartWhenAvailabley un tiempo máximo realista. - El script, honesto.
$ErrorActionPreference = 'Stop',tryycatch, un registro propio yexit 0oexit 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-ScheduledTasky 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.




