1

I am very bad and beginner with the PowerShell. What I need is to with the request that I receive run a powershell script. My request and response that I need looks like this:

public class MyRequest
{
    public string Script { get; set; }             // PowerShell script content
    public string Policy { get; set; }             // e.g. "Bypass", "RemoteSigned"
    public string Scope { get; set; }              // e.g. "Process", "CurrentUser"
    public bool RunAsAdmin { get; set; }           // true = elevated
    public int TimeoutInSeconds { get; set; }      // timeout duration
}

public class PowerShellResult
{
    public int ExitCode { get; set; }
    public string Output { get; set; }
    public string Error { get; set; }
}

I am using CliWrap library. Now my problem is that I need to run the script as an admin if it is set in the request. I tried everything, even ChatGPT at the end.

When I set the run as no admin, it runs, but when set to as admin, the console is empty. This is my sample code.

using CliWrap;
using CliWrap.Buffered;
using System.Text;

var runner = new PowerShellRunner();
var result =  await runner.RunAsync(new MyRequest
{
    Script = "Write-Output 'Hello from PowerShell'",
    Policy = "Restricted",
    Scope = "Process",
    RunAsAdmin = true,
    TimeoutInSeconds = 30
});

Console.WriteLine($"PowerShell script execution completed with code: {result.ExitCode}.");
Console.WriteLine($"Errors: {result.Error}.");
Console.WriteLine($"Output: {result.Output}.");
Console.ReadKey();

public class MyRequest
{
    public string Script { get; set; }             // PowerShell script content
    public string Policy { get; set; }             // e.g. "Bypass", "RemoteSigned"
    public string Scope { get; set; }              // e.g. "Process", "CurrentUser"
    public bool RunAsAdmin { get; set; }           // true = elevated
    public int TimeoutInSeconds { get; set; }      // timeout duration
}

public class PowerShellResult
{
    public int ExitCode { get; set; }
    public string Output { get; set; }
    public string Error { get; set; }
}

public class PowerShellRunner
{
    public async Task<PowerShellResult> RunAsync(MyRequest request)
    {
        var errors = new StringBuilder();
        var output = new StringBuilder();

        using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(request.TimeoutInSeconds));

        try
        {
            if (request.RunAsAdmin)
            {
                // Launch elevated PowerShell
                string elevateArgs =
                    $"-NoProfile -ExecutionPolicy {request.Policy} '{request.Script}'";

                string elevateCommand =
                    $"Start-Process powershell -Verb RunAs -ArgumentList \"{elevateArgs}\"";

                Console.WriteLine($"Command: {elevateCommand}");

                var result = await Cli.Wrap("powershell")
                    .WithArguments(a => a.Add("-NoProfile").Add("-Command").Add(elevateCommand))
                    .WithValidation(CommandResultValidation.None)
                    .WithStandardOutputPipe(PipeTarget.ToStringBuilder(output))
                    .WithStandardErrorPipe(PipeTarget.ToStringBuilder(errors))
                    .ExecuteBufferedAsync(cts.Token);

                return new PowerShellResult
                {
                    ExitCode = result.ExitCode,
                    Output = output.ToString(),
                    Error = errors.ToString()
                };
            }
            else
            {
                // Normal PowerShell run
                var result = await Cli.Wrap("powershell")
                    .WithArguments(a => a
                        .Add("-NoProfile")
                        .Add("-ExecutionPolicy").Add(request.Policy)
                        .Add(request.Script))
                    .WithValidation(CommandResultValidation.None)
                    .WithStandardOutputPipe(PipeTarget.ToStringBuilder(output))
                    .WithStandardErrorPipe(PipeTarget.ToStringBuilder(errors))
                    .ExecuteBufferedAsync(cts.Token);

                return new PowerShellResult
                {
                    ExitCode = result.ExitCode,
                    Output = output.ToString(),
                    Error = errors.ToString()
                };
            }
        }
    }
}

thnx

2
  • This might answer why your elevated process doesn't produce any output as well as a workaround using named pipes.... stackoverflow.com/a/79655209/15339544 Commented Oct 20 at 17:47
  • As an aside: Start-Process never allows you to capture the launched process' output streams directly: you need to use -RedirectStandardOutput and/or -RedirectStandardError in order to capture output in files. However, doing so is incompatible with elevation (-Verb RunAs), and the simplest alternative is to make the launched, elevated process itself write its output to file(s), which is also what AleksaRistic's answers suggests. See this answer for background information. Commented Oct 20 at 21:32

1 Answer 1

4

When you invoke PowerShell with administrative privileges, it no longer runs within your application's context. Instead, it's executed through the elevated Windows shell. This shift breaks the standard output redirection pipeline, meaning your application cannot directly capture the output—especially if the library you're using doesn't implement a workaround for this behavior.

Easiest workaround with this is to actually redirect output to file (which is allowed) and then read file within your application.

Sample code how you can achieve the said thing without any library:

class Program
{
    static void Main()
    {
        string tempFile = Path.Combine(Path.GetTempPath(), "ps_output.txt");
        string command = $"\"Get-Process | Select-Object -First 5 | Out-File -FilePath '{tempFile}'\"";

        ProcessStartInfo psi = new ProcessStartInfo
        {
            FileName = "powershell.exe",
            Arguments = $"-NoProfile -ExecutionPolicy Bypass -Command {command}",
            Verb = "runas",
            UseShellExecute = true
        };

        try
        {
            Process.Start(psi).WaitForExit();

            if (File.Exists(tempFile))
            {
                string output = File.ReadAllText(tempFile);
                Console.WriteLine("Output:\n" + output);
                File.Delete(tempFile);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }
    }
}

An alternative workaround is to ensure the user launches your application with administrative privileges. In this scenario, PowerShell—running with the same elevated access—would inherit the application's context, allowing output to be displayed immediately. However, this approach introduces significant security concerns. Granting your entire application administrative permissions means that any user input could potentially modify critical system components, increasing the risk of unintended or malicious changes.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.