Here is my scenario : I have add-data and add-bulkdata cmdlets, both are written in C# deriving from pscmdlet, add-bulkdata takes a csv file and each line is fed to add-data cmdlet. Add-data cmdlet might throw terminating exceptions, if it does I dont know how to receive it in the add-bulkdata cmdlet, in bulkdata cmdlet I get a commandinvocationexception but it does not have the ErrorRecord that the underlying add-data had set. Also if I query pipeline.errors it gives me no information.
What is the best way to handle such scenario?
My Add-Bulkdata ProcessRecord() function looks something like this :
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new[] { #"C:\mybinary.dll" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(cmd); //cmd is add-data cmdlet
pipeline.Commands.Add("out-string"); // I have tried with and without this
Collection<PSObject> results = pipeline.Invoke();
Collection<object> errors = pipeline.Error.ReadToEnd();
Related
I have below PowerShell cmnd which runs fine in PowerShell and I am also able to call and execute it from a unit test in C# as described below. But my problem is in PowerShell window I write $rec.Categories.Count and then I get result. How can I write this in PowerShell script which I call in C# and get the value in there to test?
In PowerShell window:
$inv = Get-Inventory -Project 'TestDemo'
$inv.Categories.Count
4
Same script I have in C# and I am calling it by below method and it runs successfully but I don't know the way to get "4" in my result variable.
[Fact]
public void VerifyGetInventoriesOfaProject()
{
string path = Path.Combine( Root, #"TestData\GetInventory.ps1" );
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript( path );
var results = pipeline.Invoke();
runspace.Close();
// Assert.Equal( 5, results );
}
If you just search c# get output from powershell the first result is a stackoverflow post about this with a pretty clear and easy answer ...
Get Powershell command's output when invoked through code
I would like to execute and capture the output of a very simple powershell script.
Its a "Hello World" script and it looks like this. I used this post for reference
filename:C:\scripts\test.ps1
Write-Host "Hello, World"
Now I would like to execute that script using C# so I am doing this
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddScript(filename);
Collection<PSObject> results = ps.Invoke();
Now when I run this code, I get nothing in results. Any suggestions on how I can resolve this issue?
I get nothing in results
The primary reason you are not getting anything in results is because you are writing out to the host using Write-Host, this is wrong.
Write-Host "Hello, World"
Instead you need to Write-Output
Write-Output "Hello, World"
You can also do (if it's the last item in the pipeline):
"Hello, World"
On another note, your code can be reduced to:
PowerShell ps = PowerShell.Create();
ps.Commands.AddScript("scriptPath");
Collection<PSObject> results = ps.Invoke();
You don't need to create a Runspace either if you are not really dealing with parallel tasks...
I have tried executing RemoteDesktop commandlets in powershell using C#.
The following is my program :
static void Main(string[] args)
{
using (var powershell = PowerShell.Create())
{
powershell.Commands.AddCommand("Import-Module").AddArgument("remotedesktop");
powershell.Invoke();
powershell.Commands.Clear();
powershell.AddCommand(#"Get-RDRemoteApp");
powershell.AddCommand(#"out-string");
foreach (var result in powershell.Invoke())
{
Console.WriteLine(result);
}
}
}
When I invoke command it gives the error System.Management.Automation.CommandNotFoundException: The term 'Get-RDRemoteApp' is not recognized as the name of a cmdlet, function, script file, or operable program.
How can I achieve calling RemoteDesktop commandlets ?
I solved this. I change run mode to x64 from Any CPU.
You need to set a persistent runspace for all of your commands. The way your code is now, each command is being executed in it's own isolated runspace. Adding the following code:
var runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
powershell.Runspace = runspace;
to the beginning of the using block should fix your problem.
I faced the same issue and found your post.
Default InitialSessionState only loads Core commandlets and ExecutionPolicy is set to the most restrictive: Microsoft.PowerShell.ExecutionPolicy.Default.
To work around this, I had to set the ExecutionPolicy as shown below :
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Bypass;
iss.ImportPSModule("RemoteDesktop");
PowerShell powershell = PowerShell.Create(iss);
...
I am trying to use MDT from c# like I can already do with powershell when importing MicrosoftDeploymentToolkit.psd1. For example I can run the command Get-MDTPersistentDrive directly from powershell without problem.
But I can't find a way to do the same thing from c#, I tried to include directly the Microsoft.BDD.PSSnapIn.dll (which was basically what was doing "MicrosoftDeploymentToolkit.psd1") then I could access to the GetPersistent class but an error message informed me that I cannot directly invoke a PSCMDlet.
I then tried to use the PowerShell class
var ps = PowerShell.Create();
ps.AddScript(#"import-module C:\...\MicrosoftDeploymentToolkit.psd1");
ps.Invoke();
ps.AddCommand("Get-MDTPersistentDrive");
var result = ps.Invoke();
But I receive this exception
The term 'Get-MDTPersistentDrive' is not recognized as the name of a
cmdlet, function, script file, or operable program
I then tried to do this
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { #"C:\...\MicrosoftDeploymentToolkit.psd1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddCommand("Get-MDTPersistentDrive");
var result= ps.Invoke();
and I receive the error
Object reference not set to an instance of an object
I'm really lost, I don't get what they mean with this error, If you could show me where I'm wrong, or a way to execute PSCmdlet from c# or even better directly how to control MDT that would be awesome.
Invoke-MyFunction is a commandlet I wrote that takes an input file, changes it, and creates a new output file at a specified location. If I open a PowerShell on my desktop, import MyCommandlet.ps1, and run
Invoke-MyFunction -InputPath path\to\input -OutputPath path\to\output
everything works as expected. But when I try to import and invoke the command from a C# program with the code below, the commandlet doesn't run, doesn't log output, and doesn't produce the output file. It doesn't throw a CommandNotFoundException, so I assume the PowerShell object recognizes my commandlet. But I can't figure out why it doesn't execute it.
//set up the PowerShell object
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { #"C:\path\to\MyCommandlet.ps1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
//have MyFunction take input and create output
ps.AddCommand("Invoke-MyFunction");
ps.AddParameter("OutputPath", #"C:\path\to\output");
ps.AddParameter("InputPath", #"C:\path\to\input");
Collection<PSObject> output = ps.Invoke();
Further, after invoking MyFunction, the PowerShell object ps fails to execute any other commands. Even known ones.
This works for me:
//set up the PowerShell object
var initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { #"C:\Users\Keith\MyModule.ps1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
//have MyFunction take input and create output
ps.AddCommand("Invoke-MyFunction");
ps.AddParameter("OutputPath", #"C:\path\to\output");
ps.AddParameter("InputPath", #"C:\path\to\input");
var output = ps.Invoke();
foreach (var item in output)
{
Console.WriteLine(item);
}
With a MyModule.ps1 of:
function Invoke-MyFunction($InputPath, $OutputPath) {
"InputPath is '$InputPath', OutputPath is '$OutputPath'"
}
One thing that did cause me a failure is that on Visual Studio 2013 (maybe 2012 as well) AnyCPU apps will actually run 32-bit on a 64-bit OS. You have to have set the execution policy for PowerShell x86 to allow script execution. Try opening up a PowerShell x86 shell in admin mode and run Get-ExecutionPolicy. If it is set to Restricted, use Set-ExecutionPolicy RemoteSigned to allows scripts to execute.