Powershell FTP Script Error: The remote server returned an error: 227 Entering Passive Mode

ftpipv4powershellscripting

I am having Powershell FTP script problems. I have pulled my hair out over this one.

I recently setup an "automated build server" for my software projects. All software projects get built and bundled into a zip file. In theory, this zip file will be downloaded via FTP to several different servers. I am trying to build a powershell script that will download my package and unzip it into the desired directories.

The part I am having problem with is downloading the file via FTP. I am able to access the source file by manually logging into FTP via DOS command window. I can download the file just fine, so I know this is not a firewall issue. Atleast I dont think it is.

Whenever powershell starts to download the source .zip file, it hangs. I can see the file appear on the harddrive, but then disappears when the script fails. It never gets any bytes from the FTP server. I then receive this error:

Powershell FTP Script Error: The remote server returned an error: 227 Entering Passive Mode (10,255,130,77,231,5)
Blockquote

Here is my script:

#create destination directory (this is where the build files will be dropped)
[IO.Directory]::CreateDirectory("C:\tempftp")

"Creating staging folder..."
#create staging folder for deployment package bundle unzip
[IO.Directory]::CreateDirectory("C:\FINALDEPLOYMENTFOLDER")

#create destination and source paths for FTP copy command
$File = "C:\tempftp\MyPackageT.zip"
$ftp = "ftp://AdminiUser:adm1n1pwd@255.255.255.255/MyPackage.zip"

"ftp url: $ftp"

$webclient = New-Object System.Net.WebClient
$uri = New-Object System.Uri($ftp)

"Downloading $File..."

#perform the actual data transfer via FTP
$webclient.DownloadFile($ftp, $File)

"Unzip build package..."
#unzip the file that was transferred via FTP
$shell_app=new-object -com shell.application 
$zip_file = $shell_app.namespace("C:\tempftp\MyPackage.zip")         #source
$destination = $shell_app.namespace("C:\FINALDEPLOYMENTFOLDER")       #final destination

#unzip to destination folder
foreach ($item in $zip_file.Items())
{
$destination.CopyHere($item, 16)
}

I am able to run this script, locally, on my build server and it runs fine. I know this script works. My trouble is, why does this script barf on my non-build servers? Why is Passive Mode such a problem for this scenario and how do I fix it? Ive tried FTP IPv4 Address rules, FTP Firewall support changes, etc. But no success.

Noteworthy, I do see a warning message on my FTP Firewall Support section of IIS7.
It states:

To accept passive connections when you are using FTP over SSL (FTPS) or when your firewall does not filter packets, configure the external IPv4 address of your firewall.

^ I am unsure what that really means, but I will give it a shot. Any ideas why Passive Mode is such a pain in my neck? Should I not be using WebClient? I really want to avoid having to download another program/patch to make this work. Id rather this work in native Powershell…so no, I cant use FTP Client libraries!!

**** EDIT: I was able to fix this issue by opening "All Ports" on my firewall, but that seems a bit scary. Any suggestions on tracking down what ports are needed so that this Passive Mode error disappears? I already had a rule allowing a few specific ports I thought the FTP was using….but how can I see which ports I need to enable?**

Best Answer

When you connect to the FTP site from a command prompt, you're using active FTP (only one available) which is far more predictable on the part of the firewall. When you connect using the System.Net.WebClient, you are connecting using PASV which takes a random port from a predefined available range. These requests are getting stopped at your firewall. You could also try opening up the range of ports on the firewall if you have to stick with PASV. Otherwise, you need to figure out how to specify to use an active FTP connection. This cannot be done using System.Net.WebClient.

Take a look at System.Net.FtpWebRequest, it may do what you want. I see there is a UsePassive property.