ML
    • Recent
    • Categories
    • Tags
    • Popular
    • Users
    • Groups
    • Register
    • Login

    Powershell - SFTP Upload Using Posh-SSH

    IT Discussion
    powershell sftp posh-ssh
    2
    9
    7.7k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • wrx7mW
      wrx7m
      last edited by wrx7m

      I had to convert an old ps script from old school FTP to SFTP that uploads files in a few different "local" directories and throws them in the root of our web server. After some digging, I came up with this script. Because the files are in different paths, I used a variable and separate command for each. It works just fine, but I was wondering if there is a more elegant or best practice way of doing that.

      # SFTP Upload of Inventory From CSV files to WPEngine SFTP. Requires installation of Posh-SSH 
      # Install-Module -Name Posh-SSH (https://github.com/darkoperator/Posh-SSH)
      
      # Set the credentials
      $Password = ConvertTo-SecureString 'passwordgoeshere' -AsPlainText -Force
      $Credential = New-Object System.Management.Automation.PSCredential ('usernamegoeshere', $Password)
      
      # Set local file path and SFTP path
      $FilePath1 = "D:\Data\SW\SWRUN\BA\BAUPC.CSV"
      $FilePath2 = "D:\Data\SW\SWRUN\MI\MIUPC.CSV"
      $FilePath3 = "D:\Data\SW\SWRUN\NM\NMUPC.CSV"
      $FilePath4 = "D:\Data\SW\SWRUN\SS\SSUPC.CSV"
      $SftpPath = '/'
      
      # Set the Hostname of the SFTP server
      $SftpServer = 'domain.sftp.wpengine.com'
      
      # Load the Posh-SSH module
      Import-Module Posh-SSH
      
      # Establish the SFTP connection
      $ThisSession = New-SFTPSession -ComputerName $SftpServer -Credential $Credential -AcceptKey -Port 2222
      
      # Upload the files to the SFTP path
      Set-SFTPFile -SessionId ($ThisSession).SessionId -Localfile $FilePath1 -RemotePath $SftpPath -Overwrite
      Set-SFTPFile -SessionId ($ThisSession).SessionId -Localfile $FilePath2 -RemotePath $SftpPath -Overwrite
      Set-SFTPFile -SessionId ($ThisSession).SessionId -Localfile $FilePath3 -RemotePath $SftpPath -Overwrite
      Set-SFTPFile -SessionId ($ThisSession).SessionId -Localfile $FilePath4 -RemotePath $SftpPath -Overwrite
      
      #Disconnect all SFTP Sessions
      Get-SFTPSession | % { Remove-SFTPSession -SessionId ($_.SessionId) }
      
      1 Reply Last reply Reply Quote 2
      • wrx7mW
        wrx7m
        last edited by wrx7m

        Well, I am updating the script, as the requirements changed from multiple local dirs to single sftp dir, to multiple local dirs to multiple sftp servers' dirs.

        Tip for WP-Engine users-
        I had some issues using WP-Engine's path option in their sftp user settings. The feature essentially creates a shortcut to root when you connect. I was telling the script to go to a specific directory, '/inventory', based on root as being root. So, my script would error out because, when I connected, the script would try to go to /inventory/inventory. After changing the $SftpPath variable back to '/', it worked.

        However, I would still like feedback on if there is a better way to write the repetitive lines. Here is the latest version of the script for many to many.

        # SFTP Upload of Inventory From CSV files to WPEngine SFTP. Requires installation of Posh-SSH 
        # Install-Module -Name Posh-SSH (https://github.com/darkoperator/Posh-SSH)
        
        # Set the credentials
        $Password = ConvertTo-SecureString 'PasswordGoesHere' -AsPlainText -Force
        $Credential1 = New-Object System.Management.Automation.PSCredential ('username', $Password)
        $Credential2 = New-Object System.Management.Automation.PSCredential ('username', $Password)
        $Credential3 = New-Object System.Management.Automation.PSCredential ('username', $Password)
        $Credential4 = New-Object System.Management.Automation.PSCredential ('username', $Password)
        
        # Set local file path and SFTP path
        $FilePath1 = "D:\Data\SW\SWRUN\BA\BAUPC.CSV"
        $FilePath2 = "D:\Data\SW\SWRUN\MI\MIUPC.CSV"
        $FilePath3 = "D:\Data\SW\SWRUN\SS\SSUPC.CSV" 
        $FilePath4 = "D:\Data\SW\SWRUN\NM\NMUPC.CSV"
        $SftpPath = '/'
        
        # Set the Hostnames of the SFTP servers
        $SftpServer1 = '1.sftp.wpengine.com'
        $SftpServer2 = '2.sftp.wpengine.com'
        $SftpServer3 = '3.sftp.wpengine.com'
        $SftpServer4 = '4.sftp.wpengine.com'
        
        # Load the Posh-SSH module
        Import-Module Posh-SSH
        
        # Establish the SFTP connection
        $Session1 = New-SFTPSession -ComputerName $SftpServer1 -Credential $Credential1 -AcceptKey -Port 2222
        $Session2 = New-SFTPSession -ComputerName $SftpServer2 -Credential $Credential2 -AcceptKey -Port 2222
        $Session3 = New-SFTPSession -ComputerName $SftpServer3 -Credential $Credential3 -AcceptKey -Port 2222
        $Session4 = New-SFTPSession -ComputerName $SftpServer4 -Credential $Credential4 -AcceptKey -Port 2222
        
        # Upload the files to the SFTP path
        Set-SFTPFile -SessionId ($Session1).SessionId -Localfile $FilePath1 -RemotePath $SftpPath -Overwrite
        Set-SFTPFile -SessionId ($Session2).SessionId -Localfile $FilePath2 -RemotePath $SftpPath -Overwrite
        Set-SFTPFile -SessionId ($Session3).SessionId -Localfile $FilePath3 -RemotePath $SftpPath -Overwrite
        Set-SFTPFile -SessionId ($Session4).SessionId -Localfile $FilePath4 -RemotePath $SftpPath -Overwrite
        
        #Disconnect all SFTP Sessions
        Get-SFTPSession | % { Remove-SFTPSession -SessionId ($_.SessionId) }
        
        ObsolesceO 1 Reply Last reply Reply Quote 0
        • ObsolesceO
          Obsolesce @wrx7m
          last edited by Obsolesce

          @wrx7m said in Powershell - SFTP Upload Using Posh-SSH:

          However, I would still like feedback on if there is a better way to write the repetitive lines. Here is the latest version of the script for many to many.

          There is, but I mean... its' a small single use script so I don't know if its' worth it if you can't "just do it as you write it the first time". What you have is working... do you really care if the username and password is in the script? Only you can know... but practice wise, definitely not. Repetitive lines, no, functions yes, loops yes, built objects yes.

          I can dive in if you are wondering for the sake of learning, sure, glad to help.... ? (but not tonight, i'm beat and soon time for bed)

          wrx7mW 1 Reply Last reply Reply Quote 0
          • wrx7mW
            wrx7m @Obsolesce
            last edited by

            @Obsolesce said in Powershell - SFTP Upload Using Posh-SSH:

            @wrx7m said in Powershell - SFTP Upload Using Posh-SSH:

            However, I would still like feedback on if there is a better way to write the repetitive lines. Here is the latest version of the script for many to many.

            There is, but I mean... its' a small single use script so I don't know if its' worth it if you can't "just do it as you write it the first time". What you have is working... do you really care if the username and password is in the script? Only you can know... but practice wise, definitely not. Repetitive lines, no, functions yes, loops yes, built objects yes.

            I can dive in if you are wondering for the sake of learning, sure, glad to help.... ? (but not tonight, i'm beat and soon time for bed)

            Thanks. They don't offer cert-based auth from what I can see on their site. Also, I am asking because I am interested in learning how to do it properly.

            1 Reply Last reply Reply Quote 0
            • ObsolesceO
              Obsolesce
              last edited by

              Okay, as a very first step for learning, I'd create a simple function (first as a PoC) to consume what you already have... something like this:
              (I don't have an sftp server to test with, but seems like it will work)

              function Invoke-SFTPDance {
                  [cmdletbinding(SupportsShouldProcess=$true)]
                  param(
                      [object]$Credentials,
                      [string]$ServerName,
                      [int]$Port,
                      [string]$FilePath,
                      [string]$RemotePath = "/"
                  )
              
                  $Session = New-SFTPSession -ComputerName $ServerName -Credential $Credentials -AcceptKey -Port $Port
              
                  Set-SFTPFile -SessionId $Session.SessionId -Localfile $FilePath -RemotePath $RemotePath -Overwrite
              
                  Remove-SFTPSession -SessionId $Session.SessionId
              
              }
              
              Invoke-SFTPDance -Credentials $Credential1 -ServerName $SftpServer1 -Port 2222 -FilePath $FilePath1
              Invoke-SFTPDance -Credentials $Credential2 -ServerName $SftpServer2 -Port 2222 -FilePath $FilePath2
              Invoke-SFTPDance -Credentials $Credential3 -ServerName $SftpServer3 -Port 2222 -FilePath $FilePath3
              Invoke-SFTPDance -Credentials $Credential4 -ServerName $SftpServer4 -Port 2222 -FilePath $FilePath4
              
              1 Reply Last reply Reply Quote 3
              • ObsolesceO
                Obsolesce
                last edited by

                Then I would start to build in more detail:

                function Invoke-SFTPDance {
                    [cmdletbinding(SupportsShouldProcess=$true)]
                    param(
                        [Parameter(Mandatory,Position=0)]
                        [object]$Credentials,
                        [Parameter(Mandatory,Position=1)]
                        [string]$ServerName,
                        [Parameter(Mandatory,Position=2)]
                        [int]$Port,
                        [Parameter(Mandatory,Position=3)]
                        [string]$FilePath,
                        [Parameter(Position=3)]
                        [string]$RemotePath = "/"
                    )
                
                    Begin {
                
                        #### PRE-REQS ####
                        $sshMod = Get-Module -Name Posh-SSH
                
                        if (-not($sshMod)) {
                            Install-Module -Name Posh-SSH
                            Import-Module -Name Posh-SSH
                        }
                        ##################
                
                    }
                
                    Process {
                
                        $Session = New-SFTPSession -ComputerName $ServerName -Credential $Credentials -AcceptKey -Port $Port
                
                        Set-SFTPFile -SessionId $Session.SessionId -Localfile $FilePath -RemotePath $RemotePath -Overwrite
                
                    }
                
                    End {
                
                        Remove-SFTPSession -SessionId $Session.SessionId
                
                    }
                
                }
                
                Invoke-SFTPDance -Credentials $Credential1 -ServerName $SftpServer1 -Port 2222 -FilePath $FilePath1
                Invoke-SFTPDance -Credentials $Credential2 -ServerName $SftpServer2 -Port 2222 -FilePath $FilePath2
                Invoke-SFTPDance -Credentials $Credential3 -ServerName $SftpServer3 -Port 2222 -FilePath $FilePath3
                Invoke-SFTPDance -Credentials $Credential4 -ServerName $SftpServer4 -Port 2222 -FilePath $FilePath4
                
                1 Reply Last reply Reply Quote 3
                • wrx7mW
                  wrx7m
                  last edited by

                  @Obsolesce Thanks, man! I will try my hand and making these adjustments. Appreciate the help and guidance.

                  1 Reply Last reply Reply Quote 0
                  • ObsolesceO
                    Obsolesce
                    last edited by Obsolesce

                    And then, error handling...
                    Note: I did this super fast and I really didn't check it over (running out of time). Also, ideally, you'd write to a log in addition to the Write-Warning. I'll leave the logging up to you. You can also create a nifty function for logging and automatic log cleaning if needed.

                    function Invoke-SFTPDance {
                        [cmdletbinding(SupportsShouldProcess=$true)]
                        param(
                            [Parameter(Mandatory,Position=0)]
                            [object]$Credentials,
                            [Parameter(Mandatory,Position=1)]
                            [string]$ServerName,
                            [Parameter(Mandatory,Position=2)]
                            [int]$Port,
                            [Parameter(Mandatory,Position=3)]
                            [string]$FilePath,
                            [Parameter(Position=3)]
                            [string]$RemotePath = "/"
                        )
                    
                        Begin {
                    
                            #### PRE-REQS ####
                            $sshMod = Get-Module -Name Posh-SSH
                    
                            if (-not($sshMod)) {
                                Install-Module -Name Posh-SSH
                                Import-Module -Name Posh-SSH
                            }
                            ##################
                    
                        }
                    
                        Process {
                    
                            $Session = New-SFTPSession -ComputerName $ServerName -Credential $Credentials -AcceptKey -Port $Port -ErrorAction SilentlyContinue -ErrorVariable SessionERR
                    
                            if ($Session) {
                    
                                if (-not($SessionERR)) {
                    
                                    #  If session created without error
                                    Set-SFTPFile -SessionId $Session.SessionId -Localfile $FilePath -RemotePath $RemotePath -Overwrite
                    
                                } else {
                    
                                    Write-Warning "A sessions was created, but also resulted in an error: $SessionERR"
                                    Set-SFTPFile -SessionId $Session.SessionId -Localfile $FilePath -RemotePath $RemotePath -Overwrite
                    
                                }
                    
                            } else {
                    
                                if ($SessionERR) {
                    
                                    Write-Warning "There was a problem creating the session, with error - $SessionERR"
                    
                                } else {
                    
                                    Write-Warning "There was a problem creating the session, but no error..."
                    
                                }
                            }
                    
                        }
                    
                        End {
                    
                            if ($Session) {
                    
                                Remove-SFTPSession -SessionId $Session.SessionId
                                
                            }
                    
                        }
                    
                    }
                    
                    Invoke-SFTPDance -Credentials $Credential1 -ServerName $SftpServer1 -Port 2222 -FilePath $FilePath1
                    Invoke-SFTPDance -Credentials $Credential2 -ServerName $SftpServer2 -Port 2222 -FilePath $FilePath2
                    Invoke-SFTPDance -Credentials $Credential3 -ServerName $SftpServer3 -Port 2222 -FilePath $FilePath3
                    Invoke-SFTPDance -Credentials $Credential4 -ServerName $SftpServer4 -Port 2222 -FilePath $FilePath4
                    

                    Of course logging, which is always best done before you start using the script... it helps with testing.

                    1 Reply Last reply Reply Quote 0
                    • ObsolesceO
                      Obsolesce
                      last edited by Obsolesce

                      There's also a lot more that can be done, especially with the credentials... storing them as an encrypted file and retrieving them for the function (function just for that, functions using other functions), keeping all the parameter data in an object to retrieve, or from CSV, etc....

                      It depends on how far down the rabbit hole you want to go, and how much time you want to spend making it.

                      Honestly, I'd do this kind of thing with a different project, but this can work too. There's a lot you can do.

                      1 Reply Last reply Reply Quote 1
                      • 1 / 1
                      • First post
                        Last post