Invoking sudo commands in a PowerShell session from a Jenkins agent to a target Linux machine

Imagine we have a Jenkins or another CI / CD tool that executes PowerShell on an agent machine (Linux in my case). The tool writes its (standard) output to the build log.

PowerShell Core is installed on the target machine and added to /etc/ssh/sshd_config:

Subsystem       powershell /usr/bin/pwsh -sshs -NoLogo -NoProfile

In a PowerShell script you have the remote session to that target Linux box:

$s = New-PSSession -HostName $deployHostname -UserName $login -KeyFilePath $keyfile

In a script you have an expression (shell command) invoked like:

Invoke-Command -Session $s {
    Invoke-Expression "sudo ..."
}

If something goes wrong with the invoked command, it can frustrate you — to see an error in the build log like this:

NotSpecified: (:String) [], RemoteException
script returned exit code 1

Even if you use -ErrorAction Stop in the following way (to convert an error output to an exception) it doesn’t help, because the output of the invoked command is not a PowerShell error output:

Invoke-Command -Session $s {
    Invoke-Expression "sudo ..." -ErrorAction Stop
}

The working pattern is:

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
    }
}

This way the build log becomes useful, thrown errors are there.

But now there are too many lines of code. So, to simplify it let’s introduce the function:

function Invoke-SudoCommand {
<#
.SYNOPSIS
Invokes a sudo command in the remote session to Linux
#&gt;
    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&gt;${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 be used like in the following examples:

Invoke-SudoCommand -Session $s -Command "rm -rf ${targetFolder}"
Invoke-SudoCommand -Session $s -Command "unzip ${zipName} -d ${targetFolder}"

So, the pretty neat way.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: