I'm trying to run powershell commands using C# but I keep getting errors when I invoke the pipeline. I was wondering if anyone know why I keep getting add-windowsfeature is not recognized. Thanks in advance.
private static void RunScript(string name)
{
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new[] { "ServerManager"});
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
// create Powershell runspace
runspace.Open();
RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runspace);
runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
Pipeline pipeline = runspace.CreatePipeline();
Command cm = new Command("Import-module");
cm.Parameters.Add("name","ServerManager");
pipeline.Commands.Add(cm);
Command command = new Command("add-windowsfeature");
command.Parameters.Add(null, name);
pipeline.Commands.Add(command);
var a = pipeline.Invoke();
foreach (var psObject in a)
{
Console.WriteLine(psObject);
}
runspace.Close();
}
ServerManager is a 64-bit only module (it doesn't exist under C:\Windows\SysWOW64\WindowsPowerShell\v1.0\Modules but will exist under C:\Windows\System32\WindowsPowerShell\v1.0\Modules). Compile as x64 and your code should work.
Related
I've been trying to enable RabbitMQ management plugin using C# code.
I was successfully able to install RabbitMQ server using c# by using following code.
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
Command myCommand = new Command("Start-Process");
CommandParameter testParam = new CommandParameter("FilePath", #"C:\Users\saadp\Desktop\Dependencies\rabbitmq-server-3.8.3.exe");
CommandParameter testParam2 = new CommandParameter("ArgumentList", new string[] { "/S" });
CommandParameter testParam3 = new CommandParameter("Wait");
myCommand.Parameters.Add(testParam);
myCommand.Parameters.Add(testParam2);
myCommand.Parameters.Add(testParam3);
pipeline.Commands.Add(myCommand);
var results = pipeline.Invoke();
But, When I try to enable RabbitMQ management plugin using following CommandParameters, It doesn't affect anything. What actually happens is after executing this code new Command Prompt opens and closes in a matter of fraction.
Here is the code which I've tried.
CommandParameter testParam = new CommandParameter("FilePath", #"""C:\Program Files\RabbitMQ Server\rabbitmq_server-3.8.3\sbin\rabbitmq-plugins.bat""");
CommandParameter testParam2 = new CommandParameter("ArgumentList", new string[] { "'enable rabbitmq_management'" });
CommandParameter testParam3 = new CommandParameter("Wait");
I got this working by following this code.
private static string RunScript(string scriptText)
{
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// add an extra command to transform the script
// output objects into nicely formatted strings
// remove this line to get the actual objects
// that the script returns. For example, the script
// "Get-Process" returns a collection
// of System.Diagnostics.Process instances.
pipeline.Commands.Add("Out-String");
// execute the script
pipeline.Invoke();
// close the runspace
runspace.Close();
}
Try this:
var startInfo =
new ProcessStartInfo
{
FileName = #"C:\Windows\System32\cmd.exe",
Arguments = "/c rabbitmq-plugins enable rabbitmq_management",
WorkingDirectory = #"C:\Program Files\RabbitMQ Server\rabbitmq_server-3.7.11\sbin",
WindowStyle = ProcessWindowStyle.Maximized
};
Process.Start(startInfo)?.WaitForExit();
Process.Start("net", "stop RabbitMQ")?.WaitForExit();
Process.Start("net", "start RabbitMQ")?.WaitForExit();
How to verify whether module (Import module ) command is imported module successfully in C# ? I have my own custom powershell, which is imported in Windows powershell.I have written C# code which will import the my custom powershell into windows powershell, When I try to execute custom powershell command from code it is not returning any result whereas when i execute same command from windows powershell (By importing module and writing a custom powershell command) it is working. I am using following code
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { "ABCD" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
initial.ThrowOnRunspaceOpenError = true;
runspace.Open();
RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runspace);
runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
string script = System.IO.File.ReadAllText(#"D:\Export-Pipeline-Script.txt");
ps.AddScript(script);
ps.Invoke();
ps.Commands.Clear();
ps.AddCommand("Test2");
Collection<PSObject> results1 = ps.Invoke();
foreach (PSObject outputItem in results1)
{
if (outputItem != null)
{
Console.WriteLine(outputItem.ToString());
}
}
In AddCommand the Test2 is a function in which I have written the custom powershell command. In the above code results1 always written count as "0" whereas when I changed the custom powershell command to windows powershell command like Get-Process, it works.
Here is an example that worked for me. I created a PS module and a custom PS script and used them in your C# code. this might give you some clue as to how you can make yours work.
C:\Temp\PowerShell.Module.psm1
here is the custom powerhell. C:\Temp\PS\GetProc.ps1
worked with this code:
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { #"C:\Temp\PowerShell.Module.psm1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
initial.ThrowOnRunspaceOpenError = true;
runspace.Open();
RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runspace);
runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
string script = System.IO.File.ReadAllText(#"C:\Temp\Export-Pipeline-Script.txt");
ps.AddScript(script);
ps.Invoke();
ps.Commands.Clear();
ps.AddCommand("GetProc");
Collection<PSObject> results1 = ps.Invoke();
foreach (PSObject outputItem in results1)
{
if (outputItem != null)
{
Console.WriteLine(outputItem.ToString());
}
}
result:
I have this code in which I load a snapin (from MS Dynamics NAV in this case):
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript("Add-PSSnapin Microsoft.Dynamics.Nav.Management")
.AddScript("Get-NAVServerInstance");
//This does not work. Says unknown cmdlet Get-NAVServerInstance
//ps.AddCommand("Add-PSSnapin").AddArgument("Microsoft.Dynamics.Nav.Management")
// .AddCommand("Get-NAVServerInstance");
var output = ps.Invoke();
}
}
This code works when I use the AddScript method as shown in the code.
But why does AddCommand method not work (see commented code)? Looks like the snapin is not loaded, because the error says that the Get-NAVServerInstance cmdlet is unknown.
How is this supposed to work?
I know I can create a runspace with an InitialSessionState on which I have imported the snapin. Then the ps.AddCommand("Get-NAVServerInstance") is working.
But when I want to create a remote runspace session (using WSManConnectionInfo) I can't find a way to supply an initialSessionState.
UPDATE:
So it seems that AddCommand only can be used for cmdlets available when the runspace is opened (or created?). Using an InitialSessionState or RunspaceConfiguration instance with RunspaceFactory.CreateRunspace(...) will do. So this code works:
var config = RunspaceConfiguration.Create();
PSSnapInException warning;
config.AddPSSnapIn("Microsoft.Dynamics.Nav.Management", out warning);
using (Runspace runspace = RunspaceFactory.CreateRunspace(config))
{
runspace.Open();
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-NAVServerInstance");
var output = ps.Invoke();
}
}
But my problem is in that case, that I can't specify a WSManConnectionInfo instance.
So how can I create a runspace with a remote connection with a snapin (installed on the remote machine) loaded? How to supply a configuration for a remote connection?
I finally found a hint how to configure a remote session (see https://superuser.com/a/518567).
You need to register a session configuration on the remote computer with
Register-PSSessionConfiguration -Name MyShell -StartupScript 'MyInitScript.ps1'
Then you can set the shellUri parameter of WSManConnectionInfo to http://schemas.microsoft.com/powershell/MyShell
The runspace you create this way will have the commands available which are imported by the MyInitScript.ps1 startup script.
So now this code will work:
string shell = "http://schemas.microsoft.com/powershell/MyShell";
var target = new Uri("https://myserver:port/wsman");
var secured = new SecureString();
foreach (char letter in "password")
{
secured.AppendChar(letter);
}
secured.MakeReadOnly();
var credential = new PSCredential("username", secured);
var connectionInfo = new WSManConnectionInfo(target, shell, credential);
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-NAVServerInstance");
var output = ps.Invoke();
}
}
Try invoking AddScript like so:
.AddScript("...", false)
this will execute the command in the global scope instead of a new local scope.
I think the proper way to do this is to use the RunspaceConfiguration class. It has an AddPSSnapin method.
I have this PowerShell command to add members to a Distributon Group in Exchange Online. This works correctly in PS. But I need to do this from a C# app.
$arr | foreach-object{Add-DistributionGroupMember -Identity 'Test' -Member $_}
I need to pass an array containing the members to the $arr in PS and pipe it to the foreach-object. I have done a lot of searching but all the examples show only how to do single a command. How do the PS command in C#?
Code snippet:
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
string[] members = new string[] { "testuser1#somecompany.com",
"testuser2#somecompany.com",
};
ps.Commands.AddParameter("Members").AddArgument(members); // ??
ps.Commands.AddCommand("foreach-object"); // I'm stuck at this point
Collection<PSObject> results = ps.Invoke();
}
}
Note:I can't do this via AddScript as remote scripts are blocked in our PS. Also doing something like below doesn't work as the runspace executes only the first pipeline.
foreach(string mem in members)
{
Command command = new Command("Add-DistributionGroupMember");
command.Parameters.Add("Identity", "test");
command.Parameters.Add("Member", member);
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(command);
pipeline.Invoke();
}
Yeah the documentation has been lacking. I whipped up the below. Clearly not the best, but seemed to work on get-process ( I cannot try out with Add-DistributionGroupMember). You can probably improve upon it:
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
var members = new[]
{
"testuser1#somecompany.com",
"testuser2#somecompany.com",
};
var command1 = new Command("write-output");
command1.Parameters.Add("InputObject", members);
ps.Commands.AddCommand(command1);
foreach (PSObject output in ps.Invoke())
{
ps.Commands.Clear();
var command2 = new Command("Add-DistributionGroupMember");
ps.Commands.AddCommand(command2);
ps.Commands.AddParameter("Name", output);
ps.Commands.AddParameter("Identity", "test");
foreach (PSObject output2 in ps.Invoke())
{
Console.WriteLine(output2);
}
}
}
To pass members to Add-DistributionGroupMember just include them in the Invoke() call:
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
string[] members = new string[] { "testuser1#somecompany.com", "testuser2#somecompany.com" };
Collection<PSObject> results = ps
.AddCommand("Add-DistributionGroupMember")
.AddParameter("Identity", "test")
.Invoke(members);
}
}
Note that in PowerShell you don't need to do a ForEach-Object, you can just pass in the whole array:
$arr | Add-DistributionGroupMember -Identity "test"
In both cases if you have a bad user, the good ones will still be added and an error will show for each of the bad ones.
I have the following code that I have tested and works:
using (new Impersonator("Administrator", "dev.dev", #########"))
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
Pipeline pipeline = runspace.CreatePipeline();
Command myCmd = new Command(#"C:\test.ps1");
myCmd.Parameters.Add(new CommandParameter("upn", upn));
myCmd.Parameters.Add(new CommandParameter("sipAddress", sipAddress));
pipeline.Commands.Add(myCmd);
// Execute PowerShell script
Collection<PSObject> results = pipeline.Invoke();
}
However, when I try to include the function in a different project so that it is called from a webservice it throws an execption:
System.Management.Automation.CmdletInvocationException: Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' is denied. ---> System.UnauthorizedAccessException: Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' is denied.
I have no idea why this is happening. Any help would be appreciated.
What's happening is that Impersonator only impersonates on the thread, and PowerShell's Runspace is running on another thread.
To make this work, you need to add:
runspace.ApartmentState = System.Threading.ApartmentState.STA;
runspace.ThreadOptions = System.Management.Automation.Runspaces.PSThreadOptions.UseCurrentThread;
just before you open the runspace.
This will force the runspace to run on the same thread as the impersonated token.
Hope this helps,
Use these namespaces :
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;
Create Runspace with InitialSessionState
InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ApartmentState = ApartmentState.STA;
initialSessionState.ThreadOptions = PSThreadOptions.UseCurrentThread;
using ( Runspace runspace = RunspaceFactory.CreateRunspace ( initialSessionState ) )
{
runspace.Open();
// scripts invocation
runspace.Close();
}