No description
  • PowerShell 100%
Find a file
2026-10-12 19:15:18 +02:00
.gitignore delete some old files, only original script in repo 2026-10-12 19:15:18 +02:00
README.md add env variable setting to readme 2026-10-12 16:19:54 +02:00
transfer.ps1 separated upload folder 2026-10-12 16:17:54 +02:00

DML → MMB SFTP transfer (transfer.ps1)

transfer.ps1 uploads files from a network share to the MMB SFTP server (sftp.mmb-media.de, directory /Export/XML/ or /Export/Transit/ depending on the mode), moves each uploaded file to a backup folder, and writes a CSV log. It is meant to run as a scheduled task every 5 minutes.

Usage

.\transfer.ps1 -Mode XML
.\transfer.ps1 -Mode Transit

-Mode is required. Any value other than XML or Transit is rejected before the script runs.

Mode Source folder Remote directory Files uploaded Old backups deleted
XML \\omc.oneds.com\Onefs\DE-OMG\ODE\Internal\MMB\XML /Export/XML/ *.xml *.xml
Transit \\omc.oneds.com\Onefs\DE-OMG\ODE\Internal\MMB\Transit /Export/Transit/ all files all files

The remote directories must already exist on the server; the script does not create them.

Hidden files and subfolders of the source folder are not uploaded.

Requirements

  • PowerShell 7 (pwsh.exe). The script was tested with it.
  • The Posh-SSH module, tested with version 4.0.0. The script uses the SSH.NET library that ships with it.
  • The private key file ($SFTPKeyFile), readable by the account that runs the script.
  • Read/write access to the source and backup shares.
  • Write access to the log folder C:\OMG_IT\Scripts_DML-MMB\log\.

What a run does

  1. Writes a start line to the log.
  2. Stops with exit code 0 if another run of the same mode is still active. XML and Transit do not block each other.
  3. Deletes files in the backup folder that are older than 14 days ($deletePeriod).
  4. Collects the files to upload from the source folder. Files changed within the last 2 minutes ($minFileAgeMinutes) are skipped, because they may still be written. They are picked up by a later run.
  5. If there are no files, logs No files found to transfer and ends with exit code 0.
  6. Connects to the SFTP server (see Connection).
  7. For each file:
    • uploads it as <name>.filepart and renames it to <name> when the upload is complete, so the server never sees a partial file. An existing remote file with the same name is replaced.
    • moves the local file to the backup folder as <name>_<yyyyMMddHHmmss>.<ext>.
    • a failed upload or move is logged and does not stop the remaining files. A file whose upload failed stays in the source folder and is retried on the next run.
  8. Disconnects and writes an end line with the exit code.

Settings

All settings are at the top of transfer.ps1.

Setting Meaning
$DryRun $true = test run without changes, see Dry run. Set to $false for production.
$SFTPSite, $SFTPPort SFTP server and port.
$SFTPUser SFTP user name.
$SFTPPassword Password for the server's password prompt. Read from the environment variable MMB_SFTP_PASSWORD.
$SFTPKeyFile Path to the private key file.
$SFTPKeyPass Passphrase of the private key file. Read from the environment variable MMB_SFTP_KEYPASS.
$expectedFingerprint MD5 fingerprint of the server's host key (colon-separated hex).
$modeSettings Source folder (LocalDirectory), file filter (FileFilter) and remote target directory (SFTPDirectory) per mode.
$backupPath Backup folder, shared by both modes.
$deletePeriod Age in days (negative) after which backups are deleted. Default -14.
$minFileAgeMinutes Minimum age of a file before it is uploaded. Default 2.
$TransferLogfile CSV log file.

Secrets

The script contains no password. It reads the SFTP password and the key passphrase from two environment variables:

Variable Content
MMB_SFTP_PASSWORD Password for the server's password prompt.
MMB_SFTP_KEYPASS Passphrase of the private key file.

Setting the variables on the Windows server

The variables are set once, as user variables of each Windows account that runs the script: the scheduled task's account, and your own user for tests.

  1. Open PowerShell as that account. Either log on to the server with it, or start a shell under it from your own session:

    runas /user:DOMAIN\<account> pwsh.exe
    
  2. Set both variables in that window:

    [Environment]::SetEnvironmentVariable('MMB_SFTP_PASSWORD', '<password>', 'User')
    [Environment]::SetEnvironmentVariable('MMB_SFTP_KEYPASS',  '<passphrase>', 'User')
    

    Use single quotes around the values, so characters like $ are not interpreted. If a value contains a single quote, double it ('').

  3. The variables only apply to processes started afterwards. Open a new PowerShell window as the same account and check that both are set (this prints True twice without showing the values):

    [bool]$env:MMB_SFTP_PASSWORD; [bool]$env:MMB_SFTP_KEYPASS
    

Alternatively, logged on as that account, run rundll32 sysdm.cpl,EditEnvironmentVariables and add both under "User variables".

Do not use the 'Machine' scope instead of 'User': every account on the server could then read the secrets.

The values are stored unencrypted in that user's Windows profile. The user and administrators of the server can read them.

If the scheduled task still reports a missing variable after the setup, log the task account off and on, or restart the server.

If a variable is missing, the script logs environment variable … is not set and ends with exit code 2.

Dry run

With $DryRun = $true the script:

  • connects and logs in to the real SFTP server, including the host key check,
  • checks that the remote directory exists,
  • logs each file it would upload (with remote path and backup name) and each backup file it would delete,
  • uploads, renames, moves and deletes nothing.

Dry-run lines are tagged XML-DRYRUN or TRANSIT-DRYRUN in the log and are also printed to the console.

Connection

The server requires two login steps: the private key, then a password prompt (keyboard-interactive). The script also checks the server's host key against $expectedFingerprint and refuses to connect if it differs.

The connection is built by a small C# class (MmbSftpHelper) that the script compiles at runtime with Add-Type. This is needed for two reasons:

  • Posh-SSH's New-SFTPSession cannot answer the password prompt after the key login.
  • SSH.NET calls the host key check and the password prompt on its own threads, where PowerShell script blocks fail with "There is no Runspace available to run scripts in this thread".

To check the server's fingerprint independently:

ssh-keyscan -t ed25519 sftp.mmb-media.de > mmb.key
ssh-keygen -l -E md5 -f mmb.key

The output must show the value of $expectedFingerprint.

Log file

C:\OMG_IT\Scripts_DML-MMB\log\Transfer.csv, delimiter ;, created with a header if it does not exist:

date;time;day;script;module;information;error
09.10.2026;11:06:05;Friday;XML;SFTP connect;connected to sftp.mmb-media.de as omcpush
  • script: the mode (XML, TRANSIT), with -DRYRUN in a dry run.
  • module: the step (script engine, Backup cleanup, Files Source, Powershell Module Posh-SSH, SFTP connect, SFTP upload, Backup move).
  • error: the error message, if the step failed.

Both modes write to the same file. If the file is locked, a log line is retried up to 5 times.

Exit codes

Code Meaning
0 Success, nothing to transfer, or another run of the same mode was still active.
2 An error occurred (connection, upload or backup move). Details are in the log.

Scheduled task

Create one task per mode, every 5 minutes, running as the account that owns the key file and has access to the shares:

Program:   pwsh.exe
Arguments: -NoProfile -NonInteractive -File "C:\OMG_IT\Scripts_DML-MMB\transfer.ps1" -Mode XML
Program:   pwsh.exe
Arguments: -NoProfile -NonInteractive -File "C:\OMG_IT\Scripts_DML-MMB\transfer.ps1" -Mode Transit

-NonInteractive makes the task fail at once if -Mode is missing, instead of waiting for input.

Troubleshooting

Log message Cause and fix
Host key fingerprint mismatch: expected …, got … The server's host key differs from $expectedFingerprint. Confirm the new key with MMB before changing the setting.
environment variable … is not set The variable is missing for the user running the script, see Secrets. For a scheduled task that still reports this after the setup, log the task account off and on, or restart the server.
Padding is invalid and cannot be removed The key file could not be decrypted: MMB_SFTP_KEYPASS is wrong.
Permission denied / authentication error Wrong user, key or MMB_SFTP_PASSWORD, or the key is not registered on the server.
error CS0012 … netstandard or other Add-Type errors The C# helper could not be compiled. Check the PowerShell and Posh-SSH versions against Requirements.
previous run still active, skipping A run of the same mode is still in progress. Normal for large transfers; check for a hanging process if it repeats.
Move of … failed The file was uploaded but could not be moved to backup (e.g. locked). It stays in the source folder and is uploaded again on the next run.