spinnifex
Goto Top

PowerShell + FTP in EXE

Hallo Admins!

folgende Batch-Datei verrichtet bislang absolut brav ihren Dienst, indem sie eine bestimmte Datei von einem ftp-Server holt, ich sie in NP++ bearbeiten kann und sie anschließend wieder hochgeladen wird. Vor dem Upload wird das Datum der letzten Änderung der index.htm noch aktualisiert, die ein iframe mit der per d3src eingebundenen csv-Datei umrahmt. Die pause dienen dazu eventuelle ftp-Probleme manuell abzufangen.

ftp -s:updt_gms_dl.ftp
pause
"C:\Program Files (x86)\Notepad++\notepad++.exe" "data.csv"  
powershell -Executionpolicy ByPass -Command "(Get-Item 'index.htm').LastWriteTime = (Get-Date)"  
ftp -s:updt_gms_ul.ftp
pause
del data.csv
del index.htm
Die in der Batch aufgerufene *.ftp-Datei für den Downoad hat folgenden Inhalt, der Upload läuft entsprechend.

open ftp.beispiel.de
XXXXXXX-ftp
PWxxxXXX999!
cd webseiten
cd project0816
get index.htm
cd data
get data.csv
bye

JETZT SOLL DARAUS EINE EXE-DATEI WERDEN.
Das Update-Tool soll auch Kollegen zur Verfügung gestellt werden, die nicht unbedingt die ftp-Credentials wissen sollen. Meine Idee war, das ganze mit dem Batch to Exe Converter 2.4.2 von www.f2ko.de in eine EXE zu konvertieren. Leider ist der Versuch (auch mit anderen bat2exe-Tools) fehlgeschlagen, weil entweder ftp oder die ps-Befehle nicht verarbeitet werden. Ergebnis: Die EXE taucht zwar gleich drei Mal im Taskmanager auf, lässt sich aber nur per Neustart (vielleicht auch per Taskill) abschießen.

Wie müsste das ganze in PowerShell aussehen, das ich dann mit einem ps1 to exe-Converter umwandeln kann?

Lieben Dank allerseits!

spinnifex

Content-Key: 298396

Url: https://administrator.de/contentid/298396

Printed on: February 28, 2024 at 00:02 o'clock

Mitglied: 117471
117471 Mar 06, 2016 at 12:32:31 (UTC)
Goto Top
Die Datei mit den Credentials hast Du ja auch dann, wenn Du die Batch konvertierst.

Vielleicht könnte man da ja mit Java glücklich werden: http://www.torsten-horn.de/techdocs/java-ftp.htm

Auf jeden Fall würde ich das Kennwort zumindest rudimentär "verschlüsseln". Ich mache das i.d.R. so, dass ich es wenigstens einmal mit meinem Geburtsdatum o.Ä. XOR-verknüpfe um wenigstens die "Zufallsfunde" abzudecken.
Member: rubberman
rubberman Mar 06, 2016 updated at 12:47:51 (UTC)
Goto Top
Hallo spinnefix.

Sorry, wenn das Off-Topic klingt, aber
JETZT SOLL DARAUS EINE EXE-DATEI WERDEN.
... bitte gar nicht erst drüber nachdenken. Der ganze Kram wird in Plaintext irgendwo in das temp Verzeichnis entpackt und ausgeführt. NULL Schutz! Vergiss diesen ...2exe Mist ganz schnell und auch sonst gehören Anmeldedaten nie hart in einen Programmcode.

Grüße
rubberman

EDIT Meine eigenen Erfahrungen:
"To Exe Converter" ...
Member: spinnifex
spinnifex Mar 06, 2016 updated at 12:47:30 (UTC)
Goto Top
@ rubberman Danke für die klaren Worte ...! Gegen richtig neugierige User lässt sich das Passwort so offensichtlich nicht schützen. Ich weiß leider keine andere Lösung und

@ FA-jka die JAVA-Idee fällt leider mangels Laufzeit-Umgebung (und meiner Unfähigkeit in Java zu schreiben) aus.

Das Auslesen eines Temp-Verzeichnisses würde ich riskieren, zumal die meisten meiner Kollegen noch nicht einmal wissen, was das ist. Und endlich kann ich den Trasnfer wohl nur mit sTP gegen z.B. Wireshark schützen.

Ansonsten bin ich natürlich dankbar für jeden Alternativvorschlag...

spinnifex
Member: rubberman
rubberman Mar 06, 2016 at 12:51:08 (UTC)
Goto Top
Hallo spinnefix,

hab zeitgleich mit deiner Antwort oben noch ein EDIT angehängt. Da gibt es noch einiges mehr, was dagegen spricht ...

Grüße
rubberman
Member: spinnifex
spinnifex Mar 06, 2016 at 12:57:11 (UTC)
Goto Top
Hallo rubberman,

Okay okay, ich hab's kapiert ... face-wink Zum Glück habe ich nicht soviele Kollegen, die dann morgens an meinem Schreibtisch warten.

Gibt es trotzdem ein Idee, wie man das sicher und elegant löst? Eine Übersetzung bat nach ps1 wäre mir schon ein Hilfe.

spinnifex
Mitglied: 117471
117471 Mar 06, 2016 at 13:12:37 (UTC)
Goto Top
Eigentlich mit jeder beliebigen Programmiersprache deiner Wahl. Java war ja nur ein Beispiel.

Und - wie gesagt - eine Übersetzung .bat nach .ps1 wäre Dir keine Hilfe, da die Zugangsdaten in einer separaten Datei stehen. Diese Datei ist kein Bestandteil deiner "Programmierung", sondern lediglich eine Steuerdatei für den FTP-Client von Windows.

Die Kollegen werden also weiterhin "an deinem Schreibtisch warten müssen". Hoffentlich hast Du nie Urlaub face-smile
Mitglied: 126919
126919 Mar 06, 2016 updated at 14:07:10 (UTC)
Goto Top
Zumal bei FTP die Daten sowieso ungeschützt in den Äther hinausposaunt werden :-P

Gruß fk
Member: spinnifex
spinnifex Mar 06, 2016 updated at 14:35:40 (UTC)
Goto Top
Zitat von @126919:
Zumal bei FTP die Daten sowieso ungeschützt in den Äther ...

... weswegen ich ja mit einer Lösung, die wenigstens gegen Dummies geschützt ist, einverstanden wäre. Als Dummie würde ich schon reichen, denn es ist mir eben nicht gelungen, die von rubberman erwähnten temp-Dateien in einer Text-bat/exe zu finden. Weder nach Beenden des Scripts, noch zur Laufzeit.

Mein Weg bleibt demnach (auch des notwendigen Aufwands wegen) ps1 to exe ... Sorry! Hilft mir dennoch jemand bei der Übersetzung?

spinnifex
Mitglied: 126919
126919 Mar 06, 2016 updated at 14:29:53 (UTC)
Goto Top
Dann bau dir halt direkt mit AutoIt,VB.Net, c# eine Exe... Oder benutze für jeden User explizite FTP-Credentials welche nur Lese-/Schreibrechte auf die jeweilige Datei auf dem FTP haben.
Member: colinardo
Solution colinardo Mar 06, 2016, updated at Mar 27, 2016 at 13:54:21 (UTC)
Goto Top
Hallo spinnifex,
FTP-Down- und Upload nativ mit Powershell, kein Problem.
Du nutzt wirklich noch Plain-FTP ohne SSL ?? Dir sollte man mal kräftig mit CAT-9 den Hintern versohlen face-wink
function Upload-File([string]$path,[string]$url,$username,$password){
    try{
        $request = [System.Net.FtpWebRequest]::Create($url)
        $request.Method = [System.Net.WebRequestMethods+FTP]::UploadFile
        $request.Credentials = New-Object System.Net.NetworkCredential($username,$password)
        [byte[]] $bytes = [System.IO.File]::ReadAllBytes($path)
        [System.IO.Stream]$stream = $request.GetRequestStream();
        $stream.Write($bytes,0,$bytes.Length)
        $stream.Close(); $stream.Dispose()
        $response = [System.Net.FtpWebResponse]$request.GetResponse()
        $result = $response.StatusDescription
        $response.Close()
        return $result
    }catch{
        throw $_.Exception.Message
        return $false
    }
}
function Download-File([string]$url,[string]$path,$username,$password){
    try{
        $request = [System.Net.FtpWebRequest]::Create($url)
        $request.Method = [System.Net.WebRequestMethods+FTP]::DownloadFile
        $request.Credentials = New-Object System.Net.NetworkCredential($username,$password)
        $response = [System.Net.FtpWebResponse]$request.GetResponse()
        [System.IO.Stream]$stream = $response.GetResponseStream()
        [System.IO.FileStream]$outstream = New-Object System.IO.FileStream ($path,[System.IO.FileMode]::Create)
        $stream.CopyTo($outstream)
        $outstream.Close();$stream.Close();$response.Close()
        $stream.Dispose();$outstream.Dispose();$response.Dispose()
        return $true
    }catch{
        throw $_.Exception.Message
    }
}

# Variablen ------------------------
$username = 'FTPUSER'  
$password =  'PASSWORD'  
$remotefile = 'ftp://ftp.beispiel.de/data.csv'  
$localfile = "$env:TEMP\data.csv"  
# ----------------------------------------

# Download file from FTP
Download-File -url $remotefile -path $localfile -username $username -password $password
# Start Editing file
Start-Process $localfile -Wait
# Upload file to FTP
Upload-File -path $localfile -url $remotefile -username $username -password $password
# remove local downloaded file
remove-item $localfile -Force
Grüße Uwe


p.s. Powershell kann auch Verschlüsseln. Hier zwei Methoden zum Ver- und Entschlüsseln mit zusätzlichem Salt und Key.
function Encrypt-String($plaintext,$password,$salt='jj3h4h5+tbbj2j281273#+', $init='j3j35&#*23kkk35!'){  
    $p = new-Object System.Security.Cryptography.AesManaged
	$p.Key = (new-Object Security.Cryptography.PasswordDeriveBytes ([Text.Encoding]::UTF8.GetBytes($password)), ([Text.Encoding]::UTF8.GetBytes($salt)), "SHA1", 5).GetBytes(32) #256/8  
	$p.IV = (new-Object Security.Cryptography.SHA1Managed).ComputeHash( [Text.Encoding]::UTF8.GetBytes($init) )[0..15]
    $encryptor = $p.CreateEncryptor()
    $msEncrypt = New-Object IO.MemoryStream
    $csEncrypt = New-Object Security.Cryptography.CryptoStream $msEncrypt,$encryptor,"Write"  
    $swEncrypt = New-Object IO.StreamWriter $csEncrypt
    $swEncrypt.Write($plaintext)
    $swEncrypt.Close();$csEncrypt.Close();$msEncrypt.Close()
    $p.Clear()
    [byte[]]$result = $msEncrypt.toArray()
    return [Convert]::ToBase64String($result)
}

function Decrypt-String($string,$password,$salt='jj3h4h5+tbbj2j281273#+', $init='j3j35&#*23kkk35!'){  
    $p = new-Object System.Security.Cryptography.AesManaged
    $p.Key = (new-Object Security.Cryptography.PasswordDeriveBytes ([Text.Encoding]::UTF8.GetBytes($password)), ([Text.Encoding]::UTF8.GetBytes($salt)), "SHA1", 5).GetBytes(32) #256/8  
	$p.IV = (new-Object Security.Cryptography.SHA1Managed).ComputeHash( [Text.Encoding]::UTF8.GetBytes($init) )[0..15]
    $decryptor = $p.CreateDecryptor()
    $msDecrypt = New-Object IO.MemoryStream @(,([Convert]::FromBase64String($string)))
    $csDecrypt = New-Object Security.Cryptography.CryptoStream $msDecrypt,$decryptor,"Read"  
    $srDecrypt = New-Object IO.StreamReader $csDecrypt
    [string]$result = $srDecrypt.ReadtoEnd()
    $srDecrypt.Close();$csDecrypt.Close();$msDecrypt.Close()
    $p.Clear()
    return $result
}
Member: spinnifex
spinnifex Mar 06, 2016 at 18:10:02 (UTC)
Goto Top
Hallo colinardo,
ich nehme auch gerne noch eine Rute aus CAT-5, Flachbandkabel und Twisted-Pair LoL

Vielen Dank für diesen DEUTLICHEN Hinweis face-wink

Den ps Code muss ich mir in Ruhe (weil ja eben Anänger) anschauen und werde mich morgen entsprechend demütig dazu äußern ...

VIELEN DANK!

spinnifex
Member: BlueEyePhoenix
BlueEyePhoenix Jan 26, 2022 updated at 12:47:49 (UTC)
Goto Top
Hallo,

ich habe mir das ganze auch mal angesehen und wollte das mit dem FTP Download jetzt mal ausprobieren

aber leider bekomme ich den Download nicht hin. Die List-FTP Files von colinardo funktioniert wunderbar

function List-FTPFiles([string]$url,$username,$password){
    try{
        $request = [System.Net.FtpWebRequest]::Create($url)
        $request.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
        $request.Credentials = New-Object System.Net.NetworkCredential($username,$password)
        $response = [System.Net.FtpWebResponse]$request.GetResponse()
        [System.IO.Stream]$stream = $response.GetResponseStream()
        $reader = new-object System.IO.StreamReader($stream)
        $list = $reader.ReadToEnd() -split "[\r\n]+" | ?{$_ -ne ''}  
        $stream.Close();$response.Close();$reader.Close()
        $stream.Dispose();$response.Dispose();$reader.Dispose()
        return $list
    }catch{
        throw $_.Exception.Message
    }
}
List-FTPFiles -url 'meinFTP' -username 'meinUser' -password 'meinpw'  

Diese Auflistung an Dateien wollte ich nun mit dem Downloadscript herunterladen

function Download-File([string]$url,[string]$path,$username,$password){
    try{
        $request = [System.Net.FtpWebRequest]::Create($url)
        $request.Method = [System.Net.WebRequestMethods+FTP]::DownloadFile
        $request.Credentials = New-Object System.Net.NetworkCredential($username,$password)
        $response = [System.Net.FtpWebResponse]$request.GetResponse()
        [System.IO.Stream]$stream = $response.GetResponseStream()
        [System.IO.FileStream]$outstream = New-Object System.IO.FileStream ($path,[System.IO.FileMode]::Create)
        $stream.CopyTo($outstream)
        $outstream.Close();$stream.Close();$response.Close()
        $stream.Dispose();$outstream.Dispose();$response.Dispose()
        return $true
    }catch{
        throw $_.Exception.Message
    }
}

# Variablen ------------------------
$username = 'meinUser'  
$password =  'meinpw'  
$remotefile = 'ftp://meinFTP/*.txt'  
$localfile = "C:\meinOrdner\"  
# ----------------------------------------

# Download file from FTP
Download-File -url $remotefile -path $localfile -username $username -password $password

Aber bekomme am Ende nur einen Error:

Kann es sein das man damit mit einen gesamten Ordnerinhalt herunterladen kann?

BlueEyePhoenix
Member: colinardo
colinardo Jan 26, 2022 updated at 13:45:39 (UTC)
Goto Top
Servus.
Zitat von @BlueEyePhoenix:
Kann es sein das man damit mit keinen gesamten Ordnerinhalt herunterladen kann?
Nein, die Funktion ist auf eine einzelne Datei ausgelegt, wenn du damit eine Liste von Dateien herunterladen willst musst du über eine Foreach Schleife über die Liste itterieren und die Download-Funktion mehrfach aufrufen
function Download-File([string]$url,[string]$path,$username,$password){
    try{
        $request = [System.Net.FtpWebRequest]::Create($url)
        $request.Method = [System.Net.WebRequestMethods+FTP]::DownloadFile
        $request.Credentials = New-Object System.Net.NetworkCredential($username,$password)
        $response = [System.Net.FtpWebResponse]$request.GetResponse()
        [System.IO.Stream]$stream = $response.GetResponseStream()
        [System.IO.FileStream]$outstream = New-Object System.IO.FileStream ($path,[System.IO.FileMode]::Create)
        $stream.CopyTo($outstream)
        $outstream.Close();$stream.Close();$response.Close()
        $stream.Dispose();$outstream.Dispose();$response.Dispose()
        return $true
    }catch{
        throw $_.Exception.Message
    }
}
function List-FTPFiles([string]$url,$username,$password){
        $request = [System.Net.FtpWebRequest]::Create($url)
        $request.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
        $request.Credentials = New-Object System.Net.NetworkCredential($username,$password)
        $response = [System.Net.FtpWebResponse]$request.GetResponse()
        [System.IO.Stream]$stream = $response.GetResponseStream()
        $reader = new-object System.IO.StreamReader($stream)
        $list = $reader.ReadToEnd() -split "[\r\n]+" | ?{$_ -ne ''}  
        $stream.Close();$response.Close();$reader.Close()
        $stream.Dispose();$response.Dispose();$reader.Dispose()
        return $list
}

$username = 'USER'  
$password =  'PASS'  
$remotepath = 'ftp://domain.tld/ordner'  
$filefilter = '*.txt'  
$localpath = 'D:\Ziel'  

List-FTPFiles -url "$remotepath/$filefilter" -username $username -password $password | %{  
    Download-File -url "$remotepath/$_" -path "$localpath/$_" -username $username -password $password  
}

Ich würde dir aber dringend raten das gleich mit WinSCP zu machen, das ist erstens schneller weil hier nicht für jeden Download eine Session mit Authentifizierung aufgemacht werden muss und zweitens noch wichtiger, es unterstützt das auch verschlüsselte Verbindungen!

Ein Beispiel wie du WinSCP mit Powershell nutzt findest du hier (ist für SSH, aber WinSCP kannst du einfach per Protocol Angabe auf FTP FTPS & Co umstellen): Script um Dateien zu verschieben und mail zu generieren

Hier ein kleines Beispiel für das WinSCP Module und ein ganz einfacher Transfer von Remote nach Lokal

# remote directory for files
$REMOTE_DIR = '/remote/folder/path'  
# local directory for files
$LOCAL_DIR = 'D:\ziel'  
# only transfer files matching filter
$FILEFILTER = '*.txt'  
# FTP Hostname
$FTP_HOST = 'ftp.domain.tld'  
# FTP Port
$FTP_PORT = 21
# FTP Username
$USERNAME = 'USER'  
# FTP Password
$PASSWORD = 'PASS'  
# ----------------------------------------
# install WinSCP module if needed
$modulename = 'WinSCP'  
if(!(Get-Module -ListAvailable -Name $modulename)){
    write-host "Module '$modulename' not found, trying to install module ... " -F Yellow -NoNewline  
    start powershell -Verb runas -ArgumentList "-EP Bypass -NoP -C Install-Module WinSCP -Force" -wait  
    if(!(Get-Module -ListAvailable -Name $modulename)){
        write-host "install failed, please try to install manualy. Exit." -F Red  
        sleep 2
        exit 1
    }else{
        write-host "Successfully installed." -F Green  
    }
}

# connection options
$params = @{
    Hostname = $FTP_HOST
    Credential = new-Object PSCredential($USERNAME,(ConvertTo-SecureString $PASSWORD -AsPlainText -Force))
    Protocol = 'ftp'  
    FtpMode = 'Passive'  
    PortNumber = $FTP_PORT
}

$session = $null
try{
    # create session
    $session = New-WinSCPSession -SessionOption (New-WinSCPSessionOption @params)
    # define transport options
    $transoptions = New-WinSCPTransferOption -TransferMode Binary
    # download files
    $transfer = $session.GetFilesToDirectory($REMOTE_DIR,$LOCAL_DIR,$FILEFILTER,$false,$transoptions)
    # check transfer result
    if ($transfer.Transfers.Count){
        write-host "Transfered files:" -F Green  
        $transfer.Transfers | ft FileName,Destination -AutoSize
        # list errors if any
        if(!$transfer.IsSuccess){
            write-host "Transfers with Errors:" -F Yellow  
            $transfer.Failures | ft -AutoSize
        }
    }else{
        write-host "No matching files found." -F Yellow  
    }
    write-host "Done." -F Green  
    
}catch{
    write-error -Message $_.Exception.Message
}finally{
    # cleanup
    $session.Close()
    $session.Dispose()
}

So, damit solltest du nun gründlich versorgt sein face-smile.

Grüße Uwe