Execute PowerShell Script from C# with Commandline Arguments

231,493

Solution 1

Try creating scriptfile as a separate command:

Command myCommand = new Command(scriptfile);

then you can add parameters with

CommandParameter testParam = new CommandParameter("key","value");
myCommand.Parameters.Add(testParam);

and finally

pipeline.Commands.Add(myCommand);

Here is the complete, edited code:

RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();

Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();

Pipeline pipeline = runspace.CreatePipeline();

//Here's how you add a new script with arguments
Command myCommand = new Command(scriptfile);
CommandParameter testParam = new CommandParameter("key","value");
myCommand.Parameters.Add(testParam);

pipeline.Commands.Add(myCommand);

// Execute PowerShell script
results = pipeline.Invoke();

Solution 2

I have another solution. I just want to test if executing a PowerShell script succeeds, because perhaps somebody might change the policy. As the argument, I just specify the path of the script to be executed.

ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = @"powershell.exe";
startInfo.Arguments = @"& 'c:\Scripts\test.ps1'";
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();

string output = process.StandardOutput.ReadToEnd();
Assert.IsTrue(output.Contains("StringToBeVerifiedInAUnitTest"));

string errors = process.StandardError.ReadToEnd();
Assert.IsTrue(string.IsNullOrEmpty(errors));

With the contents of the script being:

$someVariable = "StringToBeVerifiedInAUnitTest"
$someVariable

Solution 3

I had trouble passing parameters to the Commands.AddScript method.

C:\Foo1.PS1 Hello World Hunger
C:\Foo2.PS1 Hello World

scriptFile = "C:\Foo1.PS1"

parameters = "parm1 parm2 parm3" ... variable length of params

I Resolved this by passing null as the name and the param as value into a collection of CommandParameters

Here is my function:

private static void RunPowershellScript(string scriptFile, string scriptParameters)
{
    RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
    Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
    runspace.Open();
    RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
    Pipeline pipeline = runspace.CreatePipeline();
    Command scriptCommand = new Command(scriptFile);
    Collection<CommandParameter> commandParameters = new Collection<CommandParameter>();
    foreach (string scriptParameter in scriptParameters.Split(' '))
    {
        CommandParameter commandParm = new CommandParameter(null, scriptParameter);
        commandParameters.Add(commandParm);
        scriptCommand.Parameters.Add(commandParm);
    }
    pipeline.Commands.Add(scriptCommand);
    Collection<PSObject> psObjects;
    psObjects = pipeline.Invoke();
}

Solution 4

Mine is a bit more smaller and simpler:

/// <summary>
/// Runs a PowerShell script taking it's path and parameters.
/// </summary>
/// <param name="scriptFullPath">The full file path for the .ps1 file.</param>
/// <param name="parameters">The parameters for the script, can be null.</param>
/// <returns>The output from the PowerShell execution.</returns>
public static ICollection<PSObject> RunScript(string scriptFullPath, ICollection<CommandParameter> parameters = null)
{
    var runspace = RunspaceFactory.CreateRunspace();
    runspace.Open();
    var pipeline = runspace.CreatePipeline();
    var cmd = new Command(scriptFullPath);
    if (parameters != null)
    {
        foreach (var p in parameters)
        {
            cmd.Parameters.Add(p);
        }
    }
    pipeline.Commands.Add(cmd);
    var results = pipeline.Invoke();
    pipeline.Dispose();
    runspace.Dispose();
    return results;
}

Solution 5

You can also just use the pipeline with the AddScript Method:

string cmdArg = ".\script.ps1 -foo bar"            
Collection<PSObject> psresults;
using (Pipeline pipeline = _runspace.CreatePipeline())
            {
                pipeline.Commands.AddScript(cmdArg);
                pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
                psresults = pipeline.Invoke();
            }
return psresults;

It will take a string, and whatever parameters you pass it.

Share:
231,493
Mephisztoe
Author by

Mephisztoe

Updated on August 13, 2021

Comments

  • Mephisztoe
    Mephisztoe almost 3 years

    I need to execute a PowerShell script from within C#. The script needs commandline arguments.

    This is what I have done so far:

    RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
    
    Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
    runspace.Open();
    
    RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
    
    Pipeline pipeline = runspace.CreatePipeline();
    pipeline.Commands.Add(scriptFile);
    
    // Execute PowerShell script
    results = pipeline.Invoke();
    

    scriptFile contains something like "C:\Program Files\MyProgram\Whatever.ps1".

    The script uses a commandline argument such as "-key Value" whereas Value can be something like a path that also might contain spaces.

    I don't get this to work. Does anyone know how to pass commandline arguments to a PowerShell script from within C# and make sure that spaces are no problem?

  • Mephisztoe
    Mephisztoe over 15 years
    I still seem to have the problem that if value is something like c:\program files\myprogram, the key is set to c:\program. :(
  • Mephisztoe
    Mephisztoe over 15 years
    Never mind. Sometimes it helps when you know how to correctly split strings. ;-) Thanks again, your solution helped me in resolving my problem!
  • Steven Murawski
    Steven Murawski over 15 years
    @Tronex - you should be defining the key as being a parameter for your script. PowerShell has some great builtin tools for working with paths. Maybe ask another question about that. @Kosi2801 has the correct answer for adding parameters.
  • Steven Murawski
    Steven Murawski over 15 years
    Typing my reply overlapped yours.. I'm glad you got it resolved!
  • Eugeniu Torica
    Eugeniu Torica over 11 years
    Hi. Do you have any idea why starting powershell as you described and executing all commands process (in our case) does not exit?
  • niaher
    niaher over 10 years
    scriptInvoker variable isn't being used.
  • SoftwareSavant
    SoftwareSavant about 10 years
    What library are you using
  • Red
    Red almost 8 years
    Just added:using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))...usi‌​ng (Pipeline pipeline = runspace.CreatePipeline())
  • Muhammad Noman
    Muhammad Noman almost 6 years
    When I am passing multiple parameters, it occurs this error: An unhandled exception of type 'System.Management.Automation.ParseException' occurred in System.Management.Automation.dll
  • Asif Iqbal
    Asif Iqbal about 4 years
    How to capture the powershell output in c# back.In my case pipeline.Invoke() return null value if there is any write-host in the ps script.
  • nardnob
    nardnob over 3 years
    ftfy @niaher :)
  • Christian Storb
    Christian Storb over 3 years
    To capture the powershell output in c# see stackoverflow.com/questions/10106217/…
  • Passer
    Passer about 2 years
    Which .NET Version is the project and which packages need a reference for those classes?