How to upload files via FTP with PowerShell

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 be helpful in speeding up this process.
How to use FTP in PowerShell
There are several ways to perform FTP transfers in PowerShell.
The easiest way 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 its credentials 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 will not be able to handle TLS/SSL encrypted requests, nor do “active” FTP transfers. Using FtpWebRequest
covered below, will solve this problem.
However, storing your username and password in a script is not recommended, especially if that script is committed to a shared Git repository. You can instead set environment variables like FtpUsername
and access it 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 it a function will also allow you to easily perform 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 disable passive mode. 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 uses file streams and doesn’t read every byte, it has the advantage of working better with huge file transfers.
Sending SFTP transfers with Posh-SSH
SFTP is an alternative FTP protocol that works over SSH. It’s a bit trickier to use than regular FTP, because you can’t just send a username and password, and it’s not supported by native PowerShell.
You will need to install the Posh-SSH
package to communicate via SFTP:
Install-Module -Name Posh-SSH
Then you can start a new session, using a new credential object. It works the same way as web request forwards, except you’ll also need to log off when it’s done.
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 large files with a progress bar in PowerShell
Using File Stream CopyTo
is simple, but for long transfers you may want some kind of progress tracking. It’s a bit tricky to add, since you’ll have to copy the feeds 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()