Выполнение повышенных сценариев PowerShell от C# in. NET Core 3.0 - PullRequest
2 голосов
/ 10 апреля 2020

Я вызываю самоподъемный скрипт powershell из C# кода. Скрипт сбрасывает настройки DNS. Сценарий отлично работает, когда вызывается из невысокого PowerShell, но не действует, когда вызывается из кода C# без исключений. Моя политика выполнения временно установлена ​​без ограничений, и я использую Visual Studio от имени администратора.

Кто-нибудь знает, что не так?

C#:

    class Program
{
    static void Main(string[] args)
    {

        var pathToScript = @"C:\Temp\test.ps1";
        Execute(pathToScript);

        Console.ReadKey();


    }
    public static void Execute(string command)
    {
        using (var ps = PowerShell.Create())
        {
            var results = ps.AddScript(command).Invoke();
            foreach (var result in results)
            {
                Console.WriteLine(result.ToString());
            }
        }
    }


}

Сценарий:

# Get the ID and security principal of the current user account
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent();
$myWindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($myWindowsID);

# Get the security principal for the administrator role
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator;

# Check to see if we are currently running as an administrator
if ($myWindowsPrincipal.IsInRole($adminRole))
{
    # We are running as an administrator, so change the title and background colour to indicate this
    $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)";
    $Host.UI.RawUI.BackgroundColor = "DarkBlue";
    Clear-Host;
}
else {
    # We are not running as an administrator, so relaunch as administrator

    # Create a new process object that starts PowerShell
    $newProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell";

    # Specify the current script path and name as a parameter with added scope and support for scripts with spaces in it's path
    $newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"

    # Indicate that the process should be elevated
    $newProcess.Verb = "runas";

    # Start the new process
    [System.Diagnostics.Process]::Start($newProcess);

    # Exit from the current, unelevated, process
    Exit;
}

# Run your code that needs to be elevated here...
Set-DnsClientServerAddress -InterfaceIndex 9 -ResetServerAddresses

1 Ответ

0 голосов
/ 10 апреля 2020

Как вы только что определили, основная проблема заключалась в том, что выполнение скрипта было отключено в вашей системе , что требовало (как минимум) изменения на уровне процесса политики выполнения PowerShell, как показывает следующий код C#, который вызывает
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass перед вызовом файла сценария (*.ps1):

  class Program
  {
    static void Main(string[] args)
    {

      var pathToScript = @"C:\Temp\test.ps1";
      Execute(pathToScript);

      Console.ReadKey();

    }

    public static void Execute(string command)
    {
      using (var ps = PowerShell.Create())
      {

        // Make sure that script execution is enabled at least for 
        // the current process.
        // For extra safety, you could try to save and restore
        // the policy previously in effect after executing your script.
        ps.AddCommand("Set-ExecutionPolicy")
          .AddParameter("Scope", "Process")
          .AddParameter("ExecutionPolicy", "Bypass")
          .Invoke();

        // Now invoke the script and print its success output.
        var results = ps.AddScript(command).Invoke();
        foreach (var result in results)
        {
          Console.WriteLine(result.ToString());
        }

        // Also report non-runspace-terminating errors, if any.
        foreach (var error in ps.Streams.Error)
        {
          Console.Error.WriteLine("ERROR: " + error.ToString());
        }

      }
    }

  }

Обратите внимание, что код также сообщает о любых ошибках, не заканчивающихся в пространстве выполнения , о которых скрипт мог сообщить, через stderr (стандартный поток вывода ошибок).

Без вызова Set-ExecutionPolicy, если политика выполнения не разрешать (неподписанное) выполнение сценария, PowerShell будет сообщать о не-завершающей ошибке через свой поток ошибок (.Streams.Error) вместо того, чтобы выдавать исключение .

Если бы вы проверили .Streams.Error для начала, вы бы обнаружили конкретную c причину вашей проблемы раньше.

Следовательно:

  • При использовании PowerShell SDK * 1 035 * В дополнение к , основываясь на / перехвате исключений, вы должны проверить .Streams.Error, чтобы определить, произошли (хотя бы формально менее серьезные) ошибки.

Потенциальные проблемы с вашим сценарием PowerShell:

  • Вы не дождетесь завершения процесса с повышенными правами, прежде чем вернуться из сценария PowerShell.

  • Вы не захватываете вывод процесса с повышенными правами, который вам необходим через свойства .RedirectStandardInput и .RedirectStandardError экземпляра System.Diagnostics.ProcessStartInfo, и затем заставить ваш скрипт выводить результаты.

Следующее, упрощенная версия вашего кода обращается к первой точке и вызывает powershell.exe CLI через -ExecutionPolicy Bypass.

  • Если вы используете Windows PowerShell SDK, в этом не должно быть необходимости (поскольку политика выполнения уже изменена в коде C#), но это может быть, если вы используете PowerShell [Core] SDK, учитывая, что две редакции PowerShell имеют отдельные параметры политики выполнения.
$isElevated = & { net session *>$null; $LASTEXITCODE -eq 0 }

# Check to see if we are currently running as an administrator
if ($isElevated)
{
    # We are running as an administrator, so change the title and background colour to indicate this
    $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
    $Host.UI.RawUI.BackgroundColor = "DarkBlue"
    Clear-Host
}
else {
    # We are not running as an administrator, so relaunch as administrator

    # Create a new process object that starts PowerShell
    $psi = New-Object System.Diagnostics.ProcessStartInfo 'powershell.exe'

    # Specify the current script path and name as a parameter with and support for scripts with spaces in its path
    $psi.Arguments = '-ExecutionPolicy Bypass -File "{0}"' -f 
                     $script:MyInvocation.MyCommand.Path

    # Indicate that the process should be elevated.
    $psi.Verb = 'RunAs'
    # !! For .Verb to be honored, .UseShellExecute must be $true
    # !! In .NET Framework, .UseShellExecute *defaults* to $true,
    # !! but no longer in .NET Core.
    $psi.UseShellExecute = $true 

    # Start the new process, wait for it to terminate, then
    # exit from the current, unelevated process, passing the exit code through.
    exit $(
     try { ([System.Diagnostics.Process]::Start($psi).WaitForExit()) } catch { Throw }
    )

}

# Run your code that needs to be elevated here...
Set-DnsClientServerAddress -InterfaceIndex 9 -ResetServerAddresses
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...