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.
	#>
	
	[CmdletBinding( DefaultParameterSetName = 'Auto' )]
	param(
		[Parameter( Mandatory )]
		[string]
		# The name of a saved PuTTY session.
		$Session,
		
		[AllowEmptyString()]
		[Parameter( Mandatory )]
		[string[]]
		# One or more commands to execute in the remote session.
		$Command,

		[Parameter( ParameterSetName = 'IPv6', Mandatory )]
		[switch]
		# Forces use of IPv6. By default, Plink selects the IP version automatically, depending on your Windows networking configuration.
		$IPv6,
		
		[Parameter( ParameterSetName = 'IPv4', Mandatory )]
		[switch]
		# Forces use of IPv4. By default, Plink selects the IP version automatically, depending on your Windows networking configuration.
		$IPv4,
		
		[Parameter()]
		[ValidateSet( 'Throw', 'Merge', 'Discard', 'Split' )]
		[string]
		# 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.
		$StandardErrorBehavior = 'Throw'
	);

	[string] $Command = $Command -join "`n";
	$exe = "${env:ProgramFiles}\PuTTY\plink.exe";
	$params = @(
		switch( $true ) {
			$IPv6 { '-6'; }
			$IPv4 { '-4'; }
			default { ''; }
		};
		'-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 {
		$OutputFile, $ErrorFile | Remove-Item -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: