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.
Related
When I run powershell from my MVC application, i get no results from the Get-ChildItem –Path IIS:\AppPools command.
If i run it directly in powershell it works fine. I have set the site to run on administrator account, thinking that maybe it cant "see" the pools, but no luck.
This is just a test too, accessing the pools, as what i was origionally struggling with was turning off an application pool, as it "could not be found". with Get-WebAppPoolState -name $service
public static string test()
{
using (var powershell = PowerShell.Create())
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
using (RunspaceInvoke invoker = new RunspaceInvoke())
{
invoker.Invoke("Set-ExecutionPolicy Unrestricted");
invoker.Invoke("Import-Module WebAdministration");
runspace.Open();
powershell.Runspace = runspace;
powershell.AddScript(#"Import-Module WebAdministration
Get-ChildItem –Path IIS:\AppPools");
var results = powershell.Invoke();
var result = "";
result += ResultsToString(results); // results is a blank list
result += String.Join(",", powershell.Streams.Error.ToList().Select(x => x.Exception.Message));
return result;
}
}
}
}
Any ideas where the issue lies? code, permissions?
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 some powershell scripts that I want to call from C#.
From the commandline I would do this:
Import-Module c:\provisioning\newModule.psm1 –Force
Get-MTMailbox -customerID custid0001
This works and gives the correct result.
I want to call my scripts from C#:
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { "C:\\provisioning\\newModule.psm1" });
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddCommand("Get-MTMailbox").AddArgument("-customerID custid0001");
// Also tried this. Still ignored.
//ps.AddParameter("-customerID", "custid0001");
ps.Invoke();
Collection<PSObject> results = ps.Invoke();
foreach (PSObject psObject in results)
{
Console.Write("Result: "+psObject.ToString());
if (psObject.BaseObject is System.Collections.Hashtable)
{
Hashtable ht = (Hashtable) psObject.BaseObject;
foreach (DictionaryEntry keypair in ht)
{
Console.WriteLine(keypair.Key+" "+keypair.Value);
}
}
}
The command runs, but my arguments are always ignored.
I'm very new to C# so maybe I don't know what to search for.
What's the correct way to pass arguments to a cmdlet in C#?
try with this:
ps.AddParameter("customerID", "custid0001");
You do not need the - to specify the argument name
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'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.