How to Upload Files Over FTP With PowerShell

The File Transfer Protocol (FTP) is a common service used to transfer files between clients and servers. It’s often useful to automate these file transfers, and PowerShell scripts can come in handy to speed up this process.

How To Use FTP in PowerShell

There are a few different ways to make FTP transfers in PowerShell.

The simplest is to use WebClient.UploadFile. PowerShell is an object-oriented scripting language, and you have full access to .NET standard libraries with New-Object. With this, you can create a new WebClient, set the credentials for it, and upload a file.

$client = New-Object System.Net.WebClient
$client.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$client.UploadFile("ftp://example.com/path/archive.zip", "C:\archive.zip")

This will work fine, but won’t be able to handle TLS/SSL encrypted requests, or make “active” FTP transfers. Using FtpWebRequest, covered below, will solve this problem.

It isn’t best practice to store your username and password in a script however, especially if that script is being committed to a shared Git repository. You can instead set environment variables like FtpUsername and access them in the script.

function uploadToFTPServer($remote, $local) {
    $client = New-Object System.Net.WebClient
    $client.Credentials = New-Object System.Net.NetworkCredential($Env:FtpUsername, $Env:FtpPassword)
    $client.UploadFile($remote, $local)
}

uploadToFTPServer "ftp://example.com/path/archive.zip", "C:\archive.zip"

Making this into a function will also let you easily make multiple transfers by calling the function with different parameters.

Advanced FTP Transfers with PowerShell

If you need more control, you should use FtpWebRequest. This will support TLS transfers, and also allow you to turn passive mode off. The easiest way to use it is to open a file stream and copy it to the FTP stream.

function uploadToFTPServer($remote, $local) {
    $request = [System.Net.FtpWebRequest]::Create($remote)
    $request.Credentials = New-Object System.Net.NetworkCredential($Env:FtpUsername, $Env:FtpPassword)
    $request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
    $request.UsePassive = $true

    $fileStream = [System.IO.File]::OpenRead($local)
    $ftpStream = $request.GetRequestStream()

    $fileStream.CopyTo($ftpStream)

    $ftpStream.Dispose()
    $fileStream.Dispose()
}

uploadToFTPServer "ftp://example.com/archive.zip" "C:\archive.zip"

Since it’s using file streams, and not reading all the bytes, this has the benefit of working better with huge file transfers.

Sending SFTP Transfers with Posh-SSH

SFTP is an alternate FTP protocol that operates over SSH. It’s a bit more complicated to use than regular FTP, since you can’t just send a username and password, and it’s not supported by native PowerShell.

You’ll need to install the Posh-SSH package to communicate over SFTP:

Install-Module -Name Posh-SSH

Then you’ll be able to start a new session, using a new credential object. This works in the same way as web request transfers, except you’ll also need to close the session at the end.

Import-Module Posh-SSH

$Password = ConvertTo-SecureString $Env:FtpPassword -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($Env:FtpUsername, $Password)

$FilePath = "C:\archive.zip"
$SftpPath="/folder"

$ThisSession = New-SFTPSession -ComputerName '1.2.3.4' -Credential $Credential

Set-SFTPFile -SessionId ($ThisSession).SessionId -LocalFile $FilePath -RemotePath $SftpPath
Get-SFTPSession | % { Remove-SFTPSession -SessionId ($_.SessionId) }

Sending Huge Files with a Progress Bar in PowerShell

Using the file stream’s CopyTo is simple, but for long transfers, you may want some kind of progress monitoring. This is a little complicated to add, since you’ll have to copy the streams yourself, but works well with the following script:

$request = [Net.WebRequest]::Create("ftp://example.com/path/archive.zip")
$request.Credentials =
New-Object System.Net.NetworkCredential($Env:FtpUsername, $Env:FtpPassword)
$request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile

$fileStream = [System.IO.File]::OpenRead("C:\archive.zip")
$ftpStream = $request.GetRequestStream()

$buffer = New-Object Byte[] 10240
while (($read = $fileStream.Read($buffer, 0, $buffer.Length)) -gt 0)
{
    $ftpStream.Write($buffer, 0, $read)
    $pct = ($fileStream.Position / $fileStream.Length)
    Write-Progress `
        -Activity "Uploading" -Status ("{0:P0} complete:" -f $pct) `
        -PercentComplete ($pct * 100)
}

$ftpStream.Dispose()
$fileStream.Dispose()