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...
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
Below is the code that I currently have to run my Powershell script in my C# program.
using (PowerShell PowerShellInstance = PowerShell.Create())
{
string script = "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted; Get-ExecutionPolicy"; // the second command to know the ExecutionPolicy level
PowerShellInstance.AddScript(script);
var someResult = PowerShellInstance.Invoke();
someResult.ToList().ForEach(c => Console.WriteLine(c.ToString()));
//Console.ReadLine();
}
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
Command myCommand = new Command ("C:\\automate\\dandf\\onboarding\\Yardi_Automation\\Yardi_Automation\\Onboarding_Master\\O365_Add_User.ps1");
pipeline.Commands.Add(myCommand);
pipeline.Invoke();
runspace.Close();
Console.WriteLine("Powershell script executed..");
}
}
The script once ran will add an O365 license to any user that is put through the system. Every time I run the code it will stop at "pipeline.Invoke();) for 1 second and will move on without running the script. Any help is greatly appreciated.
Runspace Error
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.
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();