├── .github
└── FUNDING.yml
├── Examples
├── Example09-TestFile.ps1
├── Example03-AutoDetectFTPConfiguration.ps1
├── Example05-ConnectWithAutoConnect.ps1
├── Example04-ConnectWithAutoDetect.ps1
├── Example08-ConnectSSH.ps1
├── Example02-BasicExampleSCP.ps1
├── Example12-Receive-SFTPFile.ps1
├── Example13-UploadFTPSDirectory.ps1
├── Example10-SetChmodViaFTP.ps1
├── Example11-ReceiveFiles.ps1
├── Example01-BasicExampleFTP.ps1
├── Example06-UploadFTPS.ps1
├── Example07-UploadSFTP.ps1
└── Example15-BasicExampleFTPwithProxy.ps1
├── Public
├── Get-FTPChmod.ps1
├── Remove-FTPFile.ps1
├── Test-FTPFile.ps1
├── Test-FTPDirectory.ps1
├── Rename-FTPFile.ps1
├── Get-FTPChecksum.ps1
├── Set-FTPOption.ps1
├── Compare-FTPFile.ps1
├── Move-FTPFile.ps1
├── Move-FTPDirectory.ps1
├── Disconnect-FTP.ps1
├── Remove-FTPDirectory.ps1
├── Disconnect-SFTP.ps1
├── Get-SFTPList.ps1
├── Get-FTPList.ps1
├── Remove-SFTPFile.ps1
├── Start-FXPFileTransfer.ps1
├── Start-FXPDirectoryTransfer.ps1
├── Set-FTPChmod.ps1
├── Receive-FTPDirectory.ps1
├── Receive-SFTPFile.ps1
├── Send-SSHCommand.ps1
├── Send-FTPFile.ps1
├── Send-SFTPFile.ps1
├── Set-FTPTracing.ps1
├── Rename-SFTPFile.ps1
├── Send-FTPDirectory.ps1
├── Connect-SSH.ps1
├── Receive-FTPFile.ps1
├── Connect-SFTP.ps1
├── Request-FTPConfiguration.ps1
└── Connect-FTP.ps1
├── Sources
├── Transferetto
│ ├── Initialize.cs
│ ├── Transferetto.csproj
│ └── OnImportAndRemove.cs
└── Transferetto.sln
├── Tests
├── Test-FTPDirectory.Tests.ps1
├── Download-FTP.Tests.ps1
└── Connect-FTP.Tests.ps1
├── LICENSE
├── Transferetto.AzurePipelines.yml
├── Private
├── Add-PrivateFTPFile.ps1
├── Add-PrivateFTPFiles.ps1
├── Get-PrivateFTPFile.ps1
└── Get-PrivateFTPFiles.ps1
├── Transferetto.psd1
├── Transferetto.Tests.ps1
├── CHANGELOG.MD
├── README.md
├── Transferetto.psm1
└── Build
└── Manage-Module.ps1
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: PrzemyslawKlys
4 | custom: https://paypal.me/PrzemyslawKlys
--------------------------------------------------------------------------------
/Examples/Example09-TestFile.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | $Client = Connect-FTP -Server '192.168.241.187' -Verbose -Username 'test' -Password 'BiPassword90A' -EncryptionMode Explicit -ValidateAnyCertificate
4 | # List files
5 | Test-FTPFile -Client $Client -RemotePath '/Temporary'
--------------------------------------------------------------------------------
/Public/Get-FTPChmod.ps1:
--------------------------------------------------------------------------------
1 | function Get-FTPChmod {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [string] $RemotePath
6 | )
7 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
8 | $Client.GetChmod($RemotePath)
9 | }
10 | }
--------------------------------------------------------------------------------
/Public/Remove-FTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Remove-FTPFile {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $RemotePath
6 | )
7 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
8 | $Client.DeleteFile($RemotePath)
9 | }
10 | }
--------------------------------------------------------------------------------
/Public/Test-FTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Test-FTPFile {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $RemotePath
6 | )
7 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
8 | $Client.FileExists($RemotePath)
9 | }
10 | }
--------------------------------------------------------------------------------
/Public/Test-FTPDirectory.ps1:
--------------------------------------------------------------------------------
1 | function Test-FTPDirectory {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $RemotePath
6 | )
7 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
8 | $Client.DirectoryExists($RemotePath)
9 | }
10 | }
--------------------------------------------------------------------------------
/Sources/Transferetto/Initialize.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace Transferetto {
8 | /// This is a dummy class that is used to initialize the module in PowerShell
9 | /// Doesnt do anything at all
10 | public class Initialize {
11 |
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Examples/Example03-AutoDetectFTPConfiguration.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | # Login via UserName/Password
4 | $ProfileFtp1 = Request-FTPConfiguration -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password'
5 | $ProfileFtp1 | Format-Table
6 |
7 | # Anonymous login
8 | $ProfileFtp2 = Request-FTPConfiguration -Server 'speedtest.tele2.net' -Verbose
9 | $ProfileFtp2 | Format-Table
--------------------------------------------------------------------------------
/Public/Rename-FTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Rename-FTPFile {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $Path,
6 | [Parameter(Mandatory)][string] $DestinationPath
7 | )
8 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
9 | $Client.Rename($Path, $DestinationPath)
10 | }
11 | }
--------------------------------------------------------------------------------
/Public/Get-FTPChecksum.ps1:
--------------------------------------------------------------------------------
1 | function Get-FTPChecksum {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $RemotePath,
6 | [FluentFTP.FtpHashAlgorithm] $HashAlgorithm = [FluentFTP.FtpHashAlgorithm]::MD5
7 | )
8 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
9 | $Client.GetChecksum($RemotePath, $HashAlgorithm)
10 | }
11 | }
--------------------------------------------------------------------------------
/Public/Set-FTPOption.ps1:
--------------------------------------------------------------------------------
1 | function Set-FTPOption {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [nullable[int]] $RetryAttempts,
6 | [nullable[bool]] $DownloadZeroByteFiles
7 | )
8 | if ($RetryAttempts) {
9 | $Client.RetryAttempts = $RetryAttempts
10 | }
11 | if ($DownloadZeroByteFiles) {
12 | $Client.DownloadZeroByteFiles = $DownloadZeroByteFiles
13 | }
14 | }
--------------------------------------------------------------------------------
/Examples/Example05-ConnectWithAutoConnect.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | # Login via UserName/Password via autoconnect - this will try all options and connect, while displaying Verbose what settings were used to achieve connection
4 | # Useful for trying out every possible combination
5 | $Client = Connect-FTP -Server 'test.rebex.net' -Username 'demo' -Password 'password' -AutoConnect -Verbose
6 | Get-FTPList -Client $Client | Format-Table
7 | Disconnect-FTP -Client $Client
--------------------------------------------------------------------------------
/Examples/Example04-ConnectWithAutoDetect.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | # Login via UserName/Password via autodetect - keep in mind using Connect-FTP directly will be faster...
4 | $ProfileFtp1 = Request-FTPConfiguration -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password'
5 | $ProfileFtp1 | Format-Table
6 |
7 | # use first profile
8 | $Client = Connect-FTP -FtpProfile $ProfileFtp1[0]
9 | Get-FTPList -Client $Client | Format-Table
10 | Disconnect-FTP -Client $Client
--------------------------------------------------------------------------------
/Examples/Example08-ConnectSSH.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | $SshClient = Connect-SSH -Server '192.168.240.29' -Username 'przemek' -Password 'sdsd'
4 |
5 | $Command = {
6 | 'cd /'
7 | 'ls -la'
8 | 'cd /etc'
9 | 'ls -la'
10 | }
11 | $SshCommand = Send-SSHCommand -SSHClient $SshClient -Command $Command -Verbose
12 | $SshCommand
13 |
14 |
15 | $Command = {
16 | 'cat /etc/hosts.allow'
17 | }
18 | $SshCommand = Send-SSHCommand -SSHClient $SshClient -Command $Command -Verbose
19 | $SshCommand
--------------------------------------------------------------------------------
/Public/Compare-FTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Compare-FTPFile {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $LocalPath,
6 | [Parameter(Mandatory)][string] $RemotePath,
7 | [FluentFTP.FtpCompareOption] $CompareOption = [FluentFTP.FtpCompareOption]::Auto
8 |
9 | )
10 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
11 | $Client.CompareFile($LocalPath, $RemotePath, $CompareOption)
12 | }
13 | }
--------------------------------------------------------------------------------
/Public/Move-FTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Move-FTPFile {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $RemoteSource,
6 | [Parameter(Mandatory)][string] $RemoteDestination,
7 | [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip
8 | )
9 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
10 | $Client.MoveFile($RemoteSource, $RemoteDestination, $RemoteExists)
11 | }
12 | }
--------------------------------------------------------------------------------
/Public/Move-FTPDirectory.ps1:
--------------------------------------------------------------------------------
1 | function Move-FTPDirectory {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $RemoteSource,
6 | [Parameter(Mandatory)][string] $RemoteDestination,
7 | [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip
8 | )
9 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
10 | $Client.MoveDirectory($RemoteSource, $RemoteDestination, $RemoteExists)
11 | }
12 | }
--------------------------------------------------------------------------------
/Public/Disconnect-FTP.ps1:
--------------------------------------------------------------------------------
1 | function Disconnect-FTP {
2 | [cmdletBinding()]
3 | param(
4 | [FluentFTP.FtpClient] $Client
5 | )
6 | if ($Client -and $Client.IsConnected) {
7 | try {
8 | $Client.Disconnect()
9 | } catch {
10 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
11 | Write-Error $_
12 | return
13 | } else {
14 | Write-Warning "Disconnect-FTP - Error: $($_.Exception.Message)"
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Public/Remove-FTPDirectory.ps1:
--------------------------------------------------------------------------------
1 | function Remove-FTPDirectory {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [Parameter(Mandatory)][string] $RemotePath,
6 | [FluentFTP.FtpListOption] $FtpListOption
7 | )
8 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
9 | if (-not $FtpListOption) {
10 | $Client.DeleteDirectory($RemotePath)
11 | } else {
12 | $Client.DeleteDirectory($RemotePath, $FtpListOption)
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/Public/Disconnect-SFTP.ps1:
--------------------------------------------------------------------------------
1 | function Disconnect-SFTP {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient
5 | )
6 | if ($SftpClient -and $SftpClient.IsConnected) {
7 | try {
8 | $SftpClient.Disconnect()
9 | } catch {
10 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
11 | Write-Error $_
12 | return
13 | } else {
14 | Write-Warning "Disconnect-SFTP - Error: $($_.Exception.Message)"
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Examples/Example02-BasicExampleSCP.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | $SftpClient = Connect-SFTP -Server '192.168.240.29' -Username 'przemek' -Password 'YourPassword'
4 | Get-SFTPList -SftpClient $SftpClient | Format-Table
5 | Get-SFTPList -SftpClient $SftpClient -Path "/home" | Format-Table
6 | Receive-SFTPFile -SftpClient $SftpClient -RemotePath '/home/przemek/test1.txt' -LocalPath "$PSScriptRoot\Downloads\mmm.txt"
7 | Send-SFTPFile -SftpClient $SftpClient -LocalPath "$PSScriptRoot\Downloads\mmm.txt" -RemotePath '/home/przemek/mmm.txt' -AllowOverride
8 | Disconnect-SFTP -SftpClient $SftpClient
--------------------------------------------------------------------------------
/Tests/Test-FTPDirectory.Tests.ps1:
--------------------------------------------------------------------------------
1 | Describe 'Test-FTPDirectory' {
2 | It 'Given an existing path it should return $True' {
3 | # Anonymous login
4 | $Client = $Null
5 | $Client = Connect-FTP -Server 'ftp.gnu.org' -Verbose
6 | $Client.IsConnected | Should -Be $True
7 | $ExistingDirectory = Test-FTPDirectory -RemotePath '/tmp' -Client $Client | Should -Be $true
8 | $NonExistingDirectory = Test-FTPDirectory -RemotePath '/foobar1' -Client $Client | Should -Be $false
9 | Disconnect-FTP -Client $Client
10 | $Client.IsConnected | Should -Be $false
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/Example12-Receive-SFTPFile.ps1:
--------------------------------------------------------------------------------
1 | Clear-Host
2 | Import-Module .\Transferetto.psd1 -Force
3 |
4 | # Connect to SFTP Server
5 | $SftpClient = Connect-SFTP -Server 'test.rebex.net' -Username demo -Password password
6 |
7 | # Get All Files in '/test' for Export
8 | $Export_Files = Get-SFTPList -SftpClient $SftpClient -Path '/pub/example' | Where-Object { $_.IsDirectory -eq $false }
9 |
10 | # Set Export Directory
11 | $ExportPathLocal = "C:\Temp"
12 |
13 | # Download Each File
14 | $Output = ForEach ($RemoteFile in $Export_Files) {
15 | Receive-SFTPFile -SftpClient $SftpClient -RemotePath $RemoteFile.FullName -LocalPath "$ExportPathLocal\$($RemoteFile.Name)"
16 | }
17 | $Output | Format-Table
18 |
19 | # Disconnect
20 | Disconnect-FTP -Client $Client
21 |
--------------------------------------------------------------------------------
/Examples/Example13-UploadFTPSDirectory.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | Set-FTPTracing -Enable
4 |
5 | $Client = Connect-FTP -Server '192.168.241.187' -Verbose -Username 'test' -Password 'BiPassword90A' -EncryptionMode Explicit -ValidateAnyCertificate
6 | # List files
7 | $List = Get-FTPList -Client $Client
8 | #$List | Format-Table
9 | # List files within Temporary directory
10 | $List = Get-FTPList -Client $Client -Path '/Temporary'
11 | #$List | Format-Table
12 |
13 | # Get local files
14 | $ListFiles = Get-ChildItem -LiteralPath $PSScriptRoot\Upload
15 |
16 | $Upload = Send-FTPDirectory -Client $Client -LocalPath $PSScriptRoot\Upload -RemotePath '/Temporary' -Verbose -FolderSyncMode Update
17 | $Upload | Format-Table *
18 |
19 | Disconnect-FTP -Client $Client
20 |
21 | Set-FTPTracing -Disable
--------------------------------------------------------------------------------
/Public/Get-SFTPList.ps1:
--------------------------------------------------------------------------------
1 | function Get-SFTPList {
2 | [cmdletBinding()]
3 | param(
4 | [alias('FtpPath')][string] $Path,
5 | [Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient
6 | )
7 | if ($SftpClient -and $SftpClient.IsConnected -and -not $SftpClient.Error) {
8 | try {
9 | if ($Path) {
10 | $SftpClient.ListDirectory($Path)
11 | } else {
12 | $SftpClient.ListDirectory('')
13 | }
14 | } catch {
15 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
16 | Write-Error $_
17 | return
18 | } else {
19 | Write-Warning "Get-SFTPList - Error: $($_.Exception.Message)"
20 | }
21 | }
22 | } else {
23 | Write-Warning "Get-SFTPList - Skipped (IsConnected $($SftpClient.IsConnected) / Error: $($SftpClient.Error))"
24 | }
25 | }
--------------------------------------------------------------------------------
/Examples/Example10-SetChmodViaFTP.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | # If you want to track responses from FTP
4 | Set-FTPTracing -Enable
5 |
6 | $Client = Connect-FTP -Server '192.168.241.187' -Verbose -Username 'test' -Password 'BiPassword90A' -EncryptionMode Explicit -ValidateAnyCertificate
7 | # List files
8 | Get-FTPList -Client $Client -Path '/Temporary' | Format-Table *
9 | Get-FTPChmod -Client $Client -RemotePath '/Temporary'
10 |
11 | # Set / Read Chmod - you need to have permissions for this to work properly
12 | Set-FTPChmod -Client $Client -RemotePath '/Temporary/OrgChart (1).pdf' -Permissions 666
13 | Set-FTPChmod -Client $Client -RemotePath '/Temporary/CreateDir' -Permissions 666
14 |
15 | # Set / Read Chmod - you need to have permissions for this to work properly
16 | Get-FTPChmod -Client $Client -RemotePath '/Temporary/OrgChart (1).pdf'
17 | Get-FTPChmod -Client $Client -RemotePath '/Temporary/CreateDir'
18 |
19 | Set-FTPTracing -Disable
--------------------------------------------------------------------------------
/Tests/Download-FTP.Tests.ps1:
--------------------------------------------------------------------------------
1 | Describe 'Connect-FTP / Disconnect-FTP' {
2 | It 'Given FTP server should be able to download at least one file' {
3 | # Anonymous login
4 | $Client = $Null
5 | $Client = Connect-FTP -Server 'ftp.gnu.org' -Verbose
6 | $Client.IsConnected | Should -Be $True
7 | $List = Get-FTPList -Client $Client
8 | $List.Count | Should -BeGreaterThan 5
9 | $ListVideo = Get-FTPList -Client $Client -Path "/video"
10 | $ListVideo.Count | Should -BeGreaterThan 20
11 | $TestPath = [io.path]::Combine("$($TestDrive), $($ListVideo[0].Name)")
12 |
13 | Receive-FTPFile -Client $Client -RemotePath $ListVideo[0].FullName -LocalPath $TestPath
14 |
15 | Test-Path -LiteralPath $TestPath | Should -Be $True
16 | (Get-Item -LiteralPath $TestPath).Length | Should -BeGreaterThan 100000
17 |
18 | Disconnect-FTP -Client $Client
19 | $Client.IsConnected | Should -Be $false
20 | }
21 | }
--------------------------------------------------------------------------------
/Examples/Example11-ReceiveFiles.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | Set-FTPTracing -Enable
4 |
5 | # Connect to FTP
6 | $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password'
7 |
8 | # Get list of files on FTP
9 | $List = Get-FTPList -Client $Client -Path '/pub/example'
10 |
11 | # Find latest file on FTP server
12 | $FindLatestFile = $List | Where-Object { $_.Type -eq 'File' } | Sort-Object -Property Modified -Descending | Select-Object -First 2
13 |
14 | # Download that file
15 | foreach ($RemoteFile in $FindLatestFile) {
16 | Receive-FTPFile -Client $Client -RemoteFile $RemoteFile -LocalPath "$PSScriptRoot\Download\$($RemoteFile.Name)" -LocalExists Overwrite -VerifyOptions Retry, None
17 | }
18 | # Download multiple files into directory
19 | Receive-FTPFile -Client $Client -RemoteFile $FindLatestFile -LocalPath "$PSScriptRoot\Download" -LocalExists Overwrite -VerifyOptions Retry, None | Format-Table
20 |
21 | # Disconnect
22 | Disconnect-FTP -Client $Client
--------------------------------------------------------------------------------
/Public/Get-FTPList.ps1:
--------------------------------------------------------------------------------
1 | function Get-FTPList {
2 | [cmdletBinding()]
3 | param(
4 | [alias('FtpPath')][string] $Path,
5 | [FluentFTP.FtpListOption] $Options,
6 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client
7 | )
8 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
9 | try {
10 | if ($Path -and $Options) {
11 | $Client.GetListing($Path, $Options)
12 | } elseif ($Path) {
13 | $Client.GetListing($Path)
14 | } else {
15 | $Client.GetListing()
16 | }
17 | } catch {
18 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
19 | Write-Error $_
20 | return
21 | } else {
22 | Write-Warning "Get-FTPList - Error: $($_.Exception.Message)"
23 | }
24 | }
25 | } else {
26 | Write-Warning "Get-FTPList - Skipped (IsConnected $($Client.IsConnected) / Error: $($Client.Error))"
27 | }
28 | }
--------------------------------------------------------------------------------
/Public/Remove-SFTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Remove-SFTPFile {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient,
5 | [string] $RemotePath,
6 | [switch] $Suppress
7 | )
8 | if ($SftpClient -and $SftpClient.IsConnected) {
9 | try {
10 | $SftpClient.DeleteFile($RemotePath)
11 | $Status = [PSCustomObject] @{
12 | Action = 'RemoveFile'
13 | Status = $true
14 | Message = ""
15 | }
16 | } catch {
17 | $Status = [PSCustomObject] @{
18 | Action = 'RemoveFile'
19 | Status = $false
20 | Message = "Error $($_.Exception.Message)"
21 | }
22 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
23 | Write-Error $_
24 | return
25 | } else {
26 | Write-Warning "Remove-SFTPFile - Error: $($_.Exception.Message)"
27 | }
28 | }
29 | if (-not $Suppress) {
30 | $Status
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2011-2022 Evotec
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Public/Start-FXPFileTransfer.ps1:
--------------------------------------------------------------------------------
1 | function Start-FXPFileTransfer {
2 | [alias('Start-FXPFile')]
3 | [cmdletBinding()]
4 | param(
5 | [alias('SourceClient')][Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
6 | [Parameter(Mandatory)][string] $SourcePath,
7 | [Parameter(Mandatory)][FluentFTP.FtpClient] $DestinationClient,
8 | [Parameter(Mandatory)][string] $DestinationPath,
9 | [switch] $CreateRemoteDirectory,
10 | [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
11 | [FluentFTP.FtpVerify[]] $VerifyOptions = [FluentFTP.FtpVerify]::None
12 | )
13 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
14 | # FluentFTP.FtpStatus TransferFile(string sourcePath, FluentFTP.FtpClient remoteClient, string remotePath, bool createRemoteDir, FluentFTP.FtpRemoteExists existsMode, FluentFTP.FtpVerify verifyOptions, System.Action[FluentFTP.FtpProgress] progress, FluentFTP.FtpProgress metaProgress)
15 | $Client.TransferFile($SourcePath, $DestinationClient, $DestinationPath, $CreateRemoteDirectory.IsPresent, $RemoteExists, $VerifyOptions)
16 | }
17 | }
--------------------------------------------------------------------------------
/Examples/Example01-BasicExampleFTP.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | # Login via UserName/Password
4 | $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password' -EncryptionMode Auto
5 | $List = Get-FTPList -Client $Client
6 | $List | Format-Table
7 | Get-FTPDirectory -Client $Client -LocalPath 'C:\Temp\FTP' -RemotePath "/" -Verbose
8 | Disconnect-FTP -Client $Client
9 |
10 | # Login via UserName/Password
11 | $Client = Connect-FTP -Server 'ftp.dlptest.com' -Verbose -Username 'dlpuser' -Password 'rNrKYTX9g7z3RgJRmxWuGHbeu'
12 | $List = Get-FTPList -Client $Client
13 | $List | Format-Table
14 | Disconnect-FTP -Client $Client
15 |
16 | # Anonymous login
17 | $Client = Connect-FTP -Server 'speedtest.tele2.net' -Verbose
18 | $List = Get-FTPList -Client $Client
19 | $List | Format-Table
20 | Disconnect-FTP -Client $Client
21 |
22 | # Login via credentials
23 | $Credential = Get-Credential -UserName 'demo' -Message 'Please enter password' # password is password
24 | $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Credential $Credential
25 | $List = Get-FTPList -Client $Client
26 | $List | Format-Table
27 | Disconnect-FTP -Client $Client
--------------------------------------------------------------------------------
/Examples/Example06-UploadFTPS.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | $Client = Connect-FTP -Server '192.168.241.187' -Verbose -Username 'test' -Password 'BiPassword90A' -EncryptionMode Explicit -ValidateAnyCertificate
4 | # List files
5 | $List = Get-FTPList -Client $Client
6 | $List | Format-Table
7 | # List files within Temporary directory
8 | $List = Get-FTPList -Client $Client -Path '/Temporary'
9 | $List | Format-Table
10 |
11 | # Get local files
12 | $ListFiles = Get-ChildItem -LiteralPath $PSScriptRoot\Upload -File
13 |
14 | # Upload file by file
15 | $Output = foreach ($File in $ListFiles) {
16 | # To temporary
17 | Send-FTPFile -Client $Client -LocalPath $File.FullName -RemotePath "/Temporary/$($File.Name)" -RemoteExists Overwrite
18 | # to directory within Temporary that may not exists
19 | Send-FTPFile -Client $Client -LocalPath $File.FullName -RemotePath "/Temporary/CreateDir/$($File.Name)" -RemoteExists Skip -CreateRemoteDirectory
20 | }
21 |
22 | $Output | Format-Table
23 |
24 | # Upload all files at once to FTP
25 | Send-FTPFile -Client $Client -LocalPath $ListFiles.FullName -RemotePath "/Temporary" -RemoteExists Overwrite | Format-Table
26 |
27 | Disconnect-FTP -Client $Client
--------------------------------------------------------------------------------
/Examples/Example07-UploadSFTP.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | $SftpClient = Connect-SFTP -Server '192.168.241.187' -Verbose -Username 'test' -Password 'BiPassword90A'
4 | Get-SFTPList -SftpClient $SftpClient | Format-Table
5 | Get-SFTPList -SftpClient $SftpClient -Path "/Temporary" | Format-Table *
6 |
7 | $ListFiles = Get-ChildItem -LiteralPath $PSScriptRoot\Upload -Recurse -File
8 | foreach ($File in $ListFiles) {
9 | $Directory = [io.path]::GetDirectoryName($File.FullName)
10 | if ($Directory -eq "$PSScriptRoot\Upload") {
11 | Send-SFTPFile -SftpClient $SftpClient -LocalPath $File.FullName -RemotePath "/Temporary/$($File.Name)" -AllowOverride
12 | } else {
13 | #$RemotePath = "/Temporary/$($Directory.Split('\')[-1])/$($File.Name)"
14 | $RemoteFolder = "/Temporary/$($Directory.Split('\')[-1])"
15 | $List = Get-SFTPList -SftpClient $SftpClient -Path $RemoteFolder -WarningAction SilentlyContinue
16 | if (-not $List) {
17 | $SftpClient.CreateDirectory($RemoteFolder)
18 | }
19 | Send-SFTPFile -SftpClient $SftpClient -LocalPath $File.FullName -RemotePath "$RemoteFolder/$($File.Name)" -AllowOverride
20 | }
21 | }
22 |
23 | Disconnect-SFTP -SftpClient $SftpClient
--------------------------------------------------------------------------------
/Public/Start-FXPDirectoryTransfer.ps1:
--------------------------------------------------------------------------------
1 | function Start-FXPDirectoryTransfer {
2 | [alias('Start-FXPDirectory')]
3 | [cmdletBinding()]
4 | param(
5 | [alias('SourceClient')][Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
6 | [Parameter(Mandatory)][string] $SourcePath,
7 | [Parameter(Mandatory)][FluentFTP.FtpClient] $DestinationClient,
8 | [Parameter(Mandatory)][string] $DestinationPath,
9 | [FluentFTP.FtpFolderSyncMode] $FolderSyncMode = [FluentFTP.FtpFolderSyncMode]::Update,
10 | [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
11 | [FluentFTP.FtpVerify[]] $VerifyOptions = [FluentFTP.FtpVerify]::None
12 | )
13 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
14 | # System.Collections.Generic.List[FluentFTP.FtpResult] TransferDirectory(string sourceFolder, FluentFTP.FtpClient remoteClient, string remoteFolder, FluentFTP.FtpFolderSyncMode mode, FluentFTP.FtpRemoteExists existsMode, FluentFTP.FtpVerify verifyOptions, System.Collections.Generic.List[FluentFTP.Rules.FtpRule] rules, System.Action[FluentFTP.FtpProgress] progress)
15 | $Client.TransferDirectory($SourcePath, $DestinationClient, $DestinationPath, $FolderSyncMode, $RemoteExists, $VerifyOptions)
16 | }
17 | }
--------------------------------------------------------------------------------
/Public/Set-FTPChmod.ps1:
--------------------------------------------------------------------------------
1 | function Set-FTPChmod {
2 | [cmdletBinding(DefaultParameterSetName = 'ByInt')]
3 | param(
4 | [Parameter(Mandatory, ParameterSetName = 'ByInt')]
5 | [Parameter(Mandatory, ParameterSetName = 'Explicit')]
6 | [FluentFTP.FtpClient] $Client,
7 |
8 | [Parameter(Mandatory, ParameterSetName = 'ByInt')]
9 | [Parameter(Mandatory, ParameterSetName = 'Explicit')]
10 | [string] $RemotePath,
11 |
12 | [Parameter(Mandatory, ParameterSetName = 'ByInt')]
13 | [nullable[int]] $Permissions,
14 |
15 | [Parameter(Mandatory, ParameterSetName = 'Explicit')]
16 | [FluentFTP.FtpPermission] $Owner,
17 |
18 | [Parameter(Mandatory, ParameterSetName = 'Explicit')]
19 | [FluentFTP.FtpPermission] $Group,
20 |
21 | [Parameter(Mandatory, ParameterSetName = 'Explicit')]
22 | [FluentFTP.FtpPermission] $Other
23 | )
24 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
25 | #void Chmod(string path, int permissions)
26 | #void Chmod(string path, FluentFTP.FtpPermission owner, FluentFTP.FtpPermission group, FluentFTP.FtpPermission other)
27 | if ($Permissions) {
28 | $Client.Chmod($RemotePath, $Permissions)
29 | } else {
30 | $Client.Chmod($RemotePath, $Owner, $Group, $Other)
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Public/Receive-FTPDirectory.ps1:
--------------------------------------------------------------------------------
1 | function Receive-FTPDirectory {
2 | [alias('Get-FTPDirectory')]
3 | [cmdletBinding()]
4 | param(
5 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
6 | [string] $LocalPath,
7 | [Parameter(Mandatory)][string] $RemotePath,
8 | [FluentFTP.FtpFolderSyncMode] $FolderSyncMode = [FluentFTP.FtpFolderSyncMode]::Update,
9 | [FluentFTP.FtpLocalExists] $LocalExists,
10 | [FluentFTP.FtpVerify] $VerifyOptions,
11 | [FluentFTP.Rules.FtpRule[]] $Rules
12 | )
13 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
14 | #System.Collections.Generic.List[FluentFTP.FtpResult] DownloadDirectory(string localFolder, string remoteFolder, FluentFTP.FtpFolderSyncMode mode, FluentFTP.FtpLocalExists existsMode, FluentFTP.FtpVerify verifyOptions, System.Collections.Generic.List[FluentFTP.Rules.FtpRule] rules, System.Action[FluentFTP.FtpProgress] progress)
15 | #System.Collections.Generic.List[FluentFTP.FtpResult] IFtpClient.DownloadDirectory(string localFolder, string remoteFolder, FluentFTP.FtpFolderSyncMode mode, FluentFTP.FtpLocalExists existsMode, FluentFTP.FtpVerify verifyOptions, System.Collections.Generic.List[FluentFTP.Rules.FtpRule] rules, System.Action[FluentFTP.FtpProgress] progress)
16 | $Client.DownloadDirectory($LocalPath, $RemotePath, $FolderSyncMode)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Transferetto.AzurePipelines.yml:
--------------------------------------------------------------------------------
1 | jobs:
2 | - job: Build_PS_Win
3 | pool:
4 | vmImage: windows-latest
5 | steps:
6 | - powershell: |
7 | .\Transferetto.Tests.ps1
8 | displayName: "Run Pester Tests - PowerShell 5"
9 |
10 | - job: Windows_PowerShell_7
11 | pool:
12 | vmImage: windows-latest
13 | steps:
14 | - pwsh: '.\Transferetto.Tests.ps1'
15 | env:
16 | TEAMSPESTERID: $(TEAMSPESTERID)
17 | displayName: "Run Pester Tests"
18 |
19 |
20 | - job: Build_PSCore_Ubuntu
21 | pool:
22 | vmImage: ubuntu-latest
23 | steps:
24 | - script: |
25 | curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
26 | curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list
27 | sudo apt-get update
28 | sudo apt-get install -y powershell
29 | displayName: "Install PowerShell Core"
30 | - script: |
31 | pwsh -c '.\Transferetto.Tests.ps1'
32 | displayName: "Run Pester Tests"
33 |
34 | - job: Build_PSCore_MacOS
35 | pool:
36 | vmImage: macOS-latest
37 | steps:
38 | - script: |
39 | brew update
40 | brew tap caskroom/cask
41 | brew install mono-libgdiplus
42 | brew install --cask powershell
43 | displayName: "Install PowerShell Core"
44 | - script: |
45 | pwsh -c '.\Transferetto.Tests.ps1'
46 | displayName: "Run Pester Tests"
47 |
--------------------------------------------------------------------------------
/Public/Receive-SFTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Receive-SFTPFile {
2 | [alias('Get-SFTPFile')]
3 | [cmdletBinding()]
4 | param(
5 | [Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient,
6 | [string] $RemotePath,
7 | [string] $LocalPath
8 | )
9 | if ($SftpClient -and $SftpClient.IsConnected) {
10 | try {
11 | $FileStream = [System.IO.FileStream]::new($LocalPath, [System.IO.FileMode]::OpenOrCreate)
12 | $SftpClient.DownloadFile($RemotePath, $FileStream)
13 | $Status = [PSCustomObject] @{
14 | Action = 'DownloadFile'
15 | Status = $true
16 | LocalPath = $LocalPath
17 | RemotePath = $RemotePath
18 | Message = ""
19 | }
20 | } catch {
21 | $Status = [PSCustomObject] @{
22 | Action = 'DownloadFile'
23 | Status = $false
24 | LocalPath = $LocalPath
25 | RemotePath = $RemotePath
26 | Message = "Error: $($_.Exception.Message)"
27 | }
28 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
29 | Write-Error $_
30 | return
31 | } else {
32 | Write-Warning "Receive-SFTPFile - Error: $($_.Exception.Message)"
33 | }
34 | } finally {
35 | $FileStream.Close()
36 | if ($Status.Status -eq $false) {
37 | Remove-Item -LiteralPath $LocalPath
38 | }
39 | }
40 | $Status
41 | }
42 | }
--------------------------------------------------------------------------------
/Sources/Transferetto/Transferetto.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Evotec
5 | Przemyslaw Klys
6 | 1.0.0
7 | netstandard2.0
8 | Transferetto
9 |
10 | (c) 2011 - 2022 Przemyslaw Klys @ Evotec. All rights reserved.
11 | 10.0
12 | False
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | $(DefineConstants);FRAMEWORK
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Public/Send-SSHCommand.ps1:
--------------------------------------------------------------------------------
1 | function Send-SSHCommand {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][Renci.SshNet.SshClient] $SshClient,
5 | [scriptblock] $Command,
6 | [switch] $Status
7 | )
8 | if ($SshClient -and $SshClient.IsConnected -and -not $SshClient.Error) {
9 | if ($Command) {
10 | $CommandsToExecute = & $Command
11 | [string] $SendCommand = foreach ($C in $CommandsToExecute) {
12 | if ($C.Trim().EndsWith(';')) {
13 | $C
14 | } else {
15 | "$C;"
16 | }
17 | }
18 | try {
19 | Write-Verbose -Message "Send-SSHCommand - Executing command: $SendCommand"
20 | if ($Status) {
21 | [PSCustomObject] @{
22 | Status = $true
23 | Output = $SshClient.CreateCommand($SendCommand).Execute()
24 | Error = $null
25 | }
26 | } else {
27 | $SshClient.CreateCommand($SendCommand).Execute()
28 | }
29 | } catch {
30 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
31 | Write-Error $_
32 | return
33 | } else {
34 | Write-Warning "Send-SSHCommand - Error: $($_.Exception.Message)"
35 | }
36 | if ($Status) {
37 | [PSCustomObject] @{
38 | Status = $false
39 | Output = ''
40 | Error = "Error: $($_.Exception.Message)"
41 | }
42 | }
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/Examples/Example15-BasicExampleFTPwithProxy.ps1:
--------------------------------------------------------------------------------
1 | Import-Module .\Transferetto.psd1 -Force
2 |
3 | Set-FTPTracing -Enable
4 | # # Login via UserName/Password via proxy
5 | # $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password' -ProxyHost '192.252.216.81' -ProxyPort 4145 -ProxyType FtpClientSocks5Proxy
6 | # $List = Get-FTPList -Client $Client
7 | # $List | Format-Table
8 | # Disconnect-FTP -Client $Client
9 |
10 |
11 | # $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password'
12 | # $List = Get-FTPList -Client $Client
13 | # $List | Format-Table
14 | # Receive-FTPFile -Client $Client -RemotePath '/readme.txt' -LocalPath 'C:\Temp\readme.txt'
15 | # Disconnect-FTP -Client $Client
16 |
17 |
18 | # Login via UserName/Password with Proxy
19 | $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password' -ProxyHost '104.37.135.145' -ProxyPort 4145 -ProxyType FtpClientSocks4aProxy
20 | $List = Get-FTPList -Client $Client
21 | $List | Format-Table
22 | #Remove-Item -LiteralPath 'C:\Temp\readme.txt'
23 | #Receive-FTPFile -Client $Client -RemotePath '/readme.txt' -LocalPath 'C:\Temp\readme.txt'
24 | Disconnect-FTP -Client $Client
25 |
26 |
27 | # Set-FTPTracing -Enable
28 | # # Login via UserName/Password
29 | # $Client = Connect-FTP -Server 'ftp.dlptest.com' -Verbose -Username 'dlpuser' -Password 'rNrKYTX9g7z3RgJRmxWuGHbeu'
30 | # $List = Get-FTPList -Client $Client
31 | # $List | Format-Table
32 | # $List.Count
33 | # Disconnect-FTP -Client $Client
34 |
35 | # # Login via UserName/Password
36 | # $Client = Connect-FTP -Server 'ftp.dlptest.com' -Verbose -Username 'dlpuser' -Password 'rNrKYTX9g7z3RgJRmxWuGHbeu' -ProxyHost '104.37.135.145' -ProxyPort 4145 -ProxyType FtpClientSocks4aProxy
37 | # $List = Get-FTPList -Client $Client
38 | # $List | Format-Table
39 | # $List.Count
40 | # Disconnect-FTP -Client $Client
--------------------------------------------------------------------------------
/Sources/Transferetto.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.3.32804.467
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Transferetto", "Transferetto\Transferetto.csproj", "{EEF3BFAD-C8A5-474C-BA52-5C20E66EF0F9}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {EEF3BFAD-C8A5-474C-BA52-5C20E66EF0F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {EEF3BFAD-C8A5-474C-BA52-5C20E66EF0F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {EEF3BFAD-C8A5-474C-BA52-5C20E66EF0F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {EEF3BFAD-C8A5-474C-BA52-5C20E66EF0F9}.Release|Any CPU.Build.0 = Release|Any CPU
18 | {7391AC3E-2156-49C6-A228-15B6A7946FBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {7391AC3E-2156-49C6-A228-15B6A7946FBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {7391AC3E-2156-49C6-A228-15B6A7946FBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {7391AC3E-2156-49C6-A228-15B6A7946FBA}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {C3349FCA-EF1E-4AFF-8F4C-522530B18D54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {C3349FCA-EF1E-4AFF-8F4C-522530B18D54}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {C3349FCA-EF1E-4AFF-8F4C-522530B18D54}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {C3349FCA-EF1E-4AFF-8F4C-522530B18D54}.Release|Any CPU.Build.0 = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(SolutionProperties) = preSolution
28 | HideSolutionNode = FALSE
29 | EndGlobalSection
30 | GlobalSection(ExtensibilityGlobals) = postSolution
31 | SolutionGuid = {F37BB785-3AB1-4498-8102-689C0E4C3924}
32 | EndGlobalSection
33 | EndGlobal
34 |
--------------------------------------------------------------------------------
/Public/Send-FTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Send-FTPFile {
2 | [alias('Add-FTPFile')]
3 | [cmdletBinding()]
4 | param(
5 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
6 | [string] $RemotePath,
7 | [System.IO.FileInfo[]] $LocalFile,
8 | [string[]] $LocalPath,
9 | [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
10 | [FluentFTP.FtpVerify] $VerifyOptions = [FluentFTP.FtpVerify]::None,
11 | [FluentFTP.FtpError] $ErrorHandling = [FluentFTP.FtpError]::None,
12 | [switch] $CreateRemoteDirectory
13 | )
14 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
15 | if ($LocalPath.Count -gt 1 -or $LocalFile.Count -gt 1) {
16 | $Splat = @{
17 | Client = $Client
18 | RemoteExists = $RemoteExists
19 | VerifyOptions = $VerifyOptions
20 | LocalPath = $LocalPath
21 | LocalFile = $LocalFile
22 | RemotePath = $RemotePath
23 | CreateRemoteDirectory = $CreateRemoteDirectory.IsPresent
24 | ErrorHandling = $ErrorHandling
25 | }
26 | Remove-EmptyValue -Hashtable $Splat
27 | $Status = Add-PrivateFTPFiles @Splat
28 | $Status
29 | } else {
30 | foreach ($Path in $LocalPath) {
31 | $Splat = @{
32 | Client = $Client
33 | RemoteExists = $RemoteExists
34 | VerifyOptions = $VerifyOptions
35 | LocalPath = $Path
36 | RemotePath = $RemotePath
37 | CreateRemoteDirectory = $CreateRemoteDirectory.IsPresent
38 | }
39 | $Status = Add-PrivateFTPFile @Splat
40 | $Status
41 | }
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/Public/Send-SFTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Send-SFTPFile {
2 | [alias('Add-SFTPFile')]
3 | [cmdletBinding()]
4 | param(
5 | [Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient,
6 | [string] $RemotePath,
7 | [string] $LocalPath,
8 | [switch] $AllowOverride
9 | )
10 | if ($SftpClient -and $SftpClient.IsConnected) {
11 | if (Test-Path -LiteralPath $LocalPath) {
12 | try {
13 | $FileStream = [System.IO.FileStream]::new($LocalPath, [System.IO.FileMode]::OpenOrCreate)
14 | $SftpClient.UploadFile($FileStream, $RemotePath, $AllowOverride)
15 | $Status = [PSCustomObject] @{
16 | Action = 'UploadFile'
17 | Status = $true
18 | LocalPath = $LocalPath
19 | RemotePath = $RemotePath
20 | Message = ""
21 | }
22 | } catch {
23 | $Status = [PSCustomObject] @{
24 | Action = 'UploadFile'
25 | Status = $false
26 | LocalPath = $LocalPath
27 | RemotePath = $RemotePath
28 | Message = "Error: $($_.Exception.Message)"
29 | }
30 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
31 | Write-Error $_
32 | return
33 | } else {
34 | Write-Warning "Send-SFTPFile - Error: $($_.Exception.Message)"
35 | }
36 | } finally {
37 | $FileStream.Close()
38 | }
39 | } else {
40 | Write-Warning "Send-SFTPFile - File $LocalPath doesn't exists."
41 | $Status = [PSCustomObject] @{
42 | Action = 'UploadFile'
43 | Status = $false
44 | LocalPath = $LocalPath
45 | RemotePath = $RemotePath
46 | Message = "LocalPath doesn't exists $LocalPath"
47 | }
48 | }
49 | $Status
50 | }
51 | }
--------------------------------------------------------------------------------
/Public/Set-FTPTracing.ps1:
--------------------------------------------------------------------------------
1 | function Set-FTPTracing {
2 | <#
3 | .SYNOPSIS
4 | Allows enabling/disabling tracing ftp commands being sent and received during communucation with the server.
5 |
6 | .DESCRIPTION
7 | Allows enabling/disabling tracing ftp commands being sent and received during communucation with the server.
8 |
9 | .PARAMETER Enable
10 | Enable tracing
11 |
12 | .PARAMETER Disable
13 | Disable tracing
14 |
15 | .PARAMETER ShowPassword
16 | Include FTP passwords in logs? Default: false.
17 |
18 | .PARAMETER HideUserName
19 | Hide FTP usernames in logs? Default: false.
20 |
21 | .PARAMETER HideIP
22 | Hide server IP addresses in logs? Default: true.
23 |
24 | .EXAMPLE
25 | Set-FTPTracing -Enable
26 |
27 | .NOTES
28 | General notes
29 | #>
30 | [cmdletBinding()]
31 | param(
32 | [switch] $Enable,
33 | [switch] $Disable,
34 | [switch] $ShowPassword,
35 | [switch] $HideUserName,
36 | [switch] $HideIP
37 | )
38 | $Script:GlobalFTPLogging = [ordered] @{}
39 | if ($Enable) {
40 | $Script:GlobalFTPLogging.LogToConsole = $true
41 | } elseif ($Disable) {
42 | #$Script:GlobalFTPLogging.LogToConsole = $false
43 | $Script:GlobalFTPLogging = $null
44 | return
45 | } else {
46 | Write-Warning -Message 'Please specify either -Enable or -Disable'
47 | return
48 | }
49 | if ($HideUserName) {
50 | $Script:GlobalFTPLogging.LogUserName = $false; # hide FTP user names
51 | } else {
52 | $Script:GlobalFTPLogging.LogUserName = $true; # show FTP user names
53 | }
54 | if ($ShowPassword) {
55 | $Script:GlobalFTPLogging.LogPassword = $false; # hide FTP passwords
56 | } else {
57 | $Script:GlobalFTPLogging.LogPassword = $true; # show FTP passwords
58 | }
59 | if (-not $HideIP) {
60 | $Script:GlobalFTPLogging.LogHost = $true; # hide FTP server IP addresses
61 | } else {
62 | $Script:GlobalFTPLogging.LogHost = $false; # show FTP server IP addresses
63 | }
64 | }
--------------------------------------------------------------------------------
/Private/Add-PrivateFTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Add-PrivateFTPFile {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [string] $RemotePath,
6 | [string] $LocalPath,
7 | [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
8 | [FluentFTP.FtpVerify] $VerifyOptions = [FluentFTP.FtpVerify]::None,
9 | [switch] $CreateRemoteDirectory
10 | )
11 | try {
12 | $Message = $Client.UploadFile($LocalPath, $RemotePath, $RemoteExists, $CreateRemoteDirectory.IsPresent, $VerifyOptions)
13 | if ($Message -eq 'success') {
14 | $State = $true
15 | } else {
16 | if ($Message -eq 'Skipped') {
17 | $State = $true
18 | } else {
19 | $State = $false
20 | }
21 | }
22 | $Status = [PSCustomObject] @{
23 | Action = 'UploadFile'
24 | Status = $State
25 | IsSuccess = if ($State) { $true } else { $false }
26 | IsSkipped = $Message -eq 'Skipped'
27 | IsSkippedByRule = $false
28 | IsFailed = if ($State) { $false } else { $true }
29 | LocalPath = $LocalPath
30 | RemotePath = $RemotePath
31 | Message = $Message
32 | }
33 | } catch {
34 | $Status = [PSCustomObject] @{
35 | Action = 'UploadFile'
36 | Status = $false
37 | IsSuccess = $false
38 | IsSkipped = $false
39 | IsSkippedByRule = $false
40 | IsFailed = $true
41 | LocalPath = $LocalPath
42 | RemotePath = $RemotePath
43 | Message = "Error: $($_.Exception.Message)"
44 | }
45 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
46 | Write-Error $_
47 | return
48 | } else {
49 | Write-Warning "Add-PrivateFTPFile - Error: $($_.Exception.Message)"
50 | }
51 | }
52 | $Status
53 | }
--------------------------------------------------------------------------------
/Transferetto.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | AliasesToExport = @('Get-FTPDirectory', 'Get-FTPFile', 'Get-SFTPFile', 'Add-FTPDirectory', 'Add-FTPFile', 'Add-SFTPFile', 'Start-FXPDirectory', 'Start-FXPFile')
3 | Author = 'Przemyslaw Klys'
4 | CmdletsToExport = @()
5 | CompanyName = 'Evotec'
6 | CompatiblePSEditions = @('Desktop', 'Core')
7 | Copyright = '(c) 2011 - 2024 Przemyslaw Klys @ Evotec. All rights reserved.'
8 | Description = 'Module which allows ftp, ftps, sftp file transfers with advanced features. It also allows to transfer files and directorires between servers using fxp protocol. As a side feature it allows to conenct to SSH and executes commands on it. '
9 | DotNetFrameworkVersion = '4.7.2'
10 | FunctionsToExport = @('Compare-FTPFile', 'Connect-FTP', 'Connect-SFTP', 'Connect-SSH', 'Disconnect-FTP', 'Disconnect-SFTP', 'Get-FTPChecksum', 'Get-FTPChmod', 'Get-FTPList', 'Get-SFTPList', 'Move-FTPDirectory', 'Move-FTPFile', 'Receive-FTPDirectory', 'Receive-FTPFile', 'Receive-SFTPFile', 'Remove-FTPDirectory', 'Remove-FTPFile', 'Remove-SFTPFile', 'Rename-FTPFile', 'Rename-SFTPFile', 'Request-FTPConfiguration', 'Send-FTPDirectory', 'Send-FTPFile', 'Send-SFTPFile', 'Send-SSHCommand', 'Set-FTPChmod', 'Set-FTPOption', 'Set-FTPTracing', 'Start-FXPDirectoryTransfer', 'Start-FXPFileTransfer', 'Test-FTPDirectory', 'Test-FTPFile')
11 | GUID = '7d61db15-9efe-41d1-a1c0-81d738975dec'
12 | ModuleVersion = '1.0.0'
13 | PowerShellVersion = '5.1'
14 | PrivateData = @{
15 | PSData = @{
16 | ExternalModuleDependencies = @('Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility')
17 | IconUri = 'https://evotec.xyz/wp-content/uploads/2021/03/Transferetto.png'
18 | ProjectUri = 'https://github.com/EvotecIT/Transferetto'
19 | Tags = @('Windows', 'Linux', 'MacOs', 'ftp', 'sftp', 'ftps', 'scp', 'winscp', 'ssh')
20 | }
21 | }
22 | RequiredModules = @('Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility')
23 | RootModule = 'Transferetto.psm1'
24 | }
--------------------------------------------------------------------------------
/Public/Rename-SFTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Rename-SFTPFile {
2 | <#
3 | .SYNOPSIS
4 | Allows renaming remote file over SFTP protocol
5 |
6 | .DESCRIPTION
7 | Allows renaming remote file over SFTP protocol
8 |
9 | .PARAMETER SftpClient
10 | Parameter that contains the SFTP client object
11 |
12 | .PARAMETER SourcePath
13 | Path to file that is going to be renamed
14 |
15 | .PARAMETER DestinationPath
16 | New path to file, with new name
17 |
18 | .PARAMETER Suppress
19 | Suppress returning an object with information about the operation
20 |
21 | .EXAMPLE
22 | $SftpClient = Connect-SFTP -Server '192.168.240.29' -Username 'przemek' -Password 'Password'
23 | Rename-SFTPFile -SftpClient $SftpClient -SourcePath '/home/przemek/mmm.txt' -DestinationPath '/home/przemek/mmm1.txt'
24 | Disconnect-SFTP -SftpClient $SftpClient
25 |
26 | .NOTES
27 | General notes
28 | #>
29 | [cmdletBinding()]
30 | param(
31 | [Parameter(Mandatory)][Renci.SshNet.SftpClient] $SftpClient,
32 | [alias('OldPath')][string] $SourcePath,
33 | [alias('NewPath')][string] $DestinationPath,
34 | [switch] $Suppress
35 | )
36 | if ($SftpClient -and $SftpClient.IsConnected) {
37 | try {
38 | $SftpClient.RenameFile($SourcePath, $DestinationPath)
39 | $Status = [PSCustomObject] @{
40 | Action = 'RenameFile'
41 | Status = $true
42 | OldPath = $SourcePath
43 | NewPath = $DestinationPath
44 | Message = ""
45 | }
46 | } catch {
47 | $Status = [PSCustomObject] @{
48 | Action = 'RenameFile'
49 | Status = $false
50 | OldPath = $SourcePath
51 | NewPath = $DestinationPath
52 | Message = "Error $($_.Exception.Message)"
53 | }
54 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
55 | Write-Error $_
56 | return
57 | } else {
58 | Write-Warning "Rename-SFTPFile - Error: $($_.Exception.Message)"
59 | }
60 | }
61 | if (-not $Suppress) {
62 | $Status
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/Transferetto.Tests.ps1:
--------------------------------------------------------------------------------
1 | $ModuleName = (Get-ChildItem $PSScriptRoot\*.psd1).BaseName
2 | $PrimaryModule = Get-ChildItem -Path $PSScriptRoot -Filter '*.psd1' -Recurse -ErrorAction SilentlyContinue -Depth 1
3 | if (-not $PrimaryModule) {
4 | throw "Path $PSScriptRoot doesn't contain PSD1 files. Failing tests."
5 | }
6 | if ($PrimaryModule.Count -ne 1) {
7 | throw 'More than one PSD1 files detected. Failing tests.'
8 | }
9 | $PSDInformation = Import-PowerShellDataFile -Path $PrimaryModule.FullName
10 | $RequiredModules = @(
11 | 'Pester'
12 | 'PSWriteColor'
13 | if ($PSDInformation.RequiredModules) {
14 | $PSDInformation.RequiredModules
15 | }
16 | )
17 | foreach ($Module in $RequiredModules) {
18 | if ($Module -is [System.Collections.IDictionary]) {
19 | $Exists = Get-Module -ListAvailable -Name $Module.ModuleName
20 | if (-not $Exists) {
21 | Write-Warning "$ModuleName - Downloading $($Module.ModuleName) from PSGallery"
22 | Install-Module -Name $Module.ModuleName -Force -SkipPublisherCheck -AllowClobber
23 | }
24 | } else {
25 | $Exists = Get-Module -ListAvailable $Module -ErrorAction SilentlyContinue
26 | if (-not $Exists) {
27 | Install-Module -Name $Module -Force -SkipPublisherCheck -AllowClobber
28 | }
29 | }
30 | }
31 |
32 | Write-Color 'ModuleName: ', $ModuleName, ' Version: ', $PSDInformation.ModuleVersion -Color Yellow, Green, Yellow, Green -LinesBefore 2
33 | Write-Color 'PowerShell Version: ', $PSVersionTable.PSVersion -Color Yellow, Green
34 | Write-Color 'PowerShell Edition: ', $PSVersionTable.PSEdition -Color Yellow, Green
35 | Write-Color 'Required modules: ' -Color Yellow
36 | foreach ($Module in $PSDInformation.RequiredModules) {
37 | if ($Module -is [System.Collections.IDictionary]) {
38 | Write-Color ' [>] ', $Module.ModuleName, ' Version: ', $Module.ModuleVersion -Color Yellow, Green, Yellow, Green
39 | } else {
40 | Write-Color ' [>] ', $Module -Color Yellow, Green
41 | }
42 | }
43 | Write-Color
44 |
45 | Import-Module $PSScriptRoot\*.psd1 -Force
46 | $result = Invoke-Pester -Script $PSScriptRoot\Tests -Verbose -PassThru
47 |
48 | if ($result.FailedCount -gt 0) {
49 | throw "$($result.FailedCount) tests failed."
50 | }
--------------------------------------------------------------------------------
/CHANGELOG.MD:
--------------------------------------------------------------------------------
1 | #### 1.0.0 - 2024.02.03
2 | - Downgraded `SSH.NET` to `2023.0.0` as issues with PS 7
3 | - Improved `Send-FTPFile`, `Receive-FTPFile` output objects
4 | - Added `TLS 1.3` support for `Connect-FTP` - hopefully it works
5 |
6 | #### 0.0.19 - 2024.02.01
7 | - Updated `FluentFTP` to `49.0.2`
8 | - Updated `SSH.NET` to `2023.0.1`
9 | - Added `TLS 1.3` support
10 |
11 | #### 0.0.18
12 | - Upgraded `FluentFTP` to `48.0.1`
13 | - Upgraded `SSH.NET` to `2023.0.0`
14 |
15 | #### 0.0.17 - 2022.12.04
16 | - Upgraded `FluentFTP` to `42.1.0`
17 |
18 | #### 0.0.16 - 2022.11.05
19 | - Fixes `Connect-FTP` options due to new version change of FLuentFTP
20 | - Upgraded `FluentFTP` to `40.0.0` to `42.0.0`
21 | - Added additional tests to try and be proactive!
22 | - Added basic support for Proxy (maybe works)
23 |
24 | #### 0.0.15 - 2022.09.22
25 | - Upgraded `FluentFTP` to `40.0.0`
26 |
27 | #### 0.0.14 - 2022.08.04
28 | - Fixes problem with missing function in PSGallery
29 |
30 | #### 0.0.13 - 2022.07.11
31 | - Upgraded `FluentFTP` to `37.0.6`
32 |
33 | #### 0.0.12 - 2022.06.17
34 | - Upgraded `SSH.NET` to `2020.0.2` which fixes weak private key generation - [SSH.NET Advisory](https://github.com/sshnet/SSH.NET/security/advisories/GHSA-72p8-v4hg-v45p)
35 | - Upgraded `FluentFTP` to `37.0.3.0`
36 | - Fixes `Set-FTPTracing`
37 |
38 | #### 0.0.11 - 2022.04.10
39 | - Small docs update to `Set-FTPTracing`
40 | - Changed `ShowUsername` to `HideUsername` in `Set-FTPTracing`
41 | - Added detection for `netframework` to be minimum 4.7.2
42 | - Upgraded `FluentFTP` to `37.0.2.0`
43 | #### 0.0.10 - 2021.12.22
44 | - Fixes `Connect-SFTP` port functionality
45 | - Fixes `Connect-SSH` port functionality
46 | #### 0.0.9 - 2021.10.29
47 | - Fixes `Rename-SFTPFile` - reported [#7](https://github.com/EvotecIT/Transferetto/issues/7)
48 |
49 | #### 0.0.8 - 2021.09.15
50 | - Added command `Test-FTPDirectory` - thank you [Sidewinder53](https://github.com/EvotecIT/Transferetto/pull/5)
51 |
52 | #### 0.0.7 - 2021.09.15
53 | - Republished module to PowerShell Gallery
54 |
55 | #### 0.0.6 - 2021.09.14
56 | - Added support for key authentication in `Connect-SFTP` - thank you [Szeraax!](https://github.com/EvotecIT/Transferetto/pull/3)
57 | - Improved error handling of `Connect-SFTP`
58 |
59 | #### 0.0.5 - 2021.04.04
60 | - Small fixes
61 |
62 | #### 0.0.4 - 2021.03.29
63 | - First edition
64 |
--------------------------------------------------------------------------------
/Private/Add-PrivateFTPFiles.ps1:
--------------------------------------------------------------------------------
1 | function Add-PrivateFTPFiles {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [string] $RemotePath,
6 | [string[]] $LocalPath,
7 | [System.IO.FileInfo[]] $LocalFile,
8 | [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
9 | [FluentFTP.FtpVerify] $VerifyOptions = [FluentFTP.FtpVerify]::None,
10 | [FluentFTP.FtpError] $ErrorHandling = [FluentFTP.FtpError]::None,
11 | [switch] $CreateRemoteDirectory
12 | )
13 | $ErrorFound = $null
14 | try {
15 | if ($LocalFile) {
16 | $Message = $Client.UploadFiles([System.IO.FileInfo[]] $LocalFile, $RemotePath, $RemoteExists, $CreateRemoteDirectory.IsPresent, $VerifyOptions, $ErrorHandling)
17 | } else {
18 | $Message = $Client.UploadFiles([string[]] $LocalPath, $RemotePath, $RemoteExists, $CreateRemoteDirectory.IsPresent, $VerifyOptions, $ErrorHandling)
19 | }
20 | } catch {
21 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
22 | Write-Error $_
23 | return
24 | } else {
25 | Write-Warning "Add-PrivateFTPFiles - Error: $($_.Exception.Message)"
26 | }
27 | $ErrorFound = $($_.Exception.Message)
28 | }
29 | if (-not $ErrorFound) {
30 | foreach ($M in $Message) {
31 | [PSCustomObject] @{
32 | Action = 'UploadFile'
33 | Status = $M.IsSuccess
34 | IsSuccess = $M.IsSuccess
35 | IsSkipped = $M.IsSkipped
36 | IsSkippedByRule = $M.IsSkippedByRule
37 | IsFailed = $M.IsFailed
38 | LocalPath = $M.LocalPath
39 | RemotePath = $M.RemotePath
40 | Message = if ($M.IsSkipped -eq $true) { "Skipped" } else { 'Success' }
41 | }
42 | }
43 | } else {
44 | [PSCustomObject] @{
45 | Action = 'UploadFile'
46 | Status = $false
47 | IsSuccess = $false
48 | IsSkipped = $false
49 | IsSkippedByRule = $false
50 | IsFailed = $true
51 | LocalPath = $LocalPath
52 | RemotePath = $RemotePath
53 | Message = $ErrorFound
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/Public/Send-FTPDirectory.ps1:
--------------------------------------------------------------------------------
1 | function Send-FTPDirectory {
2 | <#
3 | .SYNOPSIS
4 | Uploads a directory to an FTP server.
5 |
6 | .DESCRIPTION
7 | Uploads a directory to an FTP server.
8 |
9 | .PARAMETER Client
10 | The Client to use for connection.
11 |
12 | .PARAMETER LocalPath
13 | Path on the local machine to upload to FTP Server
14 |
15 | .PARAMETER RemotePath
16 | Path on the FTP Server where to upload the content
17 |
18 | .PARAMETER FolderSyncMode
19 | Update - upload a folder and all its files
20 | Mirror - upload a folder and all its files, and delete extra files on the server
21 |
22 | .PARAMETER RemoteExists
23 | Provide decision what to do when file on the server exits.
24 | Options available: Append, AppendNoChek, NoCheck, Skip, , Overwrite
25 | Default: Skip.
26 |
27 | .PARAMETER VerifyOptions
28 | Provide options for verification of files on the remote server.
29 | Options available: Delete, OnlyChecksum, None, Retry
30 | Default: None.
31 |
32 | .PARAMETER Rules
33 | Provide rules and conditions for uploading files.
34 |
35 | .EXAMPLE
36 | Set-FTPTracing -Enable -DisplayConsole
37 |
38 | $Client = Connect-FTP -Server '192.168.241.187' -Verbose -Username 'test' -Password 'BiPassword90A' -EncryptionMode Explicit -ValidateAnyCertificate
39 | $Upload = Send-FTPDirectory -Client $Client -LocalPath $PSScriptRoot\Upload -RemotePath '/Temporary' -Verbose -FolderSyncMode Update
40 | $Upload | Format-Table *
41 |
42 | Disconnect-FTP -Client $Client
43 |
44 | Set-FTPTracing -Disable
45 |
46 | .NOTES
47 | General notes
48 | #>
49 | [alias('Add-FTPDirectory')]
50 | [cmdletBinding()]
51 | param(
52 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
53 | [string] $LocalPath,
54 | [Parameter(Mandatory)][string] $RemotePath,
55 | [FluentFTP.FtpFolderSyncMode] $FolderSyncMode = [FluentFTP.FtpFolderSyncMode]::Update,
56 | [FluentFTP.FtpRemoteExists] $RemoteExists = [FluentFTP.FtpRemoteExists]::Skip,
57 | [FluentFTP.FtpVerify] $VerifyOptions = [FluentFTP.FtpVerify]::None,
58 | [FluentFTP.Rules.FtpRule[]] $Rules
59 | )
60 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
61 | if ($Rules) {
62 | $Client.UploadDirectory($LocalPath, $RemotePath, $FolderSyncMode, $RemoteExists, $VerifyOptions, @($Rules))
63 | } else {
64 | $Client.UploadDirectory($LocalPath, $RemotePath, $FolderSyncMode, $RemoteExists, $VerifyOptions)
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/Public/Connect-SSH.ps1:
--------------------------------------------------------------------------------
1 | function Connect-SSH {
2 | [cmdletBinding(DefaultParameterSetName = 'Password')]
3 | param(
4 | [Parameter(Mandatory, ParameterSetName = 'ClearText')]
5 | [Parameter(Mandatory, ParameterSetName = 'Password')]
6 | [Parameter(Mandatory, ParameterSetName = 'PrivateKey')]
7 | [string] $Server,
8 |
9 | [Parameter(Mandatory, ParameterSetName = 'ClearText')]
10 | [Parameter(Mandatory, ParameterSetName = 'PrivateKey')]
11 | [string] $Username,
12 |
13 | [Parameter(Mandatory, ParameterSetName = 'ClearText')]
14 | [string] $Password,
15 |
16 | [Parameter(Mandatory, ParameterSetName = 'Password')]
17 | [pscredential] $Credential,
18 |
19 | [Parameter(Mandatory, ParameterSetName = 'PrivateKey')]
20 | [string] $PrivateKey,
21 |
22 | [Parameter(ParameterSetName = 'ClearText')]
23 | [Parameter(ParameterSetName = 'Password')]
24 | [Parameter(ParameterSetName = 'PrivateKey')]
25 | [int] $Port
26 | )
27 |
28 | if ($Username -and $Password) {
29 | if ($Port) {
30 | $SshClient = [Renci.SshNet.SshClient]::new($Server, $Port, $Username, $Password)
31 | } else {
32 | $SshClient = [Renci.SshNet.SshClient]::new($Server, $Username, $Password)
33 | }
34 | } elseif ($Credential) {
35 | if ($Port) {
36 | $SshClient = [Renci.SshNet.SshClient]::new($Server, $Port, $Credential.Username, $Credential.GetNetworkCredential().Password)
37 | } else {
38 | $SshClient = [Renci.SshNet.SshClient]::new($Server, $Credential.Username, $Credential.GetNetworkCredential().Password)
39 | }
40 | } elseif ($PrivateKey) {
41 | [string]$PrivateKey = Resolve-Path $PrivateKey | Select-Object -ExpandProperty ProviderPath
42 | if ($Port) {
43 | $SshClient = [Renci.SshNet.SshClient]::new($Server, $Port, $Username, [Renci.SshNet.PrivateKeyFile]$PrivateKey )
44 | } else {
45 | $SshClient = [Renci.SshNet.SshClient]::new($Server, $Username, [Renci.SshNet.PrivateKeyFile]$PrivateKey )
46 | }
47 | } else {
48 | throw 'Not implemented and unexpected.'
49 | }
50 |
51 | try {
52 | $SshClient.Connect()
53 | $SshClient | Add-Member -Name 'Error' -Value $null -Force -MemberType NoteProperty
54 | } catch {
55 | $SshClient | Add-Member -Name 'Error' -Value $($_.Exception.Message) -Force -MemberType NoteProperty
56 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
57 | Write-Error $_
58 | return
59 | } else {
60 | Write-Warning "Connect-SSH - Error: $($_.Exception.Message)"
61 | }
62 | }
63 | $SshClient
64 | }
65 |
--------------------------------------------------------------------------------
/Private/Get-PrivateFTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Get-PrivateFTPFile {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [string] $LocalPath,
6 | [FluentFTP.FtpListItem] $RemoteFile,
7 | [string] $RemotePath,
8 | [FluentFTP.FtpLocalExists] $LocalExists,
9 | [FluentFTP.FtpVerify[]] $VerifyOptions,
10 | [FluentFTP.FtpError] $FtpError
11 | )
12 | if ($RemoteFile) {
13 | if ($RemoteFile.Type -eq 'File') {
14 | $FileToDownload = $RemoteFile.FullName
15 | } else {
16 | if (-not $Suppress) {
17 | return [PSCustomObject] @{
18 | Action = 'DownloadFile'
19 | Status = $false
20 | IsSuccess = $false
21 | IsSkipped = $true
22 | IsSkippedByRule = $false
23 | LocalPath = $LocalPath
24 | RemotePath = $RemoteFile.FullName
25 | Message = "Receive-FTPFile - Given path $($RemoteFile.FullName) is $($RemoteFile.Type). Skipping."
26 | }
27 | } else {
28 | Write-Warning "Receive-FTPFile - Given path $($RemoteFile.FullName) is a directory. Skipping."
29 | return
30 | }
31 | }
32 | } else {
33 | $FileToDownload = $RemotePath
34 | }
35 | try {
36 | $Message = $Client.DownloadFile($LocalPath, $FileToDownload, $LocalExists, $VerifyOptions)
37 | if ($Message -eq 'success') {
38 | $State = $true
39 | } else {
40 | $State = $false
41 | }
42 | $Status = [PSCustomObject] @{
43 | Action = 'DownloadFile'
44 | Status = $State
45 | IsSuccess = if ($State) { $true } else { $false }
46 | IsSkipped = $false
47 | IsSkippedByRule = $false
48 | IsFailed = if ($State) { $false } else { $true }
49 | LocalPath = $LocalPath
50 | RemotePath = $FileToDownload
51 | Message = $Message
52 | }
53 | } catch {
54 | $Status = [PSCustomObject] @{
55 | Action = 'DownloadFile'
56 | Status = $false
57 | IsSuccess = $false
58 | IsSkipped = $false
59 | IsSkippedByRule = $false
60 | IsFailed = $true
61 | LocalPath = $LocalPath
62 | RemotePath = $FileToDownload
63 | Message = "Error: $($_.Exception.Message)"
64 | }
65 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
66 | Write-Error $_
67 | return
68 | } else {
69 | Write-Warning "Receive-FTPFile - Error: $($_.Exception.Message)"
70 | }
71 | }
72 | $Status
73 | }
--------------------------------------------------------------------------------
/Private/Get-PrivateFTPFiles.ps1:
--------------------------------------------------------------------------------
1 | function Get-PrivateFTPFiles {
2 | [cmdletBinding()]
3 | param(
4 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
5 | [string] $LocalPath,
6 | [FluentFTP.FtpListItem[]] $RemoteFile,
7 | [string[]] $RemotePath,
8 | [FluentFTP.FtpLocalExists] $LocalExists,
9 | [FluentFTP.FtpVerify[]] $VerifyOptions,
10 | [FluentFTP.FtpError] $FtpError
11 | )
12 |
13 | if ($RemoteFile) {
14 | $FileToDownload = foreach ($File in $RemoteFile) {
15 | if ($File.Type -eq 'File') {
16 | $File.FullName
17 | } else {
18 | if (-not $Suppress) {
19 | [PSCustomObject] @{
20 | Action = 'DownloadFile'
21 | Status = $false
22 | IsSuccess = $false
23 | IsSkipped = $true
24 | IsSkippedByRule = $false
25 | LocalPath = $LocalPath
26 | RemotePath = $File.FullName
27 | Message = "Receive-FTPFile - Given path $($RemoteFile.FullName) is $($RemoteFile.Type). Skipping."
28 | }
29 | } else {
30 | Write-Warning "Receive-FTPFile - Given path $($RemoteFile.FullName) is a directory. Skipping."
31 | }
32 | }
33 | }
34 | } else {
35 | $FileToDownload = $RemotePath
36 | }
37 | try {
38 | $Message = $Client.DownloadFiles($LocalPath, ([string[]] $FileToDownload), $LocalExists, $VerifyOptions, $FtpError)
39 | } catch {
40 | $Status = [PSCustomObject] @{
41 | Action = 'DownloadFile'
42 | Status = $false
43 | IsSuccess = $false
44 | IsSkipped = $false
45 | IsSkippedByRule = $false
46 | IsFailed = $true
47 | LocalPath = $LocalPath
48 | RemotePath = $FileToDownload
49 | Message = "Error: $($_.Exception.Message)"
50 | }
51 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
52 | Write-Error $_
53 | return
54 | } else {
55 | Write-Warning "Receive-FTPFile - Error: $($_.Exception.Message)"
56 | return $Status
57 | }
58 | }
59 | if (-not $Status) {
60 | foreach ($M in $Message) {
61 | [PSCustomObject] @{
62 | Action = 'DownloadFile'
63 | Status = $M.IsSuccess
64 | IsSuccess = $M.IsSuccess
65 | IsSkipped = $M.IsSkipped
66 | IsSkippedByRule = $M.IsSkippedByRule
67 | IsFailed = $M.IsFailed
68 | LocalPath = $LocalPath
69 | RemotePath = $M.RemotePath
70 | Message = $M
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/Public/Receive-FTPFile.ps1:
--------------------------------------------------------------------------------
1 | function Receive-FTPFile {
2 | [alias('Get-FTPFile')]
3 | [cmdletBinding(DefaultParameterSetName = 'Text')]
4 | param(
5 | [Parameter(ParameterSetName = 'Text')]
6 | [Parameter(ParameterSetName = 'Native')]
7 | [Parameter(Mandatory)][FluentFTP.FtpClient] $Client,
8 |
9 | [Parameter(ParameterSetName = 'Native')]
10 | [FluentFTP.FtpListItem[]] $RemoteFile,
11 |
12 | [Parameter(ParameterSetName = 'Text')]
13 | [Parameter(ParameterSetName = 'Native')]
14 | [string[]] $RemotePath,
15 |
16 | [Parameter(ParameterSetName = 'Text')]
17 | [Parameter(ParameterSetName = 'Native')]
18 | [string] $LocalPath,
19 |
20 | [Parameter(ParameterSetName = 'Text')]
21 | [Parameter(ParameterSetName = 'Native')]
22 | [FluentFTP.FtpLocalExists] $LocalExists = [FluentFTP.FtpLocalExists]::Skip,
23 |
24 | [Parameter(ParameterSetName = 'Text')]
25 | [Parameter(ParameterSetName = 'Native')]
26 | [FluentFTP.FtpVerify[]] $VerifyOptions = [FluentFTP.FtpVerify]::None,
27 |
28 | [Parameter(ParameterSetName = 'Text')]
29 | [Parameter(ParameterSetName = 'Native')]
30 | [FluentFTP.FtpError] $FtpError = [FluentFTP.FtpError]::Stop,
31 |
32 | [Parameter(ParameterSetName = 'Text')]
33 | [Parameter(ParameterSetName = 'Native')]
34 | [switch] $Suppress
35 | )
36 | if ($Client -and $Client.IsConnected -and -not $Client.Error) {
37 | $Path = Get-Item -LiteralPath $LocalPath -ErrorAction SilentlyContinue
38 | if ($Path -is [System.IO.DirectoryInfo]) {
39 | Get-PrivateFTPFiles -Client $Client -LocalPath $LocalPath -RemoteFile $RemoteFile -RemotePath $RemotePath -LocalExists $LocalExists -VerifyOptions $VerifyOptions -FtpError $FtpError
40 | } else {
41 | if ($RemoteFile.Count -gt 1 -or $RemotePath.Count -gt 1) {
42 | Write-Warning "Receive-FTPFile - Multiple files detected, but $LocalPath is not a directory or directory doesn't exists. "
43 | if ($RemoteFile) {
44 | $FileToDownload = $RemoteFile.FullName
45 | } else {
46 | $FileToDownload = $RemotePath
47 | }
48 | $Status = [PSCustomObject] @{
49 | Action = 'DownloadFile'
50 | Status = $false
51 | LocalPath = $LocalPath
52 | RemotePath = $FileToDownload
53 | Message = "Multiple files detected, but $LocalPath is not a directory or directory doesn't exists."
54 | }
55 | } else {
56 | $Splat = @{
57 | Client = $Client
58 | LocalExists = $LocalExists
59 | VerifyOptions = $VerifyOptions
60 | FtpError = $FtpError
61 | LocalPath = $LocalPath
62 | }
63 | if ($RemoteFile) {
64 | $Splat.RemoteFile = $RemoteFile[0]
65 | } else {
66 | $Splat.RemotePath = $RemotePath[0]
67 | }
68 | Get-PrivateFTPFile @Splat
69 | }
70 | }
71 | } else {
72 | $Status = [PSCustomObject] @{
73 | Action = 'DownloadFile'
74 | Status = $false
75 | LocalPath = $LocalPath
76 | RemotePath = $FileToDownload
77 | Message = "Not connected."
78 | }
79 | }
80 | if (-not $Suppress) {
81 | $Status
82 | }
83 | }
--------------------------------------------------------------------------------
/Public/Connect-SFTP.ps1:
--------------------------------------------------------------------------------
1 | function Connect-SFTP {
2 | [cmdletBinding(DefaultParameterSetName = 'Password')]
3 | param(
4 | [Parameter(ParameterSetName = 'ClearText', Mandatory)]
5 | [Parameter(ParameterSetName = 'Password', Mandatory)]
6 | [Parameter(ParameterSetName = 'PrivateKey', Mandatory)]
7 | [string] $Server,
8 |
9 | [Parameter(ParameterSetName = 'ClearText', Mandatory)]
10 | [Parameter(ParameterSetName = 'PrivateKey', Mandatory)]
11 | [string] $Username,
12 |
13 | [Parameter(ParameterSetName = 'ClearText', Mandatory)]
14 | [string] $Password,
15 |
16 | [Parameter(ParameterSetName = 'Password', Mandatory)]
17 | [pscredential] $Credential,
18 |
19 | [Parameter(Mandatory, ParameterSetName = 'PrivateKey')]
20 | [string] $PrivateKey,
21 |
22 | [Parameter(ParameterSetName = 'ClearText')]
23 | [Parameter(ParameterSetName = 'Password')]
24 | [Parameter(ParameterSetName = 'PrivateKey')]
25 | [int] $Port
26 | )
27 | try {
28 | if ($Username -and $Password) {
29 | if ($Port) {
30 | $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Port, $Username, $Password)
31 | } else {
32 | $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Username, $Password)
33 | }
34 | } elseif ($Credential) {
35 | if ($Port) {
36 | $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Port, $Credential.Username, $Credential.GetNetworkCredential().Password)
37 | } else {
38 | $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Credential.Username, $Credential.GetNetworkCredential().Password)
39 | }
40 | } elseif ($PrivateKey) {
41 | if (Test-Path -LiteralPath $PrivateKey) {
42 | [string]$PrivateKey = Resolve-Path -LiteralPath $PrivateKey -ErrorAction Stop | Select-Object -ExpandProperty ProviderPath
43 | if ($Port) {
44 | $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Port, $Username, [Renci.SshNet.PrivateKeyFile]$PrivateKey )
45 | } else {
46 | $SftpClient = [Renci.SshNet.SftpClient]::new($Server, $Username, [Renci.SshNet.PrivateKeyFile]$PrivateKey )
47 | }
48 | } else {
49 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
50 | throw "PrivateKey $PrivateKey doesn't exists."
51 | return
52 | } else {
53 | Write-Warning "Connect-SFTP - PrivateKey $PrivateKey doesn't exists."
54 | return
55 | }
56 | }
57 | } else {
58 | throw 'Not implemented and unexepected.'
59 | return
60 | }
61 | } catch {
62 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
63 | Write-Error $_
64 | return
65 | } else {
66 | Write-Warning "Connect-SFTP - Error: $($_.Exception.Message)"
67 | }
68 | }
69 |
70 | try {
71 | $SftpClient.Connect()
72 | $SftpClient | Add-Member -Name 'Error' -Value $null -Force -MemberType NoteProperty
73 | } catch {
74 | $SftpClient | Add-Member -Name 'Error' -Value $($_.Exception.Message) -Force -MemberType NoteProperty
75 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
76 | Write-Error $_
77 | return
78 | } else {
79 | Write-Warning "Connect-SFTP - Error: $($_.Exception.Message)"
80 | }
81 | }
82 | $SftpClient
83 | }
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Transferetto - PowerShell Module
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Transferetto is a PowerShell module that aims to provide FTP, FTPS, and SFTP functionality.
24 | To find out more about it I've created a blog post [Easy](https://evotec.xyz/easy-way-to-connect-to-ftps-and-sftp-using-powershell/) way to connect to FTPS and SFTP using PowerShell](https://evotec.xyz/easy-way-to-connect-to-ftps-and-sftp-using-powershell/).
25 | It uses the following .NET libraries to deliver this functionality:
26 |
27 | - [FluentFTP](https://github.com/robinrodricks/FluentFTP)
28 | - [SSH.NET](https://github.com/sshnet/SSH.NET/)
29 |
30 | Both libraries are MIT licenses.
31 |
32 | ## Features
33 |
34 | - FTPS/SFTP functionality
35 | - Connect to FTP, FTPS, SFTP
36 | - Upload/Download files from FTP/FTPS/SFTP
37 | - Rename SFTP files
38 | - Remove FTP/FTPS files
39 | - And some more
40 |
41 | Please make sure to read blog post or check examples to see how to use it.
42 |
43 | ## To install
44 |
45 | ```powershell
46 | Install-Module -Name Transferetto -AllowClobber -Force
47 | ```
48 |
49 | Force and AllowClobber aren't necessary, but they do skip errors in case some appear.
50 |
51 | ## And to update
52 |
53 | ```powershell
54 | Update-Module -Name Transferetto
55 | ```
56 |
57 | That's it. Whenever there's a new version, you run the command, and you can enjoy it. Remember that you may need to close, reopen PowerShell session if you have already used module before updating it.
58 |
59 | **The essential thing** is if something works for you on production, keep using it till you test the new version on a test computer. I do changes that may not be big, but big enough that auto-update may break your code. For example, a small rename to a parameter, and your code stops working! Be responsible!
--------------------------------------------------------------------------------
/Sources/Transferetto/OnImportAndRemove.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Management.Automation;
4 | using System.Reflection;
5 |
6 | public class OnModuleImportAndRemove : IModuleAssemblyInitializer, IModuleAssemblyCleanup {
7 | public void OnImport() {
8 | //#if FRAMEWORK
9 | AppDomain.CurrentDomain.AssemblyResolve += MyResolveEventHandler;
10 | //#endif
11 | }
12 |
13 | public void OnRemove(PSModuleInfo module) {
14 | //#if FRAMEWORK
15 | AppDomain.CurrentDomain.AssemblyResolve -= MyResolveEventHandler;
16 | //#endif
17 | }
18 |
19 | private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args) {
20 | // These are known to be problematic in .NET Framework, force it to use our packaged dlls.
21 | if (args.Name.StartsWith("System.Memory,")) {
22 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "System.Memory.dll");
23 | return Assembly.LoadFile(binPath);
24 | } else if (args.Name.StartsWith("System.Runtime.CompilerServices.Unsafe,")) {
25 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "System.Runtime.CompilerServices.Unsafe.dll");
26 | return Assembly.LoadFile(binPath);
27 | } else if (args.Name.StartsWith("System.Numerics.Vectors,")) {
28 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "System.Numerics.Vectors.dll");
29 | return Assembly.LoadFile(binPath);
30 | } else if (args.Name.StartsWith("System.Drawing.Common,")) {
31 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "System.Drawing.Common.dll");
32 | return Assembly.LoadFile(binPath);
33 | } else if (args.Name.StartsWith("System.Buffers,")) {
34 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "System.Buffers.dll");
35 | return Assembly.LoadFile(binPath);
36 | } else if (args.Name.StartsWith("System.ValueTuple,")) {
37 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "System.ValueTuple.dll");
38 | return Assembly.LoadFile(binPath);
39 | } else if (args.Name.StartsWith("System.Text.Encoding.CodePages,")) {
40 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "System.Text.Encoding.CodePages.dll");
41 | return Assembly.LoadFile(binPath);
42 | } else if (args.Name.StartsWith("BouncyCastle.Cryptography,")) {
43 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "BouncyCastle.Cryptography.dll");
44 | return Assembly.LoadFile(binPath);
45 | } else if (args.Name.StartsWith("Newtonsoft.Json,")) {
46 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "Newtonsoft.Json.dll");
47 | return Assembly.LoadFile(binPath);
48 | } else if (args.Name.StartsWith("Microsoft.Identity.Client,")) {
49 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "Microsoft.Identity.Client.dll");
50 | return Assembly.LoadFile(binPath);
51 | } else if (args.Name.StartsWith("FluentFTP,")) {
52 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "FluentFTP.dll");
53 | return Assembly.LoadFile(binPath);
54 | } else if (args.Name.StartsWith("Microsoft.Bcl.AsyncInterfaces,")) {
55 | string binPath = Path.Combine(Path.GetDirectoryName(typeof(OnModuleImportAndRemove).Assembly.Location), "Microsoft.Bcl.AsyncInterfaces.dll");
56 | return Assembly.LoadFile(binPath);
57 | }
58 | return null;
59 | }
60 | }
--------------------------------------------------------------------------------
/Public/Request-FTPConfiguration.ps1:
--------------------------------------------------------------------------------
1 | function Request-FTPConfiguration {
2 | <#
3 | .SYNOPSIS
4 | Short description
5 |
6 | .DESCRIPTION
7 | Automatically discover working FTP connection settings and return those connection profiles. This method will try every possible connection type combination in a loop until it finds a working combination, and it will return the first found combination or all found combinations. The connection types are tried in this order of preference.
8 |
9 | Auto connection attempts to find working connection settings in this order of preference:
10 |
11 | Protocol Preference:
12 | 1. None - Let the OS decide which TLS/SSL version to use
13 | 2. Tls12 - TLS 1.2 (TLS 1.3 is not yet stable in .NET Framework)
14 | 3. Tls11 - TLS 1.1
15 | 4. Tls - TLS 1.0
16 | 5. Ssl3 - SSL 3.0 (obsolete, need to use TLS instead)
17 | 6. Ssl2 - SSL 2.0 (obsolete, need to use TLS instead)
18 | 7. Default - Undefined/weird behaviour
19 |
20 | Data Connection Type Preference:
21 |
22 | 1. PASV - We prefer passive as its the most reliable
23 | 2. EPSV - Enhanced passive is not as well supported on servers
24 | 3. PORT - PORT is an older connection type
25 | 4. EPRT - Enhanced PORT is not as well supported on servers
26 | 5. PASVEX
27 |
28 | Encoding Type Preference:
29 |
30 | 1. UTF8 - We prefer Unicode encoding as there will be no issues with file and folder names
31 | 2. ASCII - ASCII/ANSI is a fallback used for older servers
32 |
33 | .PARAMETER Server
34 | Server Name or IP Address to Connect
35 |
36 | .PARAMETER Username
37 | UserName for FTP Connection
38 |
39 | .PARAMETER Password
40 | Password for FTP Connection (cleartext)
41 |
42 | .PARAMETER Credential
43 | UserName and Password in form of Credentials
44 |
45 | .PARAMETER FirstOnly
46 | Returns first working profile
47 |
48 | .EXAMPLE
49 | # Login via UserName/Password
50 | $ProfileFtp1 = Request-FTPConfiguration -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password'
51 | $ProfileFtp1 | Format-Table
52 |
53 | .EXAMPLE
54 | # Anonymous login
55 | $ProfileFtp2 = Request-FTPConfiguration -Server 'speedtest.tele2.net' -Verbose
56 | $ProfileFtp2 | Format-Table
57 |
58 | .NOTES
59 | General notes
60 | #>
61 | [cmdletBinding(DefaultParameterSetName = 'Password')]
62 | param(
63 | [Parameter(ParameterSetName = 'ClearText')]
64 | [Parameter(ParameterSetName = 'Password')]
65 | [string] $Server,
66 |
67 | [Parameter(ParameterSetName = 'ClearText')]
68 | [string] $Username,
69 |
70 | [Parameter(ParameterSetName = 'ClearText')]
71 | [string] $Password,
72 |
73 | [Parameter(ParameterSetName = 'Password')]
74 | [pscredential] $Credential,
75 |
76 | [Parameter(ParameterSetName = 'ClearText')]
77 | [Parameter(ParameterSetName = 'Password')]
78 | [int] $Port,
79 |
80 | [Parameter(ParameterSetName = 'ClearText')]
81 | [Parameter(ParameterSetName = 'Password')]
82 | [switch] $FirstOnly
83 | )
84 | $Client = [FluentFTP.FtpClient]::new($Server)
85 | if ($Port) {
86 | $Client.Port = $Port
87 | }
88 | if ($Username -and $Password) {
89 | $Client.Credentials = [System.Net.NetworkCredential]::new($Username, $Password)
90 | } elseif ($Credential) {
91 | $Client.Credentials = [System.Net.NetworkCredential]::new($Credential.Username, $Credential.Password)
92 | } else {
93 | # anonymous
94 | }
95 | try {
96 | $Client.AutoDetect($FirstOnly.IsPresent)
97 | } catch {
98 | $Client | Add-Member -Name 'Error' -Value $($_.Exception.Message) -Force -MemberType NoteProperty
99 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
100 | Write-Error $_
101 | return
102 | } else {
103 | Write-Warning "Request-FTPConfiguration - Error: $($_.Exception.Message)"
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/Tests/Connect-FTP.Tests.ps1:
--------------------------------------------------------------------------------
1 | Describe 'Connect-FTP / Disconnect-FTP' {
2 | It 'Given no login and password it should connect to FTP and list' {
3 | # Anonymous login
4 | $Client = $Null
5 | $Client = Connect-FTP -Server 'ftp.gnu.org' -Verbose
6 | $Client.Host | Should -Be 'ftp.gnu.org'
7 | $Client.IsConnected | Should -Be $True
8 | $List = Get-FTPList -Client $Client
9 | $List.Count | Should -BeGreaterThan 5
10 | $Names = 'Name', 'Type', 'FullName', 'Modified', 'Created'
11 | foreach ($Name in @($Names)) {
12 | $List[0].PSObject.Properties.Name | Should -Contain $Name
13 | }
14 | Disconnect-FTP -Client $Client
15 | $Client.IsConnected | Should -Be $false
16 | }
17 | It 'Given login and password and Encryption Mode it should connect to FTP and list' {
18 | # Anonymous login
19 | $Client = $Null
20 | $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password' -EncryptionMode Auto -SslBuffering Auto -SocketKeepAlive
21 | $Client.Host | Should -Be 'test.rebex.net'
22 | $Client.IsConnected | Should -Be $True
23 | $List = Get-FTPList -Client $Client
24 | $List.Count | Should -Be 2
25 | $Names = 'Name', 'Type', 'FullName', 'Modified', 'Created'
26 | foreach ($Name in @($Names)) {
27 | $List[0].PSObject.Properties.Name | Should -Contain $Name
28 | }
29 | Disconnect-FTP -Client $Client
30 | $Client.IsConnected | Should -Be $false
31 | }
32 | It 'Given login and password it should connect to FTP' {
33 | # Login via UserName/Password
34 | $Client = $Null
35 | $Client = Connect-FTP -Server 'ftp.dlptest.com' -Verbose -Username 'dlpuser' -Password 'rNrKYTX9g7z3RgJRmxWuGHbeu'
36 | $Client.Host | Should -Be 'ftp.dlptest.com'
37 | $Client.IsConnected | Should -Be $True
38 | $List = Get-FTPList -Client $Client
39 | $List.Count | Should -BeGreaterThan 1
40 | $Names = 'Name', 'Type', 'FullName', 'Modified', 'Created'
41 | foreach ($Name in @($Names)) {
42 | $List[0].PSObject.Properties.Name | Should -Contain $Name
43 | }
44 | Disconnect-FTP -Client $Client
45 | $Client.IsConnected | Should -Be $false
46 | }
47 | It 'Given login and password, and proxy Socks 5 it should conntect and list' {
48 | # Anonymous login
49 | $Client = $Null
50 | $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password' -EncryptionMode Auto -SslBuffering Auto -SocketKeepAlive -ProxyHost '192.252.216.81' -ProxyPort 4145 -ProxyType FtpClientSocks5Proxy
51 | $Client.Host | Should -Be 'test.rebex.net'
52 | $Client.IsConnected | Should -Be $True
53 | $List = Get-FTPList -Client $Client
54 | $List.Count | Should -Be 2
55 | $Names = 'Name', 'Type', 'FullName', 'Modified', 'Created'
56 | foreach ($Name in @($Names)) {
57 | $List[0].PSObject.Properties.Name | Should -Contain $Name
58 | }
59 | Disconnect-FTP -Client $Client
60 | $Client.IsConnected | Should -Be $false
61 | }
62 |
63 | # It 'Given login and password, and proxy Socks 4a it should conntect and list' {
64 | # # Anonymous login
65 | # $Client = $Null
66 | # $Client = Connect-FTP -Server 'test.rebex.net' -Verbose -Username 'demo' -Password 'password' -EncryptionMode Auto -SslBuffering Auto -SocketKeepAlive -ProxyHost '104.37.135.145' -ProxyPort 4145 -ProxyType FtpClientSocks4aProxy
67 | # $Client.Host | Should -Be 'test.rebex.net'
68 | # $Client.IsConnected | Should -Be $True
69 | # $List = Get-FTPList -Client $Client
70 | # $List.Count | Should -Be 2
71 | # $Names = 'Name', 'Type', 'FullName', 'Modified', 'Created'
72 | # foreach ($Name in @($Names)) {
73 | # $List[0].PSObject.Properties.Name | Should -Contain $Name
74 | # }
75 | # Disconnect-FTP -Client $Client
76 | # $Client.IsConnected | Should -Be $false
77 | # }
78 |
79 | }
--------------------------------------------------------------------------------
/Transferetto.psm1:
--------------------------------------------------------------------------------
1 | # Get public and private function definition files.
2 | $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue -Recurse )
3 | $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue -Recurse )
4 | $Classes = @( Get-ChildItem -Path $PSScriptRoot\Classes\*.ps1 -ErrorAction SilentlyContinue -Recurse )
5 | $Enums = @( Get-ChildItem -Path $PSScriptRoot\Enums\*.ps1 -ErrorAction SilentlyContinue -Recurse )
6 | # Get all assemblies
7 | $AssemblyFolders = Get-ChildItem -Path $PSScriptRoot\Lib -Directory -ErrorAction SilentlyContinue
8 |
9 | # Lets find which libraries we need to load
10 | $Default = $false
11 | $Core = $false
12 | $Standard = $false
13 | foreach ($A in $AssemblyFolders.Name) {
14 | if ($A -eq 'Default') {
15 | $Default = $true
16 | } elseif ($A -eq 'Core') {
17 | $Core = $true
18 | } elseif ($A -eq 'Standard') {
19 | $Standard = $true
20 | }
21 | }
22 | if ($Standard -and $Core -and $Default) {
23 | $FrameworkNet = 'Default'
24 | $Framework = 'Standard'
25 | } elseif ($Standard -and $Core) {
26 | $Framework = 'Standard'
27 | $FrameworkNet = 'Standard'
28 | } elseif ($Core -and $Default) {
29 | $Framework = 'Core'
30 | $FrameworkNet = 'Default'
31 | } elseif ($Standard -and $Default) {
32 | $Framework = 'Standard'
33 | $FrameworkNet = 'Default'
34 | } elseif ($Standard) {
35 | $Framework = 'Standard'
36 | $FrameworkNet = 'Standard'
37 | } elseif ($Core) {
38 | $Framework = 'Core'
39 | $FrameworkNet = ''
40 | } elseif ($Default) {
41 | $Framework = ''
42 | $FrameworkNet = 'Default'
43 | } else {
44 | Write-Error -Message 'No assemblies found'
45 | }
46 | if ($PSEdition -eq 'Core') {
47 | $LibFolder = $Framework
48 | } else {
49 | $LibFolder = $FrameworkNet
50 | }
51 |
52 | $Assembly = @(
53 | if ($Framework -and $PSEdition -eq 'Core') {
54 | Get-ChildItem -Path $PSScriptRoot\Lib\$Framework\*.dll -ErrorAction SilentlyContinue -Recurse
55 | }
56 | if ($FrameworkNet -and $PSEdition -ne 'Core') {
57 | Get-ChildItem -Path $PSScriptRoot\Lib\$FrameworkNet\*.dll -ErrorAction SilentlyContinue -Recurse
58 | }
59 | # if ($AssemblyFolders.BaseName -contains 'Standard') {
60 | # @( Get-ChildItem -Path $PSScriptRoot\Lib\Standard\*.dll -ErrorAction SilentlyContinue -Recurse)
61 | # }
62 | # if ($PSEdition -eq 'Core') {
63 | # @( Get-ChildItem -Path $PSScriptRoot\Lib\Core\*.dll -ErrorAction SilentlyContinue -Recurse )
64 | # } else {
65 | # @( Get-ChildItem -Path $PSScriptRoot\Lib\Default\*.dll -ErrorAction SilentlyContinue -Recurse )
66 | # }
67 | )
68 |
69 | # This is special way of importing DLL if multiple frameworks are in use
70 | $FoundErrors = @(
71 | # We load the DLL that does OnImportRemove if we have special module that requires special treatment for binary modules
72 |
73 | # Get library name, from the PSM1 file name
74 | $LibraryName = $myInvocation.MyCommand.Name.Replace(".psm1", "")
75 | $Library = "$LibraryName.dll"
76 | $Class = "$LibraryName.Initialize"
77 |
78 | # Ignore DLL files that are not NET Libraries
79 | $IgnoreLibraryFiles = @(
80 | 'libgcc_s_seh-1.dll'
81 | 'libgmp-10.dll'
82 | 'libgnutls-30.dll'
83 | 'libhogweed-6.dll'
84 | 'libnettle-8.dll'
85 | 'libwinpthread-1.dll'
86 | #ą 'Microsoft.Bcl.AsyncInterfaces.dll'
87 | )
88 |
89 | try {
90 | $ImportModule = Get-Command -Name Import-Module -Module Microsoft.PowerShell.Core
91 |
92 | if (-not ($Class -as [type])) {
93 | & $ImportModule ([IO.Path]::Combine($PSScriptRoot, 'Lib', $LibFolder, $Library)) -ErrorAction Stop
94 | } else {
95 | $Type = "$Class" -as [Type]
96 | & $importModule -Force -Assembly ($Type.Assembly)
97 | }
98 | } catch {
99 | Write-Warning -Message "Importing module $Library failed. Fix errors before continuing. Error: $($_.Exception.Message)"
100 | $true
101 | }
102 |
103 | Foreach ($Import in @($Assembly)) {
104 | if ($IgnoreLibraryFiles -contains $Import.Name) {
105 | continue
106 | }
107 | try {
108 | Add-Type -Path $Import.Fullname -ErrorAction Stop
109 | } catch [System.Reflection.ReflectionTypeLoadException] {
110 | Write-Warning "Processing $($Import.Name) Exception: $($_.Exception.Message)"
111 | $LoaderExceptions = $($_.Exception.LoaderExceptions) | Sort-Object -Unique
112 | foreach ($E in $LoaderExceptions) {
113 | Write-Warning "Processing $($Import.Name) LoaderExceptions: $($E.Message)"
114 | }
115 | $true
116 | } catch {
117 | Write-Warning "Processing $($Import.Name) Exception: $($_.Exception.Message)"
118 | $LoaderExceptions = $($_.Exception.LoaderExceptions) | Sort-Object -Unique
119 | foreach ($E in $LoaderExceptions) {
120 | Write-Warning "Processing $($Import.Name) LoaderExceptions: $($E.Message)"
121 | }
122 | $true
123 | }
124 | }
125 |
126 | #Dot source the files
127 | Foreach ($Import in @($Private + $Classes + $Enums + $Public)) {
128 | Try {
129 | . $Import.Fullname
130 | } Catch {
131 | Write-Warning -Message "Failed to import functions from $($import.Fullname).Error: $($_.Exception.Message)"
132 | $true
133 | }
134 | }
135 | )
136 |
137 | if ($FoundErrors.Count -gt 0) {
138 | $ModuleName = (Get-ChildItem $PSScriptRoot\*.psd1).BaseName
139 | Write-Warning "Importing module $ModuleName failed. Fix errors before continuing."
140 | break
141 | }
142 |
143 | Export-ModuleMember -Function '*' -Alias '*' -Cmdlet '*'
--------------------------------------------------------------------------------
/Build/Manage-Module.ps1:
--------------------------------------------------------------------------------
1 | Clear-Host
2 |
3 | Import-Module 'C:\Support\GitHub\PSPublishModule\PSPublishModule.psd1' -Force
4 |
5 | Build-Module -ModuleName 'Transferetto' {
6 | # Usual defaults as per standard module
7 | $Manifest = [ordered] @{
8 | # Version number of this module.
9 | ModuleVersion = '1.0.0'
10 | # Supported PSEditions
11 | CompatiblePSEditions = @('Desktop', 'Core')
12 | # ID used to uniquely identify this module
13 | GUID = '7d61db15-9efe-41d1-a1c0-81d738975dec'
14 | # Author of this module
15 | Author = 'Przemyslaw Klys'
16 | # Company or vendor of this module
17 | CompanyName = 'Evotec'
18 | # Copyright statement for this module
19 | Copyright = "(c) 2011 - $((Get-Date).Year) Przemyslaw Klys @ Evotec. All rights reserved."
20 | # Description of the functionality provided by this module
21 | Description = 'Module which allows ftp, ftps, sftp file transfers with advanced features. It also allows to transfer files and directorires between servers using fxp protocol. As a side feature it allows to conenct to SSH and executes commands on it. '
22 | # Minimum version of the Windows PowerShell engine required by this module
23 | PowerShellVersion = '5.1'
24 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
25 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
26 | Tags = @('Windows', 'Linux', 'MacOs', 'ftp', 'sftp', 'ftps', 'scp', 'winscp', 'ssh')
27 |
28 | IconUri = 'https://evotec.xyz/wp-content/uploads/2021/03/Transferetto.png'
29 |
30 | ProjectUri = 'https://github.com/EvotecIT/Transferetto'
31 |
32 | # DotNetFrameworkVersion = '4.7.2'
33 | }
34 | New-ConfigurationManifest @Manifest #-Prerelease 'Alpha1'
35 | # Add external module dependencies, using loop for simplicity
36 | New-ConfigurationModule -Type ExternalModule -Name 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility'
37 |
38 | # Add approved modules, that can be used as a dependency, but only when specific function from those modules is used
39 | # And on that time only that function and dependant functions will be copied over
40 | # Keep in mind it has it's limits when "copying" functions such as it should not depend on DLLs or other external files
41 | New-ConfigurationModule -Type ApprovedModule -Name 'PSSharedGoods', 'PSWriteColor', 'Connectimo', 'PSUnifi', 'PSWebToolbox', 'PSMyPassword'
42 |
43 | $ConfigurationFormat = [ordered] @{
44 | RemoveComments = $false
45 |
46 | PlaceOpenBraceEnable = $true
47 | PlaceOpenBraceOnSameLine = $true
48 | PlaceOpenBraceNewLineAfter = $true
49 | PlaceOpenBraceIgnoreOneLineBlock = $false
50 |
51 | PlaceCloseBraceEnable = $true
52 | PlaceCloseBraceNewLineAfter = $true
53 | PlaceCloseBraceIgnoreOneLineBlock = $false
54 | PlaceCloseBraceNoEmptyLineBefore = $true
55 |
56 | UseConsistentIndentationEnable = $true
57 | UseConsistentIndentationKind = 'space'
58 | UseConsistentIndentationPipelineIndentation = 'IncreaseIndentationAfterEveryPipeline'
59 | UseConsistentIndentationIndentationSize = 4
60 |
61 | UseConsistentWhitespaceEnable = $true
62 | UseConsistentWhitespaceCheckInnerBrace = $true
63 | UseConsistentWhitespaceCheckOpenBrace = $true
64 | UseConsistentWhitespaceCheckOpenParen = $true
65 | UseConsistentWhitespaceCheckOperator = $true
66 | UseConsistentWhitespaceCheckPipe = $true
67 | UseConsistentWhitespaceCheckSeparator = $true
68 |
69 | AlignAssignmentStatementEnable = $true
70 | AlignAssignmentStatementCheckHashtable = $true
71 |
72 | UseCorrectCasingEnable = $true
73 | }
74 | # format PSD1 and PSM1 files when merging into a single file
75 | # enable formatting is not required as Configuration is provided
76 | New-ConfigurationFormat -ApplyTo 'OnMergePSM1', 'OnMergePSD1' -Sort None @ConfigurationFormat
77 | # format PSD1 and PSM1 files within the module
78 | # enable formatting is required to make sure that formatting is applied (with default settings)
79 | New-ConfigurationFormat -ApplyTo 'DefaultPSD1', 'DefaultPSM1' -EnableFormatting -Sort None
80 | # when creating PSD1 use special style without comments and with only required parameters
81 | New-ConfigurationFormat -ApplyTo 'DefaultPSD1', 'OnMergePSD1' -PSD1Style 'Minimal'
82 |
83 | # configuration for documentation, at the same time it enables documentation processing
84 | New-ConfigurationDocumentation -Enable:$false -StartClean -UpdateWhenNew -PathReadme 'Docs\Readme.md' -Path 'Docs'
85 |
86 | New-ConfigurationImportModule -ImportSelf #-ImportRequiredModules
87 |
88 | $newConfigurationBuildSplat = @{
89 | Enable = $true
90 | SignModule = $true
91 | MergeModuleOnBuild = $true
92 | MergeFunctionsFromApprovedModules = $true
93 | CertificateThumbprint = '483292C9E317AA13B07BB7A96AE9D1A5ED9E7703'
94 | # require for FluentFTP to work in PS 5.1 in VSCode, works fine outside
95 | ResolveBinaryConflicts = $true
96 | ResolveBinaryConflictsName = 'Transferetto'
97 | NETProjectName = 'Transferetto'
98 | NETConfiguration = 'Release'
99 | NETFramework = 'netstandard2.0'
100 | #NETExcludeMainLibrary = $true
101 | DotSourceLibraries = $true
102 | DotSourceClasses = $true
103 | #SeparateFileLibraries = $true
104 | DeleteTargetModuleBeforeBuild = $true
105 | NETIgnoreLibraryOnLoad = @(
106 | 'libgcc_s_seh-1.dll'
107 | 'libgmp-10.dll'
108 | 'libgnutls-30.dll'
109 | 'libhogweed-6.dll'
110 | 'libnettle-8.dll'
111 | 'libwinpthread-1.dll'
112 | #'Microsoft.Bcl.AsyncInterfaces.dll'
113 | )
114 | }
115 |
116 | New-ConfigurationBuild @newConfigurationBuildSplat
117 |
118 | $newConfigurationArtefactSplat = @{
119 | Type = 'Unpacked'
120 | Enable = $true
121 | Path = "$PSScriptRoot\..\Artefacts\Unpacked"
122 | ModulesPath = "$PSScriptRoot\..\Artefacts\Unpacked\Modules"
123 | RequiredModulesPath = "$PSScriptRoot\..\Artefacts\Unpacked\Modules"
124 | AddRequiredModules = $true
125 | CopyFiles = @{
126 | #"Examples\PublishingExample\Example-ExchangeEssentials.ps1" = "RunMe.ps1"
127 | }
128 | }
129 | New-ConfigurationArtefact @newConfigurationArtefactSplat -CopyFilesRelative
130 | $newConfigurationArtefactSplat = @{
131 | Type = 'Packed'
132 | Enable = $true
133 | Path = "$PSScriptRoot\..\Artefacts\Packed"
134 | ModulesPath = "$PSScriptRoot\..\Artefacts\Packed\Modules"
135 | RequiredModulesPath = "$PSScriptRoot\..\Artefacts\Packed\Modules"
136 | AddRequiredModules = $true
137 | CopyFiles = @{
138 | #"Examples\PublishingExample\Example-ExchangeEssentials.ps1" = "RunMe.ps1"
139 | }
140 | ArtefactName = '.v.zip'
141 | }
142 | New-ConfigurationArtefact @newConfigurationArtefactSplat
143 |
144 | #New-ConfigurationTest -TestsPath "$PSScriptRoot\..\Tests" -Enable
145 |
146 | # global options for publishing to github/psgallery
147 | #New-ConfigurationPublish -Type PowerShellGallery -FilePath 'C:\Support\Important\PowerShellGalleryAPI.txt' -Enabled:$true
148 | #New-ConfigurationPublish -Type GitHub -FilePath 'C:\Support\Important\GitHubAPI.txt' -UserName 'EvotecIT' -Enabled:$true
149 | } -ExitCode
--------------------------------------------------------------------------------
/Public/Connect-FTP.ps1:
--------------------------------------------------------------------------------
1 | function Connect-FTP {
2 | [cmdletBinding(DefaultParameterSetName = 'Password')]
3 | param(
4 | [Parameter(ParameterSetName = 'FtpProfile')]
5 | [Parameter(ParameterSetName = 'ClearText')]
6 | [Parameter(ParameterSetName = 'Password')]
7 | [string] $ProxyHost,
8 |
9 | [Parameter(ParameterSetName = 'FtpProfile')]
10 | [Parameter(ParameterSetName = 'ClearText')]
11 | [Parameter(ParameterSetName = 'Password')]
12 | [int] $ProxyPort,
13 |
14 | [Parameter(ParameterSetName = 'FtpProfile')]
15 | [Parameter(ParameterSetName = 'ClearText')]
16 | [Parameter(ParameterSetName = 'Password')]
17 | [pscredential] $ProxyCredential,
18 |
19 | [Parameter(ParameterSetName = 'FtpProfile')]
20 | [Parameter(ParameterSetName = 'ClearText')]
21 | [Parameter(ParameterSetName = 'Password')]
22 | [string] $ProxyUserName,
23 |
24 | [Parameter(ParameterSetName = 'FtpProfile')]
25 | [Parameter(ParameterSetName = 'ClearText')]
26 | [Parameter(ParameterSetName = 'Password')]
27 | [string] $ProxyPassword,
28 |
29 | [Parameter(ParameterSetName = 'FtpProfile')]
30 | [Parameter(ParameterSetName = 'ClearText')]
31 | [Parameter(ParameterSetName = 'Password')]
32 | [ValidateSet(
33 | 'FtpClientSocks5Proxy',
34 | 'FtpClientHttp11Proxy',
35 | 'FtpClientSocks4aProxy',
36 | 'FtpClientSocks4Proxy',
37 | 'FtpClientUserAtHostProxy',
38 | 'FtpClientBlueCoatProxy'
39 | )][string] $ProxyType,
40 |
41 | [Parameter(ParameterSetName = 'FtpProfile')]
42 | [FluentFTP.FtpProfile] $FtpProfile,
43 |
44 | [Parameter(ParameterSetName = 'ClearText')]
45 | [Parameter(ParameterSetName = 'Password')]
46 | [string] $Server,
47 |
48 | [Parameter(ParameterSetName = 'ClearText')]
49 | [string] $Username,
50 |
51 | [Parameter(ParameterSetName = 'ClearText')]
52 | [string] $Password,
53 |
54 | [Parameter(ParameterSetName = 'Password')]
55 | [pscredential] $Credential,
56 |
57 | [Parameter(ParameterSetName = 'ClearText')]
58 | [Parameter(ParameterSetName = 'Password')]
59 | [FluentFTP.FtpEncryptionMode[]] $EncryptionMode,
60 |
61 | [Parameter(ParameterSetName = 'ClearText')]
62 | [Parameter(ParameterSetName = 'Password')]
63 | [FluentFTP.FtpDataConnectionType] $DataConnectionType,
64 |
65 |
66 | [Parameter(ParameterSetName = 'ClearText')]
67 | [Parameter(ParameterSetName = 'Password')]
68 | [FluentFTP.FtpsBuffering] $SslBuffering,
69 |
70 | [Parameter(ParameterSetName = 'ClearText')]
71 | [Parameter(ParameterSetName = 'Password')]
72 | [switch] $DisableDataConnectionEncryption,
73 |
74 | [Parameter(ParameterSetName = 'ClearText')]
75 | [Parameter(ParameterSetName = 'Password')]
76 | [switch] $DisableValidateCertificateRevocation,
77 |
78 | [Parameter(ParameterSetName = 'ClearText')]
79 | [Parameter(ParameterSetName = 'Password')]
80 | [switch] $ValidateAnyCertificate,
81 |
82 | [Parameter(ParameterSetName = 'ClearText')]
83 | [Parameter(ParameterSetName = 'Password')]
84 | [int] $Port,
85 |
86 | [Parameter(ParameterSetName = 'ClearText')]
87 | [Parameter(ParameterSetName = 'Password')]
88 | [switch] $SendHost,
89 |
90 | [Parameter(ParameterSetName = 'ClearText')]
91 | [Parameter(ParameterSetName = 'Password')]
92 | [switch] $SocketKeepAlive,
93 |
94 | [Parameter(ParameterSetName = 'ClearText')]
95 | [Parameter(ParameterSetName = 'Password')]
96 | [switch] $AutoConnect
97 | )
98 | $ClientInternal = $null
99 | if ($PSBoundParameters.ContainsKey('ProxyHost') -or $PSBoundParameters.ContainsKey('ProxyPort') -or $PSBoundParameters.ContainsKey('ProxyType')) {
100 | if ($PSBoundParameters.ContainsKey('ProxyHost') -and $PSBoundParameters.ContainsKey('ProxyType')) {
101 | $ProxyProfile = [FluentFTP.FtpProxyProfile]::new()
102 | $ProxyProfile.ProxyHost = $ProxyHost
103 | $ProxyProfile.ProxyPort = $ProxyPort
104 |
105 | if ($ProxyUsername -and $ProxyPassword) {
106 | $ProxyProfile.ProxyCredentials = [System.Net.NetworkCredential]::new($Username, $Password)
107 | } elseif ($ProxyCredential) {
108 | $ProxyProfile.ProxyCredentials = [System.Net.NetworkCredential]::new($ProxyCredential.Username, $ProxyCredential.Password)
109 | } else {
110 | # anonymous
111 | }
112 |
113 | $ProxyProfile.FtpHost = $Server
114 | if ($Port) {
115 | $ProxyProfile.FtpPort = $Port
116 | }
117 |
118 | if ($Username -and $Password) {
119 | $ProxyProfile.FTPCredentials = [System.Net.NetworkCredential]::new($Username, $Password)
120 | } elseif ($Credential) {
121 | $ProxyProfile.FTPCredentials = [System.Net.NetworkCredential]::new($Credential.Username, $Credential.Password)
122 | } else {
123 | # anonymous
124 | }
125 |
126 | if ($ProxyType -eq 'FtpClientSocks5Proxy') {
127 | $ClientInternal = [FluentFTP.Proxy.SyncProxy.FtpClientSocks5Proxy]::new($ProxyProfile)
128 | } elseif ($ProxyType -eq 'FtpClientHttp11Proxy') {
129 | $ClientInternal = [FluentFTP.Proxy.SyncProxy.FtpClientHttp11Proxy]::new($ProxyProfile)
130 | } elseif ($ProxyType -eq 'FtpClientSocks4aProxy') {
131 | $ClientInternal = [FluentFTP.Proxy.SyncProxy.FtpClientSocks4aProxy]::new($ProxyProfile)
132 | } elseif ($ProxyType -eq 'FtpClientSocks4Proxy') {
133 | $ClientInternal = [FluentFTP.Proxy.SyncProxy.FtpClientSocks4Proxy]::new($ProxyProfile)
134 | } elseif ($ProxyType -eq 'FtpClientUserAtHostProxy') {
135 | $ClientInternal = [FluentFTP.Proxy.SyncProxy.FtpClientUserAtHostProxy]::new($ProxyProfile)
136 | } elseif ($ProxyType -eq 'FtpClientBlueCoatProxy') {
137 | $ClientInternal = [FluentFTP.Proxy.SyncProxy.FtpClientBlueCoatProxy]::new($ProxyProfile)
138 | }
139 | } else {
140 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
141 | Write-Error "ProxyHost, ProxyPort, and ProxyType must be specified together when using Proxy. Only ProxyUserName, ProxyPassword or ProxyCredential are optional."
142 | return
143 | } else {
144 | Write-Warning "Connect-FTP - ProxyHost, ProxyPort, and ProxyType must be specified together when using Proxy. Only ProxyUserName, ProxyPassword or ProxyCredential are optional."
145 | }
146 | }
147 | }
148 | if ($FtpProfile) {
149 | if (-not $ClientInternal) {
150 | $ClientInternal = [FluentFTP.FtpClient]::new()
151 | }
152 | $ClientInternal.LoadProfile($FtpProfile)
153 | } else {
154 | if (-not $ClientInternal) {
155 | $ClientInternal = [FluentFTP.FtpClient]::new($Server)
156 | }
157 | if ($Username -and $Password) {
158 | $ClientInternal.Credentials = [System.Net.NetworkCredential]::new($Username, $Password)
159 | } elseif ($Credential) {
160 | $ClientInternal.Credentials = [System.Net.NetworkCredential]::new($Credential.Username, $Credential.Password)
161 | } else {
162 | # anonymous
163 | }
164 | }
165 | if ($Script:GlobalFTPLogging) {
166 | $ClientInternal.Config.LogHost = $Script:GlobalFTPLogging.LogHost
167 | $ClientInternal.Config.LogUsername = $Script:GlobalFTPLogging.LogUsername
168 | $ClientInternal.Config.LogPassword = $Script:GlobalFTPLogging.LogPassword
169 | $ClientInternal.Config.LogToConsole = $Script:GlobalFTPLogging.LogToConsole
170 | }
171 |
172 | if ($Port) {
173 | $ClientInternal.Port = $Port
174 | }
175 | if ($DataConnectionType) {
176 | $ClientInternal.Config.DataConnectionType = $DataConnectionType
177 | }
178 | if ($DisableDataConnectionEncryption) {
179 | $ClientInternal.Config.DataConnectionEncryption = $false
180 | }
181 | if ($EncryptionMode) {
182 | $ClientInternal.Config.EncryptionMode = $EncryptionMode
183 | }
184 | if ($ValidateAnyCertificate) {
185 | $ClientInternal.Config.ValidateAnyCertificate = $true
186 | }
187 | if ($DisableValidateCertificateRevocation) {
188 | $ClientInternal.Config.ValidateCertificateRevocation = $false
189 | }
190 | if ($SendHost) {
191 | $ClientInternal.Config.SendHost = $true
192 | }
193 | if ($SocketKeepAlive) {
194 | $ClientInternal.Config.SocketKeepAlive = $true
195 | }
196 | if ($FtpsBuffering) {
197 | $ClientInternal.Config.SslBuffering = $SslBuffering
198 | }
199 | try {
200 | if ($AutoConnect) {
201 | $TempFtpProfile = $ClientInternal.AutoConnect()
202 | if ($TempFtpProfile -and $ClientInternal.IsConnected) {
203 | Write-Verbose "Following options where used to autoconnect: "
204 | foreach ($Name in $TempFtpProfile.PSObject.Properties.Name) {
205 | Write-Verbose "[x] $Name -> $($TempFtpProfile.$Name)"
206 | }
207 | }
208 | } else {
209 | $ClientInternal.Connect()
210 | }
211 | $ClientInternal | Add-Member -Name 'Error' -Value $null -Force -MemberType NoteProperty
212 | } catch {
213 | $ClientInternal | Add-Member -Name 'Error' -Value $($_.Exception.Message) -Force -MemberType NoteProperty
214 | if ($PSBoundParameters.ErrorAction -eq 'Stop') {
215 | Write-Error $_
216 | return
217 | } else {
218 | Write-Warning "Connect-FTP - Error: $($_.Exception.Message)"
219 | }
220 | }
221 | $ClientInternal
222 | }
--------------------------------------------------------------------------------