PowerShell ftp get won’t save on Windows Server

ftppowershellwindows-server-2012windows-server-2012-r2

From a Windows Server 2012, using PoweShell, I connect to a remote device through ftp and run get to retrieve a file. The process goes through with no problems but the file doesn't get saved on to my local machine. The command returns Operation Complete and after a few seconds the connection gets closed.

Operation CompleteConnection closed by remote host.
ftp> 

On the destination location a temporary file of size 0 gets created at the beginning of the process and it remains unchanged. Tmp6A94.tmp

I have tried to open up the firewall following this How to Configure Windows Firewall for a Passive Mode FTP Server

netsh advfirewall firewall add rule name=”FTP Service” dir=in protocol=TCP enable=yes action=allow profile=any service=ftpsvc localport=any 
netsh advfirewall set global StatefulFTP disable

What am I missing?

EDIT 1

I have tested the ftp behavior on another WS2012 and also on a WS2012R2 and the two do not have the same issue. None of them have the firewall ftp passive mode. I wonder if there might be some other firewall rule that enables the ftp transfer.

EDIT 2

This is the PowerShell script I use to retrieve the file from the remote device through ftp:

function getFTPFile([String]$fileName)
{
    $ftpUser = "user"
    $ftpPassword = "password"
    $ftpServer = "ftpServer"

    $webclient = New-Object System.Net.WebClient 
    $webclient.Credentials = New-Object System.Net.NetworkCredential($ftpUser, $ftpPassword)

    $uri = New-Object System.Uri("$ftpServer/files")

    $webclient.DownloadFile($uri, $fileName)
} 

Running this script or doing it manually from the PowerShell console renders the same outcome. Everything will run properly until the file needs to be saved on the destination. I have successfully used this script on other Windows Servers.

This is the error thrown by the script:

Exception calling "DownloadFile" with "2" argument(s): "The remote server returned an error: 150-Starting operation:
STATUS: Getting logs ...
Please wait...
Please wait...
STATUS: Finished getting logs
STATUS: get logs operation is complete
Size: 8262246 bytes
Please wait for 8 seconds ...
Operation Complete150-Accepted data connection
150 (8262246 bytes) to download
."
At C:\Users\administrator\getFTPFile.ps1:73 char:2
+     $webclient.DownloadFile($uri, $fileName)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : WebException

And this is the failed operation from a PowerShell prompt:

ftp> get logs logsFile
200 PORT command successful
150-Starting operation:
STATUS: Getting logs ...
Please wait...
Please wait...
STATUS: Finished getting logs
STATUS: get logs operation is complete
Size: 8283146 bytes
Please wait for 8 seconds ...

Operation CompleteConnection closed by remote host.

This is the output when the transfer is successful:

ftp> get logs logsFile
200 PORT command successful
150-Starting operation:
STATUS: Getting logs ...
Please wait...
Please wait...
STATUS: Finished getting logs
STATUS: get logs operation is complete
Size: 8283146 bytes
Please wait for 8 seconds ...

Operation Complete150-Connecting to port 63596
150 (8275012 bytes) to download
226-File successfully transferred
226 0.778 seconds (measured here), 10.15 Mbytes per second
ftp: 8275012 bytes received in 0.76Seconds 10916.90Kbytes/sec.

Also, I have not tried any other FTP clients.

EDIT 3

Now it's working when using ftp from PowerShell terminal and not working through the script, ran from the PowerShell.

Best Answer

I'm not sure what exactly is the process behind the WebClient::DownloadFile method, but it seems to me that it interrupts the ftp connection right before the download transfer completes causing the operation to fail.

By using the asynchronous method I managed to successfully download the file.
Here's my updated function using the WebClient::DownloadFileTaskAsync method. I also implemented a status check.

function getFTPfile([String]$fileName)
{
    $ftpUser = "user"
    $ftpPassword = "password"
    $ftpServer = "ftpServer"

    $webclient = New-Object System.Net.WebClient 
    $webclient.Credentials = New-Object System.Net.NetworkCredential($ftpUser, $ftpPassword)

    $uri = New-Object System.Uri("$ftpServer/files")

    $job = $webclient.DownloadFileTaskAsync($uri, $fileName) 

    while(!$job.IsCompleted)
    { 
        Write-Host "Downloading file..."
        sleep 10
    }
    if($job.Status -ne "RanToCompletion")
        { Write-Host "Failed to download file." }
    else
        { Write-Host "Download successful." }
}