First, PowerShell Core have to be installed on every target Linux machine and added to its /etc/ssh/sshd_config
:
Subsystem powershell /usr/bin/pwsh -sshs -NoLogo -NoProfile
Probably, in a script you have the remote session to that target box started like:
$s = New-PSSession -HostName $deployHostname -UserName $login -KeyFilePath $keyfile
If you have to execute a sudo command in that session, you could do like:
Invoke-Command -Session $s { Invoke-Expression "sudo ..."}
Imagine you put your script into CI / CD pipeline and want to see its output in a log. In case of failure of a command invoked like shown above, probably, you’ll be frustrated with the error output like this:
NotSpecified: (:String) [], RemoteExceptionscript returned exit code 1
Such useless output originate from the fact that fail happens in a remote session. You can’t cope with that by just adding a conversion of an output error to exception with -ErrorAction Stop
parameter for Invoke-Expression
, because the output of the invoked shell command is not a PowerShell error output.
But throwing exceptions is the right way of passing errors. So, the working solution includes writing output to a file and throwing its content with the exception this way:
Invoke-Command -Session $s { $errFile = "/tmp/$($(New-Guid).Guid).err" Invoke-Expression "sudo ... 2>${errFile}" -ErrorAction Stop $err = Get-Content $errFile -ErrorAction SilentlyContinue Remove-Item $errFile -ErrorAction SilentlyContinue If (-Not $null -eq $err) { throw $err }}
Perfect, but has too many lines of code for just copying and pasting it all over the script. So, let’s introduce a function:
function Invoke-SudoCommand {<#.SYNOPSISInvokes sudo command in a remote session to Linux#> param ( [Parameter(Mandatory=$true)] [PSSession] $Session,
[Parameter(Mandatory=$true)] [String] $Command ) Invoke-Command -Session $Session { $errFile = "/tmp/$($(New-Guid).Guid).err" Invoke-Expression "sudo ${using:Command} 2>${errFile}" -ErrorAction Stop $err = Get-Content $errFile -ErrorAction SilentlyContinue Remove-Item $errFile -ErrorAction SilentlyContinue If (-Not $null -eq $err) { throw $err } }}
Using this function you can invoke sudo commands with a single line of code:
Invoke-SudoCommand -Session $s -Command "..."
Script variables can also be used:
Invoke-SudoCommand -Session $s -Command "rm -rf ${targetFolder}"Invoke-SudoCommand -Session $s -Command "unzip ${zipName} -d ${targetFolder}"
PowerShell allows you to wrap things in a pretty neat way, isn’t it?
Guessing can you use this function inside of a 1st level remote session to execute the sudo commands in the nested remote sessions? Sure, export a function to the remote session and invoke it there. Look in this repository for the implementation and usage of Export-FunctionRemote
.