# testSftpConnect.ps1
# Quick script to test SFTP connectivity

$username='FTPUSER'
$password='PASSWORD'
$sftpServer='x.x.x.x'
$sftpPort=22

function testSftpConnect($sftpServer,$username,$password,$sftpPort){
 
    function includePowerShellWrapper($appName){
        # Ensure that PowerShell is using TLS 1.2 in this session
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        # Uncomment these lines to make TLS1.2 defaults on next reboot
        #Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord
        #Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord    
  
        # Deterministic verification on whether module commands are able to load
        Import-Module $appName -ea SilentlyContinue
        # Sanity check: validate module name before proceeding
        $moduleVersion=.{$y=find-module $appName -ea SilentlyContinue;if($y){return $y.Version.ToString()}}
        if(!$moduleVersion){write-warning "'$($appName.ToUpper())' doesn't match PowerShell module";return $false}   
        elseif(!$moduleIsLoaded){
            # Part A) Compare the installed module with its online counter-part; install if necessary
            # Select Major, Minor, and Build - omitting Revision.
            # This is because PowerShell wrapper will deem an installed binary as compatible as long as those values match     
            $installedVersion=.{$x=Get-Module $appName -ea SilentlyContinue;if($x){return $x.Version.ToString()}}
            $targetVersion=.{try{[void]($moduleVersion -match '^(\d+\.\d+\.{0,1}\d+)');$matches[1]}catch{}}
            if($matches){Clear-Variable -name matches}
            write-host "$($appName.ToUpper()) PowerShell wrapper`: Installed $installedVersion v.s Updated version $moduleVersion"
            if($installedVersion -ne $moduleVersion){
                write-host "Attempting to install the module $appName $moduleVersion"
                Remove-Module $appName -ea SilentlyContinue
                try{                
                    Install-Module -Name $appName -Force -Confirm:$false;
                    Import-Module $appName;
                    write-host "$appName PowerShell wrapper module has been installed."
                    return $true
                    }
                catch{
                    if(!('NuGet' -in (get-packageprovider).Name)){    
                        try{
                            Install-PackageProvider -Name NuGet -MinimumVersion '2.8.5.201' -Force -Confirm:$false -ErrorAction Stop;
                            Install-Module -Name $appName -Force -Confirm:$false;
                            Import-Module $appName;
                            write-host "$appName PowerShell wrapper module has been installed."
                            }
                        catch{                  
                            write-host "$error"
                            write-warning "Unable to install $appName module"
                            return $false
                            }            
                        }
                    }
                }
  
            # Part B) Check Installed applications and upgrade/downgrade as necessary
            $installedApps=Get-CimInstance -ClassName win32_InstalledWin32Program -ErrorAction SilentlyContinue|select Name,Version
            $appwizAppExists=.{try{$matchApp=$installedApps|?{$_.Name -like "*$appName*"|select -First 1 }
                                if($matchApp){$matchApp}else{$false}}catch{}}
            $appwizVersion=.{try{[void]($(if($appwizAppExists){$appwizAppExists.Version}else{$false}) -match '^(\d+\.\d+\.{0,1}\d+)');$matches[1]}catch{}}
            if($matches){Clear-Variable -name matches}
            write-host "Application Wizard version being detected: $appwizAppExists"
            $isAppwizSameVersion=$targetVersion -eq $appwizVersion
      
            $chocoInstalledApps=choco list -l
            $chocoAppExists=$chocoInstalledApps|?{$_ -like "*$appName*"}|select -First 1
            $chocoVersion=.{try{[void]($(.{[void]($chocoAppExists -match ' ([\d+\.]+)');return $matches[1]}) -match '^(\d+\.\d+\.{0,1}\d+)');$matches[1]}catch{}}
            if($matches){Clear-Variable -name matches}
            write-host "Choco version being detected: $chocoAppExists"
            $isChocoSameVersion=$targetVersion -eq $chocoVersion
            write-host "Target version: $targetVersion, Appwiz version: $appwizVersion, Choco version: $chocoVersion"
      
            if (!($isAppwizSameVersion -or $isChocoSameVersion)){        
                write-host "Now trying to install $appname..."
                    if (!(Get-Command choco.exe -ErrorAction SilentlyContinue)) {
                        Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))}
                    try{
                        if(!$chocoAppExists -and !$appwizAppExists){choco install $appName --version=$targetVersion -y}
                        elseif (!$isChocoSameVersion -and !$isAppwizSameVersion){choco install $appName --version=$targetVersion --force -y}
                        write-host "Application $appName $targetVersion is now installed"
                        if($matches){Clear-Variable -name matches}
                        return $true
                    }
                    catch{
                        write-warning "Unable to install the app name $appName $targetVersion using Chocolatey."
                        if($matches){Clear-Variable -name matches}
                        return $false
                        }
                }
            else{
                write-host "$appName is already installed.";
                if($matches){Clear-Variable -name matches}
                return $true}
        }
        else{write-host "$($appName.ToUpper()) module has loaded successfully.";return $true}
    }
 
    function connectWinScp{
        param (
            $remoteHost,
            $username,
            $password,
            $port=22,
            $hostKey,
            $privateKey
            )
        Import-Module WinScp -ea SilentlyContinue
        # Possible error:
        # Import-Module : The specified module 'WinScp' was not loaded because no valid module file was found in any module
        # directory.
        # At line:102 char:15
        # +         $null=Import-Module WinScp
        # +               ~~~~~~~~~~~~~~~~~~~~
        #     + CategoryInfo          : ResourceUnavailable: (WinScp:String) [Import-Module], FileNotFoundException
        #     + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand
        #         $moduleIsLoaded=Get-Command -Module $appName -ea SilentlyContinue
        if(!(get-module Winscp)){
            includePowerShellWrapper Winscp
            Import-Module WinScp
            }
        # if ($scpSession){ # deal with case that the global variable $scpSession has already been assigned
        #     try{
        #         Close-WinSCPSession -WinSCPSession $scpSession -ea SilentlyContinue
        #         $scpSession.Dispose()
        #     }catch{
        #         Clear-Variable scpSession
        #         }
        #     } 
         $optionValues="
            `$options = New-Object WinSCP.SessionOptions -Property @{
                # source: https://winscp.net/eng/docs/library_sessionoptions
                Protocol = [WinSCP.Protocol]::Sftp
                HostName = '$remoteHost'
                username = '$username'
                portnumber = '$port'
                $(if($hostKey){"SshHostKeyFingerprint = '$hostKey'"}else{"GiveUpSecurityAndAcceptAnySshHostKey = `$True"})
                $(if($privateKey){"SshPrivateKeyPath = '$privateKey'"}else{"password = '$password'"})
            }"
         try{
            write-host $optionValues
            Invoke-Expression $optionValues
            $scpSession =  Open-WinSCPSession -SessionOption $options -ea Stop
            write-host "WinSCP has successfully connected to $remoteHost" -ForegroundColor Green
            return $scpSession
            }
        catch{
            write-waring $_
            write-host "unable to connnect to $remoteHost" -ForegroundColor Yellow
            return $false
            }
    }
 
    $scpSession=connectWinScp $sftpServer $username $password $sftpPort
    if($scpSession){
        write-host "Now closing test WinScp session"
        Close-WinSCPSession -WinSCPSession $scpSession
        # $scpSession.dispose() # alternate cleanup routine
        return $true
    }else{
        return $false
        }
}
 
testSftpConnect $sftpServer $username $password $sftpPort