Как передать потоки Warning и Verbose из удаленной команды при вызове powershell с помощью System.Management.Automation? - PullRequest
0 голосов
/ 09 января 2019

Я пытаюсь вызвать команду удаленно (либо на локальной виртуальной машине, либо с помощью сети), используя PowerShell в C #. Я использую библиотеку System.Management.Automation из https://www.nuget.org/packages/Microsoft.PowerShell.5.ReferenceAssemblies/

По неизвестным мне причинам предупреждения и подробные сообщения не регистрируются обратно.

При локальном вызове commnd в C # он работает нормально.

Program output

Я пробовал различные параметры, связанные с потоками ($ verbosepreference, $ warningpreference), но я не думаю, что они связаны - этой проблемы не существует при вызове PowerShell из консоли.

Я проверил как удаленное соединение с виртуальной машиной (параметр -VmName в Invoke-Command), так и с компьютером в сети (параметр -ComputerName в Invoke-Command) - оба страдают от этой проблемы.

Я попытался найти какой-то магический переключатель в PowerShell Invoke (), который бы передавал эти потоки - я нашел вещь под названием RemoteStreamOptions (https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.remotestreamoptions?view=powershellsdk-1.1.0), но его установка не помогла.

Есть ли что-нибудь, что я могу сделать, чтобы это заработало?

namespace ConsoleApp2
{
using System;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Security;

class Program
{
    static void Main(string[] args)
    {
        var script = @"
        $VerbosePreference='Continue' #nope, it's not about these settings
        $WarningPreference = 'Continue'
        Write-Host "" ------- $env:computername  ------- ""
        Write-Warning ""Write warning directly in warn script""
        Write-Error ""Write error directly in warn script""
        Write-Verbose ""Write verbose directly in warn script"" -verbose
        Write-Host ""Write host directly in warn script""   
        Write-Information ""Write Information in warn script""
        ";


        Call(script, command =>
            {
                SecureString password = new SecureString();
                "admin".ToCharArray().ToList().ForEach(c => password.AppendChar(c));
                command.Parameters.Add("Credential", new PSCredential("admin", password));
                command.Parameters.Add("VmName", new[] { "190104075839" });
            }
    );
        Call(script);
    }

    private static void Call(string script, Action<Command> addtionalCommandSetup = null)
    {
        using (var shell = PowerShell.Create())
        {
            shell.Streams.Information.DataAdded += LogProgress<InformationRecord>;
            shell.Streams.Warning.DataAdded += LogProgress<WarningRecord>;
            shell.Streams.Error.DataAdded += LogProgress<ErrorRecord>;
            shell.Streams.Verbose.DataAdded += LogProgress<VerboseRecord>;
            shell.Streams.Debug.DataAdded += LogProgress<DebugRecord>;



            var command = new Command("Invoke-Command");
            command.Parameters.Add("ScriptBlock", ScriptBlock.Create(script));
            addtionalCommandSetup?.Invoke(command);
            shell.Commands.AddCommand(command);
            shell.Invoke();
            Console.ReadKey();
        }
    }

    private static void LogProgress<T>(object sender, DataAddedEventArgs e)
    {
        var data = (sender as PSDataCollection<T>)[e.Index];
        Console.WriteLine($"[{typeof(T).Name}] {Convert.ToString(data)}");
    }
}
}

1 Ответ

0 голосов
/ 09 января 2019

Я думаю, что проблема может заключаться в том, что вы создаете локальный сеанс PowerShell с var shell = PowerShell.Create(), который затем создает отдельный удаленный сеанс с Invoke-Command, и потоки не проходят должным образом. Если вы создаете удаленный сеанс напрямую, он работает нормально. Вот упрощенная версия вашего кода, которая использует удаленное пространство выполнения:

namespace ConsoleApp2
{
    using System;
    using System.Linq;
    using System.Management.Automation;
    using System.Management.Automation.Runspaces;
    using System.Security;

    class Program
    {
        static void Main(string[] args)
        {
            var script = @"
            Write-Host ""-------$env:computername-------""
            Write-Warning ""Write warning directly in warn script""
            Write-Error ""Write error directly in warn script""
            Write-Verbose ""Write verbose directly in warn script"" -verbose
            Write-Host ""Write host directly in warn script""
            Write-Information ""Write Information in warn script""
            ";

            SecureString password = new SecureString();
            "Password".ToCharArray().ToList().ForEach(c => password.AppendChar(c));
            PSCredential credentials = new PSCredential("UserName", password);

            WSManConnectionInfo connectionInfo = new WSManConnectionInfo();
            connectionInfo.ComputerName = "TargetServer";
            connectionInfo.Credential = credentials;

            using (var shell = PowerShell.Create())
            {
                using (var runspace = RunspaceFactory.CreateRunspace(connectionInfo))
                {
                    runspace.Open();

                    shell.Runspace = runspace;

                    shell.Streams.Information.DataAdded += LogProgress<InformationRecord>;
                    shell.Streams.Warning.DataAdded += LogProgress<WarningRecord>;
                    shell.Streams.Error.DataAdded += LogProgress<ErrorRecord>;
                    shell.Streams.Verbose.DataAdded += LogProgress<VerboseRecord>;
                    shell.Streams.Debug.DataAdded += LogProgress<DebugRecord>;

                    shell.AddScript(script);
                    shell.Invoke();
                }
            }

            Console.ReadKey();
        }

        private static void LogProgress<T>(object sender, DataAddedEventArgs e)
        {
            var data = (sender as PSDataCollection<T>)[e.Index];
            Console.WriteLine($"[{typeof(T).Name}] {Convert.ToString(data)}");
        }
    }
}
...