Powershell Stdout не возвращается в Jenkins - PullRequest
1 голос
/ 19 июня 2020

Я работаю с Psexe c из-за некоторых ограничений компании в отношении PSremoting и WMI, а также из-за ненормального вывода PSexe c и определенных значений, интерпретируемых как код ошибки / код выхода в PowerShell. Я адаптировал оболочку из этого post .

Все работает нормально при вызове на локальном компьютере, но не stdout при вызове из этапа сборки Jenkins.

Я хотел бы сделать запрос для рабочего стола SessionID удаленной машины, и эти данные возвращаются в stdout. Есть ли способ сделать это с помощью оболочки, или мне понадобится прямой подход? Это действительно сработало, но я хотел иметь лучшую структуру и отчеты об ошибках во всех сценариях, используемых для сборки:

$stdout = & $psexec \\$hostName -u $user -p $pass -accepteula cmd /c "tasklist /FI "IMAGENAME eq taskhost*" /FO List | findstr "Session#:""

Функция Get-SessionID:

function Get-SessionID
{
<#
    .SYNOPSIS
    Get session ID.

    .DESCRIPTION
    Get the host machines desktop session ID.

    .PARAMETER None
#>

# Import external functions
Push-Location $PSScriptRoot
Import-module .\Write-Log.ps1 -Force
Import-module .\CheckAndLoad-EnvironmentVariable.ps1 -Force
Import-module .\Psexec-Wrapper.ps1 -Force

# Check and load the environment variables
    $hostName = "<IP Address of the remote machine>"
    $psexec = "<Path to PSExec.exe>"
    $user = "<Remote Machine User Name>"
    $pass = "<Remote Machine Password>"
    $cmd = "-u $user -p $pass -accepteula -nobanner cmd /c tasklist /FI ""IMAGENAME eq taskhost*"" /FO List"

    Write-Log("Running the following command with PsExec: $psexec \\$hostName $cmd")

    # Call PsExec with the command line arguments
    $result = Psexec-Wrapper $psexec $hostName $cmd
    # Check if PsExec call was successful
if(!$? -or ($LASTEXITCODE -ne 0) -or ($result.session_id -eq "") -or ($result.session_id -eq "None"))
{
    $errorMessage = "Failed to get the session ID. Result: $logResult"
    Report-Error $errorMessage "-13"
    Pop-Location
    return
} 

# Process results
$logResult = $result | Out-String
Write-Log("PsExec output: $logResult")

#Report if error occurred
If($result.exit_code -ne 0)
{
    #Exit code for certain run issues should be -13 or Psexec specific error code
    $errorMessage = $result.std_err | Out-String
    Report-Error $errorMessage $result.exit_code
    Pop-Location
    return
}

# Get the ID from string
$sessionID= $result.session_id | Out-String

Pop-Location
return $sessionID
}

Вот код оболочки :

function Psexec-Wrapper
{
param
(
    [Parameter(Mandatory=$true)] [string] $psexec_path,
    [Parameter(Mandatory=$true)] [string] $remote_computer,
    [Parameter(Mandatory=$true)] [string] $command_str,
    [switch] $include_blank_lines
)

begin 
{
    $remote_computer_regex_escaped = [regex]::Escape($remote_computer)
    # $ps_exec_header = "`r`nPsExec v2.2 - Execute processes remotely`r`nCopyright (C) 2001-2016 Mark Russinovich`r`nSysinternals - www.sysinternals.com`r`n"
    $ps_exec_regex_headers_array = @(
    '^\s*PsExec v\d+(?:\.\d+)? - Execute processes remotely\s*$',
    '^\s*Copyright \(C\) \d{4}(?:-\d{4})? Mark Russinovich\s*$',
    '^\s*Sysinternals - www\.sysinternals\.com\s*$'
    )
    $ps_exec_regex_info_array = @(
    ('^\s*Connecting to ' + $remote_computer_regex_escaped + '\.{3}\s*$'),
    ('^\s*Starting PSEXESVC service on ' + $remote_computer_regex_escaped + '\.{3}\s*$'),
    ('^\s*Connecting with PsExec service on ' + $remote_computer_regex_escaped + '\.{3}\s*$'),
    ('^\s*Starting .+ on ' + $remote_computer_regex_escaped + '\.{3}\s*$')
    )
    $bypass_regex_array = $ps_exec_regex_headers_array + $ps_exec_regex_info_array
    $exit_code_regex_str =('^.+ exited on ' + $remote_computer_regex_escaped + ' with error code (\d+)\.\s*$')
    $process_id_regex_str =('^.+ started on ' + $remote_computer_regex_escaped + ' with process ID (\d+)\.\s*$')
    $session_id_regex_str =('^\s*Session#:\s*(\d+)\s*$')
    $ps_exec_args_str = ('"\\' + $remote_computer + '" ' + $command_str)
}

process 
{
    $return_dict = @{
        'std_out' = (New-Object 'system.collections.generic.list[string]');
        'std_err' = (New-Object 'system.collections.generic.list[string]');
        'bypassed_std' = (New-Object 'system.collections.generic.list[string]');
        'process_id' = "None";
        'session_id' = "None";
        'exit_code' = $null;
    }

    $process_info = New-Object System.Diagnostics.ProcessStartInfo
    $process_info.CreateNoWindow = $true
    $process_info.RedirectStandardError = $true
    $process_info.RedirectStandardOutput = $true
    $process_info.UseShellExecute = $false
    $process_info.FileName = $psexec_path
    $process_info.Arguments = $ps_exec_args_str
    $process = New-Object System.Diagnostics.Process
    $process.StartInfo = $process_info
    $process.Start() | Out-Null

    $std_dict = [ordered] @{
        'std_out' = New-Object 'system.collections.generic.list[string]';
        'std_err' = New-Object 'system.collections.generic.list[string]';
    }

    # $stdout_str = $process.StandardOutput.ReadToEnd()
    while ($true) 
    {
        $line = $process.StandardOutput.ReadLine()
        if ($line -eq $null) 
        {
            break
        }
        $std_dict['std_out'].Add($line)
    }
    # $stderr_str = $process.StandardError.ReadToEnd()
    while ($true) 
    {
        $line = $process.StandardError.ReadLine()
        if ($line -eq $null) 
        {
            break
        }
        $std_dict['std_err'].Add($line)
    }

    $process.WaitForExit()

    ForEach ($std_type in $std_dict.Keys) 
    {
        ForEach ($line in $std_dict[$std_type]) 
        {
            if ((-not $include_blank_lines) -and ($line -match '^\s*$')) 
            {
                continue
            }

            $do_continue = $false
            ForEach ($regex_str in $bypass_regex_array) 
            {
                if ($line -match $regex_str) 
                {
                    $return_dict['bypassed_std'].Add($line)
                    $do_continue = $true
                    break
                }
            }
            if ($do_continue) 
            {
                continue
            }

            $exit_code_regex_match = [regex]::Match($line, $exit_code_regex_str)
            $process_id_regex_match = [regex]::Match($line, $process_id_regex_str)
            $session_id_regex_match = [regex]::Match($line, $session_id_regex_str)
            if ($exit_code_regex_match.Success) 
            {
                $return_dict['exit_code'] = [int] $exit_code_regex_match.Groups[1].Value
                if ($std_type -eq 'std_err') 
                {
                    $return_dict['std_err'].Add($line)
                }
            } 
            elseif ($process_id_regex_match.Success) 
            {
                $return_dict['process_id'] = [int] $process_id_regex_match.Groups[1].Value
                $return_dict['exit_code'] = 0
                if ($std_type -eq 'std_out') 
                {
                    $return_dict['std_out'].Add($line)
                } 
            }
            elseif ($session_id_regex_match.Success) 
            {
                $return_dict['session_id'] = [int] $session_id_regex_match.Groups[1].Value
                $return_dict['exit_code'] = 0
                if ($std_type -eq 'std_out') 
                {
                    $return_dict['std_out'].Add($line)
                } 
            }
            elseif ($std_type -eq 'std_out') 
            {
                $return_dict['std_out'].Add($line)
            } 
            elseif ($std_type -eq 'std_err') 
            {
                $return_dict['std_err'].Add($line)
                $return_dict['exit_code'] = -13
            } 
            else 
            {
                throw 'this conditional should never be true; if so, something was coded incorrectly'
            }
        }
    }
    return $return_dict
}
}
...