Execute PowerShell Script from C# with Commandline Arguments
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?
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.