Calling Plink from PowerShell

Plink is part of the PuTTY suite. Plink is primarily used to run automated operations (rather than interactive sessions) on a remote server, using the SSH protocol.

To reliably run Plink from PowerShell, you need to redirect Plink's input from $null. This avoids the problem that both PowerShell and Plink might attempt to read from the console, resulting in an exception: Cannot read keys when either application does not have a console or when console input has been redirected.

The function Invoke-Plink encapsulates the call of Plink:

function Invoke-Plink {
    
    <#
        .SYNOPSIS
            Executes a remote command via Plink.
    
        .PARAMETER Session
            The name of a saved PuTTY session.
            
        .PARAMETER Command
            One or more commands to execute in the remote session.
    
        .PARAMETER IPv6
            Forces use of IPv6. By default, Plink selects the IP version automatically, depending on your Windows networking configuration.
            
        .PARAMETER IPv4
            Forces use of IPv4. By default, Plink selects the IP version automatically, depending on your Windows networking configuration.

        .PARAMETER StandardErrorBehavior
            Specifies how to handle output written to stderr by the remote command:
            • 'Throw' will raise an exception if any output was written to stderr.
            • 'Merge' will combine stdout and stderr output, similar to a console.
            • 'Discard' will simply omit any stderr output.
            • 'Split' will return stdout and stderr output in two distinct properties.
    #>
    
    [CmdletBinding( DefaultParameterSetName = 'Auto' )]
    param(
        [Parameter( Mandatory )]
        [string]
        $Session,
        
        [AllowEmptyString()]
        [Parameter( Mandatory )]
        [string[]]
        $Command,

        [Parameter( ParameterSetName = 'IPv6', Mandatory )]
        [switch]
        $IPv6,
        
        [Parameter( ParameterSetName = 'IPv4', Mandatory )]
        [switch]
        $IPv4,
        
        [Parameter()]
        [ValidateSet( 'Throw', 'Merge', 'Discard', 'Split' )]
        [string]
        $StandardErrorBehavior = 'Throw'
    );

    [string] $Command = $Command | Join-String -Separator "`n";
    $exe = "$env:ProgramFiles\PuTTY\plink.exe";
    $params = @(
        $IPv6 ? '-6' : $IPv4 ? '-4' : '';
        '-batch';
        '-load'
        $Session;
        $Command;
    );
    
    $OutputFile = [System.IO.Path]::GetTempFileName();
    $ErrorFile = [System.IO.Path]::GetTempFileName();
    
    try {
        switch( $StandardErrorBehavior ) {
            'Throw' {
                $null | & $exe $params 1>$OutputFile 2>$ErrorFile;
                $ErrorContent = Get-Content -LiteralPath $ErrorFile;
                if( $ErrorContent ) {
                    throw $ErrorContent;
                } else {
                    Get-Content -LiteralPath $OutputFile;
                }
            }
            'Merge' {
                $null | & $exe $params 1>$OutputFile 2>&1;
                Get-Content -LiteralPath $OutputFile;
            }
            'Discard' {
                $null | & $exe $params 1>$OutputFile 2>$null;
                Get-Content -LiteralPath $OutputFile;
            }
            'Split' {
                $null | & $exe $params 1>$OutputFile 2>$ErrorFile;
                [pscustomobject] @{
                    Output = Get-Content -LiteralPath $OutputFile;
                    Errors = Get-Content -LiteralPath $ErrorFile;
                };
            }
        }
    } finally {
        Remove-Item -LiteralPath $OutputFile -ErrorAction 'SilentlyContinue';
        Remove-Item -LiteralPath $ErrorFile -ErrorAction 'SilentlyContinue';
    }
}
Invoke-Plink.ps1

Use the main PuTTY program to configure and save a session, then call this function as follows:

PS C:\> Invoke-Plink -Session 'example.net' -Command 'whoami'
root

The -StandardErrorBehavior parameter specifies how to handle output written to stderr by the remote command: