Automatizar transferencias SFTP cada 15 min con WinSCP, PowerShell y el Programador de tareas

Automatizar la transferencia de archivos entre un servidor SFTP y un servidor Linux desde Windows es más fácil de lo que parece. Con WinSCP, PowerShell y el Programador de tareas puedes crear un flujo confiable que mueva datos cada 15 min sin intervención humana.

Índice

Visión general

En entornos mixtos donde conviven sistemas Windows y Linux, la sincronización automática de archivos es esencial para procesos de integración, respaldo o ingestión de datos. Este artículo detalla paso a paso cómo:

  • Crear un script en PowerShell que use WinSCP para mover (o copiar) archivos de \\sftp3\sftp\packnet (/mnt/sftp2/packnet en el servidor) a /root/mercury/packnet.
  • Registrar ese script en el Programador de tareas de Windows para que se ejecute cada 15 min.
  • Aplicar buenas prácticas de seguridad, registro y mantenimiento.

Requisitos previos

  • Windows 10/11, Windows Server 2016 o posterior.
  • WinSCP instalado (incluye: WinSCP.exe, WinSCP.com y WinSCPnet.dll).
  • PowerShell 5.1 o PowerShell 7 + (compatible con .NET Standard).
  • Credenciales SFTP con permisos de lectura y eliminación en /mnt/sftp2/packnet y escritura en /root/mercury/packnet.
  • Cuenta de servicio local o de dominio con permiso de «Log on as a batch job». La tarea se ejecutará con este usuario.

Creación del script de PowerShell

Guarda el archivo como C:\Scripts\WinSCP_Copy.ps1. Dos enfoques son válidos: uno rápido con el intérprete de comandos y otro robusto con la biblioteca .NET.

Método rápido: intérprete de comandos de WinSCP

Ideal para tareas simples. El propio script genera un bloque de órdenes y lo envía a WinSCP.com en memoria:

$script = @"
option batch on
option confirm off
open sftp://usuario:contraseña@sftp3
mv /mnt/sftp2/packnet/* /root/mercury/packnet/
exit
"@
$winscp = "C:\Program Files (x86)\WinSCP\WinSCP.com"
$script | & $winscp /script=-

Detalles clave:

  • option batch on deshabilita cualquier pregunta interactiva.
  • option confirm off evita confirmaciones al sobrescribir o eliminar.
  • mv mueve y borra la fuente. Sustitúyelo por get (download) o put (upload) si deseas copiar en lugar de trasladar.
  • El uso de /script=- permite pasar el script directamente vía stdin, sin crear archivos temporales.

Método robusto: biblioteca .NET de WinSCP

Recomendado si necesitas validación granular, reintentos o integración con otros módulos.

Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"

\$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = \[WinSCP.Protocol]::Sftp
HostName = "sftp3"
UserName = "usuario"
Password = "contraseña"
\# Si usas clave privada:
\# SshPrivateKeyPath = "C:\Keys\id\_rsa.ppk"
\# GiveUpSecurityAndAcceptAnySshHostKey = \$true  # SOLO LAB / NO PRODUCCIÓN
}

\$session = New-Object WinSCP.Session
try {
\$session.Open(\$sessionOptions)
\# Mueve todos los archivos y conserva estructura
\$transferResult = \$session.MoveFile("/mnt/sftp2/packnet/\*", "/root/mercury/packnet/")
if (\$transferResult.IsSuccess) {
Write-Host "Transferencia completada: \$(\$transferResult.Transfers.Count) elementos."
}
else {
foreach (\$err in \$transferResult.Failures) {
Write-Warning "Error: \$(\$err.Message)"
}
throw "Se detectaron fallos en la transferencia."
}
}
finally {
\$session.Dispose()
} 

La clase Session expone GetFiles, PutFiles, MoveFile, RemoveFiles y más, lo que brinda máxima flexibilidad.

Ubicación y permisos del script

  • Aloja los scripts en C:\Scripts o en un recurso compartido con control de versiones (Git, DevOps).
  • Otorga a la cuenta que ejecutará la tarea permisos «Lectura y Ejecución» sobre la carpeta y «Lectura y Escritura» en la ruta donde se ubiquen los logs.
  • Si el script almacena contraseñas en texto claro, restringe su lectura a usuarios administradores o, mejor aún, emplea el Windows Credential Manager.

Configuración de la tarea programada

Desde la Consola del Programador de tareas (taskschd.msc) crea una nueva tarea con la siguiente configuración:

ElementoValor recomendado
NombreTransferencia SFTP Packnet
Usuariosvc_winscp (cuenta de servicio)
Ejecutar si el usuario inició sesiónNo (marca «Ejecutar tanto si…»)
Privilegios«Ejecutar con los privilegios más altos»
DesencadenadorDiario a 00:00 → Repetir cada 15 min (Indefinidamente)
Programa/scriptpowershell.exe
Argumentos-ExecutionPolicy Bypass -File "C:\Scripts\WinSCP_Copy.ps1"
Iniciar enC:\Scripts
Detener si sobrepasa30 min (evita solapamiento)
ReintentosReintentar cada 5 min, 3 veces

Nota: Usar -ExecutionPolicy Bypass permite ejecutar el script sin firmar. En entornos regulados, firma el script y emplea AllSigned.

Pruebas y validación

  1. Abre una consola de PowerShell como el mismo usuario del Programador de tareas y ejecuta manualmente el script: .\WinSCP_Copy.ps1 -Verbose. Verifica que no aparezcan cuadros de diálogo y que los archivos se muevan.
  2. Consulta los logs de WinSCP, si los configuraste (/log=C:\Logs\winscp_$(Get-Date -Format yyyyMMdd).log).
  3. En el Programador, usa «Ejecutar» y examina el Historial. El código de salida (Column «Resultado de la última ejecución») debe ser 0x0.
  4. Revisa que el tiempo total de ejecución sea menor a 15 min; si no, ajusta la frecuencia.

Buenas prácticas y depuración

Registrar salida detallada

$logPath = "C:\Logs\winscp$(Get-Date -Format yyyyMMddHHmmss).log"
$scriptBlock = {
    param([string]$innerScript)
    $innerScript | & "C:\Program Files (x86)\WinSCP\WinSCP.com" /log=$logPath /script=-
}
$script | & $scriptBlock

Gestión de errores

  • Para capturar excepciones en la versión .NET, envuelve la lógica en try / catch y retorna códigos de error personalizados (exit 10).
  • Con WinSCP.com agrega option failonnomatch off para ignorar un directorio vacío.
  • Si usas clave SSH, valida huella digital del host para evitar MITM. Ejemplo: SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx...".

Evitar solapamiento de instancias

Si la carga de archivos es impredecible y una ejecución puede extenderse, utiliza:

Register-ScheduledTask -TaskName "Transferencia SFTP Packnet" `
    -Settings (New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew)

Automatización avanzada y escalabilidad

  • Variables de entorno: almacena $env:SFTPUSER y $env:SFTPPASS para no hardcodear credenciales.
  • JSON de parámetros: carga rutas y filtros desde un archivo (config.json) y reutiliza el mismo script para múltiples tareas.
  • Integración continua: añade pruebas Pester que verifiquen que las rutas son accesibles y que WinSCP devuelve 0.
  • Notificaciones: envía correo mediante Send-MailMessage cuando el número de archivos transferidos sea 0 durante cierto tiempo.
  • Compatibilidad con FTPS: cambia Protocol = 'Ftp' y añade TlsHostCertificateFingerprint para conexiones FTPS explícitas.

Preguntas frecuentes

¿Puedo copiar en lugar de mover?
Sí. Sustituye mv por get o put. En la API .NET usa GetFiles/PutFiles.

¿Cómo uso autenticación por clave?
Convierte tu clave privada a formato .ppk con PuTTYgen y añade SshPrivateKeyPath. Si tu clave tiene frase de paso, agrega PrivateKeyPassphrase.

¿Qué ocurre si el directorio está vacío?
Añade option failonnomatch off o comprueba con $session.FileExists antes de mover. Sin esta opción, WinSCP finaliza con código 1 y la tarea marcará error.

¿Se pueden transferir subcarpetas?
Sí. Usa comodines recursivos (/) o -filemask=" >0" en el intérprete. En la API .NET ajusta TransferOptions con $opts.FileMask y $opts.PreserveTimestamp.

¿Cómo rotar los logs?
Incluye la fecha en el nombre del archivo (como en el ejemplo) o ejecuta un script secundario que elimine los registros con antigüedad > 30 días.

Conclusión

Con un script de PowerShell bien estructurado, WinSCP como motor SFTP y el Programador de tareas de Windows como orquestador, obtienes una solución estable y flexible para mover archivos cada 15 min o con la frecuencia que tu flujo de negocio requiera. Invierte tiempo en pruebas y registro; la automatización perfecta no es la que nunca falla, sino la que avisa a tiempo y se autorecupera.

Índice