Need advice/support with a PowerShell script

Advertisement

sleepersimulant
Joined:
Posts:
6
Location:
Switzerland

Need advice/support with a PowerShell script

Hello together

First of all I would like to thank you for your time and possible ideas.

The following situation

I have a SFTP server running on Debian and I have written a script that creates users, a password and gives them access to /share only. This all works and I can successfully log in via WinSCP and see everything from /share.

Now I want to write a PowerShell script that has the password encrypted in the script, establishes the connection, checks whether data that exists locally also exists in the remote folder (on the SFTP server) and copies it if not. I have read the documentation on the WinSCP page (thanks for that!). Unfortunately I fail.

What was done:
Encryption
$key = (1..16)  
$plainPassword = 'password' 
$secureString = ConvertTo-SecureString -String $plainPassword -AsPlainText -Force
$encryptedPassword = ConvertFrom-SecureString -SecureString $secureString -Key $key
$encryptedPassword | Out-File "C:\path\encryptedpassword.txt"
Created XML-File with Username and encrypted password
<Configuration>
  <UserName>USER</UserName>
  <Password>ENCRYPTED
</Password>
</Configuration>
And the PowerShell-Script
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
[xml]$config = Get-Content "C:\path\config.xml"
$securePassword = ConvertTo-SecureString $config.Configuration.Password
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
    Protocol = [WinSCP.Protocol]::Sftp
    HostName = "FQDN"
    PortNumber = PORT
    UserName = $config.Configuration.UserName
    SecurePassword = $securePassword
    GiveUpSecurityAndAcceptAnySshHostKey = $true 
}
$session = New-Object WinSCP.Session
try {
    $session.Open($sessionOptions)
 
    # Define paths
    $localPath = "C:\Users\test\Downloads\test\*"
    $remotePath = "/share/remotetest/"
 
    # Synchronize files
    $synchronizationResult = $session.SynchronizeDirectories([WinSCP.SynchronizationMode]::Local, $localPath, $remotePath, $true)
 
    # Throw on any error
    $synchronizationResult.Check()
}
finally {
    $session.Dispose()
}
 
Write-Host "Synchronization complete."

But when I run the script in Powershell ISE I get the following output
ConvertTo-SecureString : The parameter value "NOT THE ACTUAL ENCRYPTED STRING
" is not a valid encrypted string.
At line:8 char:19
+ ... ecurePassword = ConvertTo-SecureString $config.Configuration.Password
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [ConvertTo-SecureString], PSArgumentException
    + FullyQualifiedErrorId : ImportSecureString_InvalidArgument,Microsoft.PowerShell.Commands.ConvertToSecureStringCommand
 
New-Object : The value supplied is not valid, or the property is read-only. Change the value, and then try again.
At line:11 char:19
+ $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [New-Object], Exception
    + FullyQualifiedErrorId : SetValueException,Microsoft.PowerShell.Commands.NewObjectCommand
 
Exception calling "Open" with "1" argument(s): "Value cannot be null.
Parameter name: sessionOptions"
At line:23 char:5
+     $session.Open($sessionOptions)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentNullException
 
Synchronization complete.
Can anyone here help me? Thank you very much!

Reply with quote

Advertisement

martin
Site Admin
martin avatar
Joined:
Posts:
41,041
Location:
Prague, Czechia

Re: Need advice/support with a PowerShell script

When you use a key to encrypt the password, you have to use the same key when decrypting it:
$securePassword = ConvertTo-SecureString $config.Configuration.Password -Key $key

Reply with quote

sleepersimulant
Joined:
Posts:
6
Location:
Switzerland

Hello martin

Thanks for your reply and sorry for the late reply!

I am testing an automation, which should take place with secure credentials. I am testing various things for this, one of which is the script from above. Your input worked, but unfortunately I'm still not satisfied.

Now I am testing the following and wanted to ask for your input. I now use the Windows Credentials Manager, which I use to save the login.
$username = "testtest"
$password = "testtest"
cmdkey /generic:fqdn.com /user:$username /pass:$password

WinSCP-Script
# Define the local and remote paths
$localPath = "C:\testfolder\"
$remotePath = "/testfolder/test"
 
# Define the SFTP server details
$ftpServer = "fqdn.com"
$port = 2222
 
# Function to get credentials from Windows Credential Manager
function Get-CredentialFromManager {
    param (
        [string]$target
    )
 
    $cred = cmdkey /list:$target 2>&1 | Select-String -Pattern "User:"
    if ($cred) {
        $username = $cred -replace ".*User: (.*)", '$1'
        $password = ConvertFrom-SecureString -SecureString (New-Object System.Management.Automation.PSCredential($username, (Read-Host -AsSecureString "Enter password for $username")).Password) -AsPlainText
        return New-Object -TypeName PSCredential -ArgumentList $username, (ConvertTo-SecureString $password -AsPlainText -Force)
    } else {
        throw "Credentials not found in Windows Credential Manager for $target"
    }
}
 
# Load the credentials from the Windows Credential Manager
try {
    $credential = Get-CredentialFromManager -target $ftpServer
    $username = $credential.UserName
    $password = $credential.GetNetworkCredential().Password
} catch {
    Write-Error $_.Exception.Message
    exit 1
}
 
# Secure the password
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
 
# Define the WinSCP assembly location
$WinSCPPath = "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
 
# Load the WinSCP .NET assembly
Add-Type -Path $WinSCPPath
 
# Create a new WinSCP session
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
    Protocol = [WinSCP.Protocol]::Sftp
    HostName = $ftpServer
    PortNumber = $port
    UserName = $username
    Password = $securePassword
    SshHostKeyFingerprint = "ssh-ed25519 256 SHA256:xxx"
}
 
$session = New-Object WinSCP.Session
 
try {
    # Connect to the server
    $session.Open($sessionOptions)
 
    # Synchronize the files
    $synchronizationResult = $session.SynchronizeDirectories(
        [WinSCP.SynchronizationMode]::Remote,
        $localPath,
        $remotePath,
        $False,
        [WinSCP.SynchronizationCriteria]::Checksum
    )
 
    # Check for errors
    if ($synchronizationResult.IsSuccess) {
        Write-Host "Synchronization succeeded!"
    } else {
        Write-Host "Synchronization failed with errors:"
        $synchronizationResult.Failures | ForEach-Object { Write-Host $_.Message }
    }
} finally {
    # Disconnect from the server
    $session.Dispose()
}
Error:
 : Credentials not found in Windows Credential Manager for fqdn.com
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
The credentials are saved and can also be viewed in the Credential Manager under "Generic". I have experimented with it for a long time now but can't get it to work. Perhaps my Powershell script is simply wrong. Do you see anything here?

Thanks a lot martin!

Reply with quote

martin
Site Admin
martin avatar

If I understand your post correctly, this is not not a WinSCP problem.
Please ask it at appropriate place, e.g. on Stack Overflow.

Reply with quote

Advertisement

sleepersimulant
Joined:
Posts:
6
Location:
Switzerland

Need advice/support with a PowerShell script

Sorry for the late response Martin! Unfortunately, I was confined to bed for longer than expected. I was able to fix the problem that had to do with the Credential Manager and through my test script I was able to successfully load the credentials. You saw my post on Stack Overflow, that helped me.

I think I'm on a very good way with the script, but I still have a problem.

I have written a test script to test the login via WinSCP before I write a script that does the automation. The script looks like that:
# Importieren des CredentialManager-Moduls
Import-Module CredentialManager
 
# Definieren Sie die Zieladresse der Anmeldeinformationen
$server = "server.server.com"
$port = 2222
$credentialTarget = "${server}:${port}"
 
$credential = Get-StoredCredential -Target $credentialTarget
 
if ($credential -eq $null) {
    Write-Host "Anmeldeinformationen für $credentialTarget wurden nicht gefunden."
    exit 1
} else {
    Write-Host "Anmeldeinformationen erfolgreich abgerufen."
}
function ConvertFrom-SecureStringToPlainText {
    param (
        [System.Security.SecureString]$secureString
    )
    $ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
    try {
        return [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr)
    }
    finally {
        [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr)
    }
}
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
 
try {
    $passwordPlainText = ConvertFrom-SecureStringToPlainText -secureString $credential.Password
    $sessionOptions = New-Object WinSCP.SessionOptions
    $sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
    $sessionOptions.HostName = $server
    $sessionOptions.PortNumber = $port
    $sessionOptions.UserName = $credential.UserName
    $sessionOptions.Password = $passwordPlainText
    $sessionOptions.SshHostKeyFingerprint = "ssh-rsa SHA256:xxx"
    Write-Host "Sitzungseinstellungen erfolgreich konfiguriert."
}
catch {
    Write-Host "Fehler beim Konfigurieren der Sitzungseinstellungen: $($_.Exception.Message)"
    exit 1
}
 
$session = New-Object WinSCP.Session
 
try {
    # Sitzung öffnen
    $session.Open($sessionOptions)
    Write-Host "Erfolgreich mit dem SFTP-Server verbunden."
}
catch {
    Write-Host "Verbindung zum SFTP-Server fehlgeschlagen: $($_.Exception.Message)"
}
finally {
    # Sitzung schließen, falls geöffnet
    if ($session.Opened) {
        $session.Dispose()
    }
}
I have saved the script and get the following output after running it in Powershell:
Anmeldeinformationen erfolgreich abgerufen.
Fehler beim Konfigurieren der Sitzungseinstellungen: Exception setting "SshHostKeyFingerprint": "SSH host key fingerprint "ssh-rsa SHA256:xxxx" does not match pattern /((ssh-rsa|ssh-dss|ssh-ed25519|ecdsa-sha2-nistp(256|384|521))( |-))?(\d+ )?(([0-9a-fA-F]{2}(:|-)){15}[0-9a-fA-F]{2}|[0-9a-zA-Z+/\-_]{43}=?)(;((ssh-rsa|ssh-dss|ssh-ed25519|ecdsa-sha2-nistp(256|384|521))( |-))?(\d+ )?(([0-9a-fA-F]{2}(:|-)){15}[0-9a-fA-F]{2}|[0-9a-zA-Z+/\-_]{43}=?))*/"
The fingerprint i get on the server looks like that "SHA256:xxx". And that didn't work in the script at the beginning, that's why I tried some other ways. Where have I made a mistake in my thinking? Thank you very much for your great support and help!

Reply with quote

Advertisement

You can post new topics in this forum