Nessuna descrizione

Blance 8505e75757 Add local archive and rename-after-upload options 1 giorno fa
README.md 8505e75757 Add local archive and rename-after-upload options 1 giorno fa
Send-FilesToSftp.ps1 8505e75757 Add local archive and rename-after-upload options 1 giorno fa

README.md

Send-FilesToSftp.ps1

PowerShell script to transfer files to an SFTP server with regex filtering, secure credential handling, and logging.

Prerequisites

WinSCP .NET Assembly (WinSCPnet.dll) is required. Install using one of these methods:

Method Steps
Drop-in Download the .NET assembly package, extract WinSCPnet.dll next to the script
NuGet Install-Package WinSCP -Source nuget.org
WinSCP Installer Install WinSCP and check the ".NET assembly" option during setup

The script auto-searches these locations in order:

  1. -WinScpDllPath parameter (if provided)
  2. Same directory as the script
  3. lib\ subdirectory of the script folder
  4. C:\Program Files (x86)\WinSCP\
  5. C:\Program Files\WinSCP\
  6. NuGet package cache (~\.nuget\packages\winscp)

Quick Start

# Basic usage — prompted for password interactively
.\Send-FilesToSftp.ps1 -LocalPath "C:\exports" -RemotePath "/incoming" `
    -HostName "sftp.example.com" -UserName "uploader"

Parameters

Parameter Required Default Description
-LocalPath Yes Local source folder to scan
-RemotePath Yes Remote SFTP destination folder
-HostName Yes SFTP server hostname or IP
-UserName Yes SFTP username
-FileFilter No .* Regex pattern to match filenames
-Port No 22 SFTP port
-Credential No PSCredential object
-CredentialFile No Path to saved credential XML (see below)
-KeyFilePath No Path to SSH private key (.ppk)
-SshHostKeyFingerprint No SSH host key fingerprint for verification
-RenamePattern No Regex pattern to match in filename for renaming on the remote side before upload
-RenameReplacement No Replacement string for -RenamePattern (supports capture groups like $1)
-ArchivePath No Move successfully uploaded files to this local folder (created if missing). Cannot combine with -DeleteAfterTransfer
-LocalRenamePattern No Regex pattern to rename the local file after upload (in place, or into -ArchivePath)
-LocalRenameReplacement No Replacement string for -LocalRenamePattern (supports capture groups like $1)
-Recurse No false Scan subdirectories
-DeleteAfterTransfer No false Delete local files after successful upload. Cannot combine with -ArchivePath
-DryRun No false Preview transfers without uploading
-LogFile No Path to log file (logs to console if omitted)
-WinScpDllPath No Explicit path to WinSCPnet.dll

Authentication

The script supports three auth methods, checked in this order:

1. Credential Object (interactive or pipeline)

$cred = Get-Credential
.\Send-FilesToSftp.ps1 -LocalPath "C:\data" -RemotePath "/uploads" `
    -HostName "sftp.example.com" -UserName "brad" -Credential $cred

2. Saved Credential File (unattended / scheduled tasks)

# One-time setup — run as the same user that will run the scheduled task
Get-Credential | Export-Clixml -Path "C:\secure\sftp_cred.xml"

# Use in script
.\Send-FilesToSftp.ps1 -LocalPath "C:\data" -RemotePath "/uploads" `
    -HostName "sftp.example.com" -UserName "svc_upload" `
    -CredentialFile "C:\secure\sftp_cred.xml"

Note: Export-Clixml encrypts credentials using Windows DPAPI, tied to the user account and machine that created the file. Only that same user on that same machine can decrypt it.

3. SSH Private Key

.\Send-FilesToSftp.ps1 -LocalPath "C:\data" -RemotePath "/uploads" `
    -HostName "sftp.example.com" -UserName "svc_upload" `
    -KeyFilePath "C:\keys\id_rsa.ppk"

4. Interactive Prompt

If none of the above are provided and no key file is specified, you'll be prompted for a password at runtime.

File Filtering (Regex)

The -FileFilter parameter uses PowerShell regex (case-insensitive by default).

Filter Matches
'\.csv$' All CSV files
'\.xlsx?$' .xls and .xlsx files
'^report_\d{8}' Files starting with report_ + 8 digits (e.g. report_20260415.csv)
'(?-i)^Data' Files starting with Data (case-sensitive)
'badge.*\.xlsx$' Excel files with badge anywhere in the name
'\.(csv\|txt)$' CSV or TXT files
'.*' Everything (default)

Usage Examples

Transfer all CSVs

.\Send-FilesToSftp.ps1 -LocalPath "C:\exports" -RemotePath "/incoming" `
    -HostName "sftp.example.com" -UserName "uploader" -FileFilter '\.csv$'

Move files (delete after upload) with logging

.\Send-FilesToSftp.ps1 -LocalPath "C:\exports" -RemotePath "/incoming" `
    -HostName "sftp.example.com" -UserName "svc_upload" `
    -CredentialFile "C:\secure\cred.xml" `
    -DeleteAfterTransfer -LogFile "C:\logs\sftp_transfer.log"

Dry run — preview without transferring

.\Send-FilesToSftp.ps1 -LocalPath "C:\exports" -RemotePath "/incoming" `
    -HostName "sftp.example.com" -UserName "uploader" -DryRun

Recursive with subdirectories

.\Send-FilesToSftp.ps1 -LocalPath "C:\data\projects" -RemotePath "/archive" `
    -HostName "sftp.example.com" -UserName "uploader" `
    -Recurse -FileFilter '\.pdf$'

Local Archive & Rename After Upload

After a successful upload you can archive or rename the local source file. These are mutually exclusive with -DeleteAfterTransfer.

Archive to a folder

.\Send-FilesToSftp.ps1 -LocalPath "C:\exports" -RemotePath "/incoming" `
    -HostName "sftp.example.com" -UserName "uploader" `
    -ArchivePath "C:\exports\sent"

Archive and rename while archiving (e.g. add _sent before extension)

.\Send-FilesToSftp.ps1 -LocalPath "C:\exports" -RemotePath "/incoming" `
    -HostName "sftp.example.com" -UserName "uploader" `
    -ArchivePath "C:\exports\sent" `
    -LocalRenamePattern '^(.+?)(\.[^.]+)$' -LocalRenameReplacement '${1}_sent${2}'

Rename local file in place (no archive, no delete)

.\Send-FilesToSftp.ps1 -LocalPath "C:\exports" -RemotePath "/incoming" `
    -HostName "sftp.example.com" -UserName "uploader" `
    -LocalRenamePattern '^' -LocalRenameReplacement 'done_'

-LocalRenamePattern/-LocalRenameReplacement only rename the local file — the remote name is controlled by -RenamePattern/-RenameReplacement. You can use both together.

Renaming Files on the Remote Side

Use -RenamePattern (regex) and -RenameReplacement together to rename files on the remote side without touching the local files.

Goal Pattern Replacement
Add date before extension '^(.+?)(\.[^.]+)$' '${1}_20260416${2}'
Add prefix '^' 'processed_'
Add suffix before extension '^(.+?)(\.[^.]+)$' '${1}_done${2}'
Strip _draft from name '_draft' ''
Replace spaces with underscores ' ' '_'

Rename uses PowerShell's -replace operator (regex, case-insensitive). The local file is not modified — only the remote destination path changes.

Add today's date to every filename

$date = Get-Date -Format 'yyyyMMdd'
.\Send-FilesToSftp.ps1 -LocalPath "C:\exports" -RemotePath "/incoming" `
    -HostName "sftp.example.com" -UserName "uploader" `
    -RenamePattern '^(.+?)(\.[^.]+)$' -RenameReplacement "${date}_`$1`$2"

Tip: Use -DryRun first to preview the renamed remote paths before committing to the transfer.

SSH Host Key Fingerprint

For production use, always provide the host key fingerprint to prevent MITM attacks:

# Get the fingerprint from the server
ssh-keyscan sftp.example.com | ssh-keygen -lf -

# Use it in the script
.\Send-FilesToSftp.ps1 -LocalPath "C:\data" -RemotePath "/uploads" `
    -HostName "sftp.example.com" -UserName "uploader" `
    -SshHostKeyFingerprint "ssh-rsa 2048 aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99"

Pass "*" to accept any key (development/testing only — not recommended for production).

Scheduled Task Setup

To run unattended via Windows Task Scheduler:

  1. Save credentials as the service account user:

    Get-Credential | Export-Clixml -Path "C:\secure\sftp_cred.xml"
    
  2. Create the scheduled task:

    $action = New-ScheduledTaskAction `
       -Execute "powershell.exe" `
       -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\scripts\Send-FilesToSftp.ps1" -LocalPath "C:\exports" -RemotePath "/incoming" -HostName "sftp.example.com" -UserName "svc_upload" -CredentialFile "C:\secure\sftp_cred.xml" -FileFilter "\.csv$" -DeleteAfterTransfer -LogFile "C:\logs\sftp.log"'
    
    $trigger = New-ScheduledTaskTrigger -Daily -At "2:00AM"
    
    Register-ScheduledTask -TaskName "SFTP Upload" `
       -Action $action -Trigger $trigger `
       -User "DOMAIN\svc_upload" -Password "****" `
       -RunLevel Highest
    

Exit Codes

Code Meaning
0 All files transferred (or no files matched filter)
1 One or more failures occurred

Log Output

Logs include timestamps and severity levels. Sample output:

[2026-04-16 14:30:01] [INFO] ═══ SFTP Transfer Starting ═══
[2026-04-16 14:30:01] [INFO] Local path  : C:\exports
[2026-04-16 14:30:01] [INFO] Remote path : /incoming
[2026-04-16 14:30:01] [INFO] File filter : \.csv$
[2026-04-16 14:30:01] [INFO] Found 3 file(s) matching filter
[2026-04-16 14:30:02] [SUCCESS] Connected to sftp.example.com
[2026-04-16 14:30:03] [SUCCESS] Transferred: report_20260415.csv → /incoming/report_20260415.csv  (42.3 KB)
[2026-04-16 14:30:03] [SUCCESS] Transferred: badges_export.csv → /incoming/badges_export.csv  (18.1 KB)
[2026-04-16 14:30:04] [SUCCESS] Transferred: access_log.csv → /incoming/access_log.csv  (7.8 KB)
[2026-04-16 14:30:04] [INFO] ═══ Transfer Complete ═══
[2026-04-16 14:30:04] [INFO]   Succeeded : 3
[2026-04-16 14:30:04] [INFO]   Mode      : COPY (source files retained)