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?
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();
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
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();
}
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.
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;
}
For me, the most flexible way to run PowerShell script from C# was using PowerShell.Create().AddScript()
First you'll need to install the Microsoft.PowerShell.SDK nuget package. Or if targeting .net framework you'll need Microsoft.PowerShell.5.ReferenceAssemblies
The snippet of the code is
using System.Management.Automation;
string scriptDirectory = Path.GetDirectoryName(
ConfigurationManager.AppSettings["PathToTechOpsTooling"]);
var script =
"Set-Location " + scriptDirectory + Environment.NewLine +
"Import-Module .\\script.psd1" + Environment.NewLine +
"$data = Import-Csv -Path " + tempCsvFile + " -Encoding UTF8" +
Environment.NewLine +
"New-Registration -server " + dbServer + " -DBName " + dbName +
" -Username \"" + user.Username + "\" + -Users $userData";
_powershell = PowerShell.Create().AddScript(script);
_powershell.Invoke<User>();
foreach (var errorRecord in _powershell.Streams.Error)
Console.WriteLine(errorRecord);
You can check if there's any error by checking Streams.Error. It was really handy to check the collection.
User is the type of object the PowerShell script returns.
Here is a way to add Parameters to the script if you used
pipeline.Commands.AddScript(Script);
This is with using an HashMap as paramaters the key being the name of the variable in the script and the value is the value of the variable.
pipeline.Commands.AddScript(script));
FillVariables(pipeline, scriptParameter);
Collection<PSObject> results = pipeline.Invoke();
And the fill variable method is:
private static void FillVariables(Pipeline pipeline, Hashtable scriptParameters)
{
// Add additional variables to PowerShell
if (scriptParameters != null)
{
foreach (DictionaryEntry entry in scriptParameters)
{
CommandParameter Param = new CommandParameter(entry.Key as String, entry.Value);
pipeline.Commands[0].Parameters.Add(Param);
}
}
}
this way you can easily add multiple parameters to a script. I've also noticed that if you want to get a value from a variable in you script like so:
Object resultcollection = runspace.SessionStateProxy.GetVariable("results");
//results being the name of the v
you'll have to do it the way I showed because for some reason if you do it the way
Kosi2801 suggests the script variables list doesn't get filled with your own variables.
Here's what it worked for me, including cases when the arguments contains spaces:
using (PowerShell PowerShellInst = PowerShell.Create())
{
PowerShell ps = PowerShell.Create();
string param1= "my param";
string param2= "another param";
string scriptPath = <path to script>;
ps.AddScript(File.ReadAllText(scriptPath));
ps.AddArgument(param1);
ps.AddArgument(param2);
ps.Invoke();
}
I find this approach very easy to understand and very clear.
If your code cannot find the System.Management.Automation.Runspaces namespace you need to add a dependency to System.Management.Automation.dll.
This DLL gets shipped with PowerShell and is by default located in the directory: C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0
To add a reference open your project and right-click "references > add reference" and select the "Browse" button to go to the above mentioned location and select the needed .dll file. Click "add" and the reference will show up in the browse tab with a checked checkbox next to it.
After adding the referenceSystem.Management.Automation.Runspaces
You can run the in other mentioned code to add parameters and execute the PowerShell script. I find it very convenient to use a tuple to store the "key", "value" pairs.
/// <summary>
/// Run a powershell script with a list of arguments
/// </summary>
/// <param name="commandFile">The .ps1 script to execute</param>
/// <param name="arguments">The arguments you want to pass to the script as parameters</param>
private void ExecutePowerShellCommand(string commandFile, List<Tuple<string, string>> arguments)
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
//commandFile is the PowerShell script you want to execute, e.g. "FooBar.ps1"
Command cmd = new Command(commandFile);
// Loop through all the tuples containing the "key", "value" pairs and add them as a command parameter
foreach (var parameter in arguments)
cmd.Parameters.Add(new CommandParameter(parameter.Item1, parameter.Item2));
pipeline.Commands.Add(cmd);
// Execute the PowerShell script
var result = pipeline.Invoke();
}
and the calling code:
string commandFile = #"C:\data\test.ps1";
List<Tuple<string, string>> arguments = new List<Tuple<string, string>>();
arguments.Add(new Tuple<string, string>("filePath", #"C:\path\to\some\file"));
arguments.Add(new Tuple<string, string>("fileName", "FooBar.txt"));
ExecutePowerShellCommand(commandFile, arguments);
Related
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?
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();
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
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();
}
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.
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;
}
For me, the most flexible way to run PowerShell script from C# was using PowerShell.Create().AddScript()
First you'll need to install the Microsoft.PowerShell.SDK nuget package. Or if targeting .net framework you'll need Microsoft.PowerShell.5.ReferenceAssemblies
The snippet of the code is
using System.Management.Automation;
string scriptDirectory = Path.GetDirectoryName(
ConfigurationManager.AppSettings["PathToTechOpsTooling"]);
var script =
"Set-Location " + scriptDirectory + Environment.NewLine +
"Import-Module .\\script.psd1" + Environment.NewLine +
"$data = Import-Csv -Path " + tempCsvFile + " -Encoding UTF8" +
Environment.NewLine +
"New-Registration -server " + dbServer + " -DBName " + dbName +
" -Username \"" + user.Username + "\" + -Users $userData";
_powershell = PowerShell.Create().AddScript(script);
_powershell.Invoke<User>();
foreach (var errorRecord in _powershell.Streams.Error)
Console.WriteLine(errorRecord);
You can check if there's any error by checking Streams.Error. It was really handy to check the collection.
User is the type of object the PowerShell script returns.
Here is a way to add Parameters to the script if you used
pipeline.Commands.AddScript(Script);
This is with using an HashMap as paramaters the key being the name of the variable in the script and the value is the value of the variable.
pipeline.Commands.AddScript(script));
FillVariables(pipeline, scriptParameter);
Collection<PSObject> results = pipeline.Invoke();
And the fill variable method is:
private static void FillVariables(Pipeline pipeline, Hashtable scriptParameters)
{
// Add additional variables to PowerShell
if (scriptParameters != null)
{
foreach (DictionaryEntry entry in scriptParameters)
{
CommandParameter Param = new CommandParameter(entry.Key as String, entry.Value);
pipeline.Commands[0].Parameters.Add(Param);
}
}
}
this way you can easily add multiple parameters to a script. I've also noticed that if you want to get a value from a variable in you script like so:
Object resultcollection = runspace.SessionStateProxy.GetVariable("results");
//results being the name of the v
you'll have to do it the way I showed because for some reason if you do it the way
Kosi2801 suggests the script variables list doesn't get filled with your own variables.
Here's what it worked for me, including cases when the arguments contains spaces:
using (PowerShell PowerShellInst = PowerShell.Create())
{
PowerShell ps = PowerShell.Create();
string param1= "my param";
string param2= "another param";
string scriptPath = <path to script>;
ps.AddScript(File.ReadAllText(scriptPath));
ps.AddArgument(param1);
ps.AddArgument(param2);
ps.Invoke();
}
I find this approach very easy to understand and very clear.
If your code cannot find the System.Management.Automation.Runspaces namespace you need to add a dependency to System.Management.Automation.dll.
This DLL gets shipped with PowerShell and is by default located in the directory: C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0
To add a reference open your project and right-click "references > add reference" and select the "Browse" button to go to the above mentioned location and select the needed .dll file. Click "add" and the reference will show up in the browse tab with a checked checkbox next to it.
After adding the referenceSystem.Management.Automation.Runspaces
You can run the in other mentioned code to add parameters and execute the PowerShell script. I find it very convenient to use a tuple to store the "key", "value" pairs.
/// <summary>
/// Run a powershell script with a list of arguments
/// </summary>
/// <param name="commandFile">The .ps1 script to execute</param>
/// <param name="arguments">The arguments you want to pass to the script as parameters</param>
private void ExecutePowerShellCommand(string commandFile, List<Tuple<string, string>> arguments)
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
//commandFile is the PowerShell script you want to execute, e.g. "FooBar.ps1"
Command cmd = new Command(commandFile);
// Loop through all the tuples containing the "key", "value" pairs and add them as a command parameter
foreach (var parameter in arguments)
cmd.Parameters.Add(new CommandParameter(parameter.Item1, parameter.Item2));
pipeline.Commands.Add(cmd);
// Execute the PowerShell script
var result = pipeline.Invoke();
}
and the calling code:
string commandFile = #"C:\data\test.ps1";
List<Tuple<string, string>> arguments = new List<Tuple<string, string>>();
arguments.Add(new Tuple<string, string>("filePath", #"C:\path\to\some\file"));
arguments.Add(new Tuple<string, string>("fileName", "FooBar.txt"));
ExecutePowerShellCommand(commandFile, arguments);
I need to execute a powershell script from my asp.net MVC Web application. My requirement is to create site collections dynamically. I have the script for it and it works perfectly.There are no arguments which are to be passed to the script. The code which I have been using has been displayed below:
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
//Here's how you add a new script with arguments
Command myCommand = new Command(scriptfiellocation);
pipeline.Commands.Add(myCommand);
pipeline.Commands.Add("Out-String");
// Execute PowerShell script
var result = pipeline.Invoke();
On executing the code, when I check the count of variable result it gives the count as 1. However on checking my site, there is no site collection that has been created. I am not able to identify where I am going wrong as there is no run time error and the Invoke command also seems to be running properly.
Could anyone tell me where I might be going haywire ? Considering that the PowerShell script works perfectly when running through Management shell.
I had to forego the pipeline approach as I was not able to figure out what the issue was. Also another problem with that approach is that it threw the error: "Get-SPWbTemplate is not recognized as an cmdlet". The following code worked perfectly fine for me and created the required site collections:
PowerShell powershell = PowerShell.Create();
//RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
//Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration)
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
//RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
//scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
powershell.Runspace = runspace;
//powershell.Commands.AddScript("Add-PsSnapin Microsoft.SharePoint.PowerShell");
System.IO.StreamReader sr = new System.IO.StreamReader(scriptfilepath);
powershell.AddScript(sr.ReadToEnd());
//powershell.AddCommand("Out-String");
var results = powershell.Invoke();
if (powershell.Streams.Error.Count > 0)
{
// error records were written to the error stream.
// do something with the items found.
}
}
Also there was no requirement to set the execution policy.
well don't know if its help but i never use pipeline to run Command shell not sure how that work.
But here a quick example
Runspace RS = RunspaceFactory.CreateRunspace(myConnection);
PowerShell PS = PowerShell.Create();
PSCommand PScmd = new PSCommand();
string cmdStr = "Enable-Mailbox -Identity " + username + " -Database DB01 -Alias " + aliasexample;
PScmd.AddScript(cmdStr);
try
{
RS.Open();
PS.Runspace = RS;
PS.Commands = PScmd;
PS.Invoke();
}
catch (Exception ex)
{
ex.ToString();
}
finally
{
RS.Dispose();
RS = null;
PS.Dispose();
PS = null;
}
with the try catch you can catch the error with debugging if something goes wrong.
If i remember correctly i had to put ACL.exe for permission to file system so i can execute the commandshell you can do a quick search on google for it.
Hope this help.
This is an incredibly frustrating problem that nobody else seems to have the answer to so I'm trying to break my question down into the simplest thing possible. Surely there are other 3rd party Powershell SDK's out there that people have tried to access and use via C#. Does anybody know why this would get an error saying the following?
The term 'add-PSSnapin Citrix*.Admin.V* is not recognized as the name of a cmdlet, function, script file, or operable program.
The same goes for other commands that this PSSnapin provides at the command prompt.
I can type this command in manually at a powershell command prompt and it works. So do the other commands. What's the deal?
public void testPS()
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddCommand("add-PSSnapin Citrix.*.Admin.V*");
ps.Invoke();
//Commented sections below don't work either, same error.
//ps.AddCommand("Get-BrokerSession");
//ps.AddParameter("AdminAddress");
//ps.AddParameter("SERVERNAME");
//ps.Invoke();
//Collection<PSObject> psr = ps.Invoke();
//foreach (PSObject x in psr)
//{
// MessageBox.Show(x.ToString());
//}
}
}
UPDATE:
This new code as suggested in the answer below gets a new error: 'System.Management.Automation.ParameterBindingException' occurred in System.Management.Automation.dll
public void testPS()
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
PSSnapInException psex;
runspace.RunspaceConfiguration.AddPSSnapIn("Citrix.Broker.Admin.V2", out psex);
Pipeline pipeline = runspace.CreatePipeline();
Command getSession = new Command("Get-BrokerSession");
getSession.Parameters.Add("AdminAddress");
getSession.Parameters.Add("MYSERVERNAME");
//also tried the above with this code
//getSession.Parameters.Add("-AdminAddress MYSERVERNAME");
// and
//getSession.Parameters.Add("AdminAddress MYSERVERNAME");
// and other methods as well
pipeline.Commands.Add(getSession);
//This line below is where the exception occurs.
Collection<PSObject> output = pipeline.Invoke();
foreach (PSObject x in output)
{
MessageBox.Show(x.ToString());
}
}
}
UPDATE 2:
I also get this same error above trying to set the execution policy.
UPDATE 3:
Fixed, see comments in answer below. The syntax of the parameters line was incorrect.
Use RunspaceConfiguration.AddPSSnapIn to add PSSnapin and then add a command:
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
PSSnapInException psex;
runSpace.RunspaceConfiguration.AddPSSnapIn("Citrix.Broker.Admin.V2", out psex);
Pipeline pipeline = runSpace.CreatePipeline();
Command getSession = new Command("Get-BrokerSession");
getSession.Parameters.Add("AdminAddress", "SERVERNAME");
pipeline.Commands.Add(getSession);
Collection<PSObject> output = pipeline.Invoke();
You need to make sure that you split the command from its argument. In your case, it would be something like:
ps.AddCommand("add-PSSnapin");
Then you can always just post-append Citrix.*.Admin.V* as an argument for the command above.
For those still stuck with this like me, even as of the latest Microsoft.Powershell.Sdk 6.2.0 nuget package I was running into this issue. A colleague of mine ended up getting it to work by doing the following:
public string RunPowershell(string param1)
{
var outputString = "";
var startInfo = new ProcessStartInfo
{
FileName = #"powershell.exe",
Arguments = "C:\\path\\to\\script.ps1" + " -param1 " + param1,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var process = new Process())
{
process.StartInfo = startInfo;
process.Start();
var output = process.StandardOutput.ReadToEnd();
var errors = process.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(output))
{
outputString = output;
}
if (!string.IsNullOrEmpty(errors))
{
Console.WriteLine($"Error: {errors}");
}
}
return outputString;
}
"C:\path\to\script.ps1" can have Add-PSSnapin commands in it and still run just fine.
I couldn't find a bug filed for Microsoft.Powershell.Sdk to handle this, but if someone creates one and links it here, thanks!
Hope this helps
How can I get the powershell variable $test from a c# application?
I tried it like this:
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
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");
CommandParameter testParam2 = new CommandParameter("key", "value");
CommandParameter testParam3 = new CommandParameter("key", "value");
myCommand.Parameters.Add(testParam);
myCommand.Parameters.Add(testParam2);
myCommand.Parameters.Add(testParam3);
pipeline.Commands.Add(myCommand);
// Execute PowerShell script
var results = pipeline.Invoke();
var resultVariable = runspace.SessionStateProxy.PSVariable.GetValue("test");
resultVariable is null. But I filled it in powershell with an int (i.e. $test = 4).
Use $global:test=4 in script or run script in same scope. By default script run in its own scope, so any variable changed in script does not visible outside.
I'm writing 2 apps one with c# and the other with powershell 1.0, in some point of my code I want to pass a string that indicating the server name from my c# app to a powershell script file that I wrote, how do I send it? and how do i accept it?
my code :
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
String scriptfile = #"c:\test.ps1";
Command myCommand = new Command(scriptfile, false);
CommandParameter testParam = new CommandParameter("username", "serverName");
myCommand.Parameters.Add(testParam);
pipeline.Commands.Add(myCommand);
Collection<PSObject> psObjects;
psObjects = pipeline.Invoke();
runspace.Close();
and my powershell script
param([string]$Username)
write-host $username
What am I missing? I'm kinda new with powershell.
I have machines with PowerShell 2.0 and 3.0 but not 1.0, so my results may differ. When I run your code on my PowerShell 3.0 box, I get:
A command that prompts the user failed because the host program or the
command type does not support user interaction. Try a host program
that supports user interaction, such as the Windows PowerShell Console
or Windows PowerShell ISE, and remove prompt-related commands from
command types that do not support user interaction, such as Windows
PowerShell workflows.
It didn't like the Write-Host, so I changed your script to
param([string]$Username)
Get-Date
Get-ChildItem -Path $userName
Get-Date so that I could see some output without depending on the parameter and GCI to use the parameter. I modified your code to look like this:
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
using (var runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
{
runspace.Open();
String scriptfile = #"..\..\..\test.ps1";
String path = #"C:\Users\Public\";
var pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(new Command("Set-ExecutionPolicy RemoteSigned -Scope Process", true));
pipeline.Invoke();
pipeline = runspace.CreatePipeline();
var myCommand = new Command(scriptfile, false);
var testParam = new CommandParameter("username", path);
myCommand.Parameters.Add(testParam);
pipeline.Commands.Add(myCommand);
var psObjects = pipeline.Invoke();
foreach (var obj in psObjects)
{
Console.WriteLine(obj.ToString());
}
runspace.Close();
}
Console.WriteLine("Press a key to continue...");
Console.ReadKey(true);
and it ran without error and displayed the folder contents, on both PoSh 2 and 3.
For info, if you're only setting the execution policy for the current process, you don't need to run elevated, hence I was able to do it in-code.