Powershell Pipe and Foreach-Object in c# - c#

I am invoking a get-msoluser cmdlet of office365 and i use the following cmdlet in powershell
Get-MsolUser -UserPrincipalName user#organization.onmicrosoft.com | ForEach-Object{ $_.licenses}
The output is a collection of licenses and i wanted the same script to be run in c#. so i have written the code as follows
private void displayLicenses(){
Command cmd = new Command("Get-MsolUser");
cmd.Parameters.Add("UserPrincipalName","user#organization.onmicrosoft.com");
Command cmd2 = new Command("ForEach-Object");
cmd2.Parameters.Add("$_.licenses.AccountSku");
Pipeline pipe = Office365Runspace.CreatePipeline();
pipe.Commands.Add(cmd);
pipe.Commands.Add(cmd2);
Console.WriteLine("Before invoking the pipe");
ICollection<PSObject> result = pipe.Invoke();
CheckForErrors(pipe);
Console.WriteLine("Executed command {0} + {1} with no error", cmd.CommandText, cmd2.CommandText);
foreach(PSObject obj in result){
foreach(PSPropertyInfo propInfo in obj.Properties){
Console.WriteLine(propInfo.Name+": "+propInfo.Value+" "+propInfo.MemberType);
}
}
}
But i still get an error on executing this function saying
Unhandled Exception:
System.Management.Automation.CommandNotFoundException: The term
'ForEach-Object' is not recognized as the name of a cmdlet, function,
scrip t file, or operable program. Check the spelling of the name, or
if a path was in cluded, verify that the path is correct and try
again.
I checked that my project has a reference to System.management.Automation.dll file that contains the ForEach-Object cmdlet.
I found the dll using this cmd in powershell
(Get-Command ForEach-Object).dll
Thanks,
Satya

I figured out the problem causing for the issue. It is due to the misconfigured runspace i created.
InitialSessionState initalState = InitialSessionState.Create();
initalState.ImportPSModule(new String[] { "msonline" });
//initalState.LanguageMode = PSLanguageMode.FullLanguage;
Office365Runspace = RunspaceFactory.CreateRunspace(initalState);
Office365Runspace.Open();
i was creating the initalstate with empty one,When i changed it to default one it worked fine.On creating the default one it includes all the modules that were obtained by default.
InitialSessionState initalState = InitialSessionState.CreateDefault();
it worked fine.
Thanks,
Satya

It sounds like your're trying to run that in the remote session at the Exchange server. Those are NoLanguage constrained sessions, meaning that you can only run the Exchange cmdlets in those sessions. If you want to use PowerShell core language cmdlets (like foreach-object), you have to do that in a local session and either use Import-PSSession to import the Exchange functions into your local session (implicit remoting) , or use Invoke-Command and point it at the remote session on the Exchange server.

Related

How to get output of PowerShell's Get-WmiObject in C#

I need to get which network interface is connected to which network. I found that this information is accessible in MSFT_NetConnectionProfile. Unfortunatelly I cannot access it directly from C# (I get ManagementException: Provider load failure on computer where it should run) but when I access it from PowerShell, it works. Then my idea is to run PowerShell command from C# but I cannot get the result.
using System.Management.Automation;
string command = "Get-WmiObject -Namespace root/StandardCimv2 -Class MSFT_NetConnectionProfile | Select-Object -Property InterfaceAlias, Name";
PowerShell psinstance = PowerShell.Create();
psinstance.Commands.AddScript(command);
var results = psinstance.Invoke();
foreach (var psObject in results)
{
/* Get name and interfaceAlias */
}
The code runs without errors but results are empty. I tried even adding Out-File -FilePath <path-to-file> with relative and absolute file path but no file was created. I even tried old >> <path-to-file> but without luck. When I added Out-String then there was one result but it was empty string.
When I tested the commands directly in PowerShell then it worked. Is there a way how to get it in C#?
The PS commands must be constructed in a builder-pattern fashion.
Additionally, in PS Core the Get-WmiObject has been replaced by the Get-CimInstance CmdLet.
The following snippet is working on my env:
var result = PowerShell.Create()
.AddCommand("Get-CimInstance")
.AddParameter("Namespace", "root/StandardCimv2")
.AddParameter("Class", "MSFT_NetConnectionProfile")
.Invoke();

The term 'New-CsOnlineSession' is not recognized as the name of a cmdlet

I am trying to run a power shell script from the c#.
When running the power shell script only, it runs successfully. But , while trying to run the same script from the c# . I get the error "The term 'New-CsOnlineSession' is not recognized as the name of a cmdlet"
Here is the code:
public static void GetLyncUsers(string userName, string password)
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
var script = string.Format("$Username =\"{0}\"\n" +
"$Password =\"{1}\"\n" +
"$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force\n" +
"$cred = new-Object System.Management.Automation.PSCredential ($Username , $secpasswd)\n" +
"$CSSession = New-CsOnlineSession -Credential $cred\n" +
"Import-PSSession $CSSession -AllowClobber\n" +
"Get-CsOnlineUser", userName, password);
// use "AddScript" to add the contents of a script file to the end of the execution pipeline.
// use "AddCommand" to add individual commands/cmdlets to the end of the execution pipeline.
powerShellInstance.AddScript(script);
// use "AddParameter" to add a single parameter to the last command/script on the pipeline.
// invoke execution on the pipeline (collecting output)
Collection<PSObject> psOutput = powerShellInstance.Invoke();
// check the other output streams (for example, the error stream)
if (powerShellInstance.Streams.Error.Count > 0)
{
// I am getting this error
//The term 'New-CsOnlineSession' is not recognized as the name of a cmdlet
}
}
Is there anything i am missing? I am new to powershell in general.
Solution:
using (PowerShell powerShellInstance = PowerShell.Create())
{
// Import-Module lynconlineconnector
powershellInstance.Commands
.AddCommand("Import-Module")
.AddArgument("lynconlineconnector");
// rest of your code ....
Why?
When running an interactive session in powershell v3 and higher, the host traps CommandNotFound, and searches every module in all the known locations. If it finds the command, it automatically loads the module, and proceeds normally.
When running same script in C#, the CommandNotFound exception isn't trapped, and hence you get the error.
Related Question(s):
PowerShell - How to Import-Module in a Runspace
#PSTip Cmdlet Discovery and Module auto-loading
I ran into the same problem. You have to install the Lync/Skype For Business Online Connector as described on Technet
The setup program copies the Skype for Business Online Connector
module (and the New-CsOnlineSession cmdlet) to your local computer.

Why there is no way to pass InitialSessionState for a remote RunspacePool?

Why the following method does not have an overload which accepts a RunspaceConnectionInfo (to specify the remote server info) as well as a InitialSessionState?
http://msdn.microsoft.com/en-us/library/system.management.automation.runspaces.runspacefactory.createrunspacepool(v=vs.85).aspx
Full Context:
I am building a RunspacePoolCache, that would cache remote RunspacePools created using RunspaceFactory. Cache is keyed on remote server info. Till the pool's RunspacePoolStateInfo.State is Open, the same RunspacePool would be used for executing Powershell scripts on same remote server. (Shameless plug: Will this work?)
Now, I want to add a set of Powershell snap-ins that are common to the created RunspacePool. Adding the snap-ins within the executed script is at times leading to the following exception:
An item with the same key has already been added
This is happening even when I do the following in the Powershell script (but less often):
if ((Get-PSSnapin | ? { $_.Name -eq 'VeeamPSSnapIn' }) -eq $null) {
Add-PsSnapin -Name VeeamPSSnapIn -ErrorAction SilentlyContinue
}
That is where I am trying to load the snap-ins via InitialSessionState. But from the set of methods provided, it seems InitialSessionState can be specified only while creating local RunspacePools.
To add snap-ins to the ISS you use the ImportPSSnapIn method. Example:
$iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
s.ImportPSSnapIn($snapName,[ref]'') | Out-Null
The you create the runspace pool like this:
$runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool($minRunspaces, $maxRunspaces, $iss, $Host)

Executing cmdlet Get-ClusterGroup from C#

Hi I'm trying to execute the Get-ClusterGroup cmdlet from C# 4.0. I've used the following code
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new string[] { "failoverclusters"});
Runspace myRunSpace = RunspaceFactory.CreateRunspace(iss);
myRunSpace.Open();
Pipeline pipeLine = myRunSpace.CreatePipeline();
Command myCommand = new Command("Get-ClusterGroup");
pipeLine.Commands.Add(myCommand);
Console.WriteLine("Invoking Command");
Collection commandResult = pipeLine.Invoke();
foreach (PSObject resultObject in commandResult)
{
Console.WriteLine(resultObject.ToString());
}
myRunSpace.Close();
But getting the following error
The term 'Get-ClusterGroup' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.
It will be great if someone can show me the where I'm missing the logic or where is the problem in my code
Get-ClusterGroup is a Powershell Commandlet, not an .exe file. You can invoke Powershell commands from .NET using the System.Management.Automation.PowerShell class, as described on MSDN here: http://msdn.microsoft.com/en-us/library/system.management.automation.powershell(v=vs.85).aspx

Powershell SQLPS module not importing within C#

I'm attempting to execute a SQL Query from within Powershell, within C#. I have been successful in doing so with ActiveDirectory cmdlets and wanted to take it one step further.
My first issue is while the following format works with ActiveDirectory (and in the ISE) it fails in C#:
using (PowerShell pS = PowerShell.Create())
{
pS.AddCommand("import-module");
pS.AddArgument("sqlps");
pS.Invoke();
}
I've long since had the security set to Unrestricted, but the error I'm getting is:
CmdletInvocationException was unhandled
File C:\Program Files (x86)\Microsoft SQL Server\110\Tools\PowerShell\Modules\sqlps\Sqlps.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at http://go.microsoft.com/fwlink/?LinkID=135170.
However, if I run like this I get no error, though a later "Get-Module -all" call shows no sign of the module:
using (PowerShell pS = PowerShell.Create())
{
pS.AddScript("Import-Module sqlps");
pS.Invoke();
}
If I then try importing the ActiveDirectory module and calling Get-Module, it shows nothing.
What's going on here?
I'm not that great with C sharp but when calling scripts from outside of powershell there is a flag when executing the program to bypass the execution policy, i.e.
powershell.exe -executionpolicy bypass -command "& '\\somepath\somescript.ps1' "
This allows remote scripts to be called, as even with unrestricted set I still found that it wanted to prompt for the execution of some scripts so for instance in the task scheduler it would simply fail to run.
Also when importing SQLPS I've also found it's useful to add the -DisableNameChecking flag, you may also want to push your location beforehand and pop it afterwards otherwise you will end up in the SQLPS PSdrive with no access to local locations if you need it.
Did you try something like this?
PowerShell ps = PowerShell.Create();
ps.AddScript("set-executionpolicy unrestricted -scope process");
ps.AddScript("import-module sqlps");
ps.AddScript("get-module sqlps");
var m = ps.Invoke();
foreach (var mm in m.Select(x => x.BaseObject as PSModuleInfo))
Console.WriteLine(new { mm.Name, mm.Version });
I had a similar issue with the sqlServer ps module. Looks like when executing from C# you need to load the modules manually into the runspace in order for this to work.
string scriptText = File.ReadAllText("yourScript.ps1");
//This is needed to use Invoke-sqlcommand in powershell. The module needs to be loaded into the runspace before executing the powershell.
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { #"SqlServer\SqlServer.psd1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
using (PowerShell psInstance = PowerShell.Create())
{
psInstance.Runspace = runspace;
psInstance.AddScript(scriptText);
var PSOutput = psInstance.Invoke();
}
Also add all the references located in the SqlServer.psd1. This file is usually found in "C:\Program Files\WindowsPowerShell\Modules\SqlServer". I added to folder to my solution to be able to execute on remote servers.
You need to add Microsoft.SqlServer.BatchParser.dll reference in order to execute invoke-sqlcommand from the Powershell.
You should be able to do the same for sqlps module. Rather use SqlServer as it is newer.

Categories