Output of a runspace factory in c# - c#

I run this c# code in a asp.net application. I use c# to execute a powershell file. The powershell file search for a mailbox and delete a particular email.
The execution works, but when I try to get the output, I get a System.NullReferenceException'.
The problem is this line :
weboutput.InnerHtml += result.Members["Success"].ToString();
I tried various things to get some values :
pipeline.Commands.Add("Out-String");
I added/removed : |Out-String in the powershell file.
I tried with result.properties, then I tried to cast the object and it outputed like this :
weboutput.InnerHtml += result.ToString();
It gives me the following :
Microsoft.Exchange.InfoWorker.Common.Search.SearchMailboxResult
So here is the c# code :
InitialSessionState initialSession = InitialSessionState.CreateDefault();
initialSession.ImportPSModule(new[] { "MSOnline" });
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
Command connectCommand = new Command(#"C:\DeleteMail.ps1");
using (Runspace psRunSpace = RunspaceFactory.CreateRunspace(initialSession))
{
// Open runspace.
psRunSpace.Open();
//Iterate through each command and executes it.
foreach (var com in new Command[] { connectCommand })
{
var pipe = psRunSpace.CreatePipeline();
pipe.Commands.Add(com);
// Execute command and generate results and errors (if any).
//Collection<PSObject> results = pipe.Invoke();
foreach (PSObject result in pipe.Invoke()) {
weboutput.InnerHtml += result.Members["Success"].ToString();
}
var error = pipe.Error.ReadToEnd();
}
// Close the runspace.
psRunSpace.Close();
}
}
Here is the powershell file.
Import-Module MsOnline
$AdminUsername = "admin#domain.com"
$AdminPassword = "SuperSecret!111"
$SecurePassword = ConvertTo-SecureString $AdminPassword -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $AdminUsername,$SecurePassword
Connect-MSOLService -Credential $cred
$exchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $cred -Authentication "Basic" -AllowRedirection
Import-PSSession $exchangeSession –DisableNameChecking
Get-Mailbox -ResultSize unlimited -Filter {Name -eq "bigboss"} | Search-Mailbox -SearchQuery "me#domain.com Subject:'test'" -DeleteContent -Force | Out-String
Finally here is the output when I run the powershell file alone.
RunspaceId : random #
Identity : bigboss
TargetMailbox :
Success : True
TargetFolder :
ResultItemsCount : 1
ResultItemsSize : 124 KB (124 kilobytes)
How can I get the output of the file and/or how can I parse the values of the PSObject ?

Related

The term 'Get-SmbOpenFile' is not recognized when running script from C# code

I have a PowerShell script that closes opened shared files on remote server
$User = "user"
$PWord = ConvertTo-SecureString -String "pas" -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
$cim = New-CimSession -ComputerName epicorlive -Credential $Credential
Get-SmbOpenFile -CimSession $cim | Where-Object Path -like "*dll\Form.dll" | Close-SmbOpenFile -Force
Running this script from native PowerShell window works fine.
but when i try to run from C# code there is an error:
The term 'Get-SmbOpenFile' 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
string scriptText = //"\r\n" +
"$User = \"user\"\r\n" +
"$PWord = ConvertTo-SecureString -String \"pas\" -AsPlainText -Force\r\n" +
"$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord\r\n" +
"$cim = New-CimSession -ComputerName epicorlive -Credential $Credential\r\n" +
"Get-SmbOpenFile -CimSession $cim | Where-Object Path -like \"*dll\\" + file + "\" | Close-SmbOpenFile -Force";
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
Why is this command not recognized running from C#?
How can i fix it?
If there is some other options to close opened shared files on remote computer from C# code, I will be glad to hear about them :)

Powershell return different results running the same script from c#

I am trying to run a simple script for getting file attributes.
string script = #"$path = 'C:\Temp\Indexing\Asm1.asm'
$shell = New-Object -COMObject Shell.Application
$folder = Split-Path $path
$file = Split-Path $path -Leaf
$shellfolder = $shell.Namespace($folder)
$shellfile = $shellfolder.ParseName($file)
0..500 | Foreach-Object { '{0} = {1}' -f $shellfolder.GetDetailsOf($null, $_), $shellfolder.GetDetailsOf($shellfile, $_).toString()}";
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(script);
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
When running within powershell I get more attribute results which are empty when running it through the code.
It seems like an access issue.
any help will be much appreciated!
Try adding #!/usr/bin/env pwsh as first line of the script.

powershell cmdlet C#?

I'm tired to search about way to use powershell in C#, this first time to use Powershell and I don't know how to add it in C#, i have my codes working in Powershell any help to add in C#?
New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts"
New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList"
New-ItemProperty -path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" -name BigBear -value "0" -propertyType DWord
(BigBear) it's name and I want to change it with textbox
i tried this
private void Shell()
{
using (var runspace = RunspaceFactory.CreateRunspace())
{
// using (var powerShell = PowerShell.Create())
// {
// powerShell.Runspace = runspace;
// powerShell.AddScript(#"Hidden.ps1");
// //powerShell.AddParameter("UserName", UserName.Text);
// powerShell.Invoke();
// }
using (var powerShell = PowerShell.Create())
{
powerShell.Runspace = runspace;
powerShell.AddCommand("New-Item -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\"");
powerShell.AddCommand("New-Item -Path \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList\"");
powerShell.AddCommand("New-ItemProperty -path \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList\" -name " + UserName.Text + " -value \"0\" -propertyType DWord");
//powerShell.AddParameter("ParamA", varA);
var results = powerShell.Invoke();
// Do whatever with results
}
}
What have you tried?
Here are some links that describes how it can be done. But your question is not very detailed on your requirements... The last one have code you can download and try/modify!
https://blogs.msdn.microsoft.com/kebab/2014/04/28/executing-powershell-scripts-from-c/
http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C

Execute multiple line PowerShell Script from C#

I am new to PowerShell. I am trying to execute PowerShell script from C#. PS Script that I have written transfer xml file from host computer (running PS script) to a remote computer. Script is as follows
$Username = "User"
$Password = "Pass"
$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force"
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass"
$contents = [IO.File]::ReadAllBytes("D:\MyFile.xml")
Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes("D:\MyFile.xml",$using:contents)}
I store username and password of remote computer, to which I want to transfer my file, in variables. Convert password to secure string and create a credential object containing username and password. Use credential to copy the contents of file from my computer to remote computer.
This code works fine if I execute these commands one by one from my powershell or by running these lines from a .ps1 powershell script from my computer.
I used following code in C# to execute above script and transfer the file.
Runspace runSpace = RunspaceFactory.CreateRunspace();
Pipeline pipeline = runSpace.CreatePipeline();
runSpace.Open();
string script = "";
script = "$Username = \"User\"\n";
script = script + "$Password = \"Pass\"\n";
script = script + "$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force\n";
script = script + "$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass\n";
script = script + "$contents = [IO.File]::ReadAllBytes(\"D:\\MyFile.xml\")\n";
script = script + "Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes(\"D:\\MyFile.xml\",$using:contents)}\n";
// pipeline.Runspace.SessionStateProxy.SetVariable
pipeline.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();
However, it did not transfer the file. So, I thought I need to Add each line in script one by one to the pipeline and then execute it. So I changed it to following.
string[] commands = script.Split('\n');
foreach (string command in commands)
{
pipeline.AddScript(command);
}
Collection<PSObject> results = pipeline.Invoke();
It did not work either. I executed single Invoke-Command from C# code on my computer and it worked fine. Also, if I try to execute other single PS commands they work. So I assessed that setting variable values like this $User = "user" is not working through C#.
I used SessionStateSetProcy.SetVariable to Store variable values in powershell session as follows.
pipeline.Runspace.SessionStateProxy.SetVariable("User", "User");
pipeline.Runspace.SessionStateProxy.SetVariable("Password", "Pass");
var value = pipeline.Runspace.SessionStateProxy.GetVariable("Password");
Console.WriteLine(value);
So, I was able to set value and retrieve as well. But, since I was using Other cmdlets like ConvertTo-SecureString, I realized I need to execute these cmdlets separately in my code.
I finally I came up with following piece of code.
pipeline.Runspace.SessionStateProxy.SetVariable("User", "User");
pipeline.Runspace.SessionStateProxy.SetVariable("Password", "Pass");
Command command = new Command("ConvertTo-SecureString");
string pass_value = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("Password").ToString();
command.Parameters.Add("AsPlainText",pass_value);
command.Parameters.Add("Force");
pipeline.Runspace.SessionStateProxy.SetVariable("SecurePass",command);
/*Build a command2*/
Command command1 = new Command("New-Object");
string user_name = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("User").ToString();
command1.Parameters.Add("TypeName", "System.Management.Automation.PSCredential");
string args = user_name + " , "+pass_value;
command1.Parameters.Add("-ArgumentList", args);
pipeline.Runspace.SessionStateProxy.SetVariable("Cred", command1);
byte[] wifiXML = System.IO.File.ReadAllBytes(#"D:\MyFile.xml");
pipeline.Runspace.SessionStateProxy.SetVariable("contents", wifiXML);
Object a = pipeline.Runspace.SessionStateProxy.PSVariable.GetValue("contents");
string script = "Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes(\"D:\MyFile.xml\",$using:contents)}";
pipeline.Commands.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();
It gave some weird error in System.Management.Automation. I know in SetVariable I am doing it wrong by passing command variable.
I have tried PowerShell ps = PowerShell.Create() instead of pipeline as well and tried ps.AddCommand and ps.AddParameters followed by ps.AddArgument as well. But I am not sure that once I run cmdlet ConvertTo-SecureString through ps.AddCommad, ps.AddArgument etc., how should I set it's output value to $Cred variable?
I am aware that solution to this won't be this complicated and someone must have executed multiple line powershell script through C#. All I want to accomplish is execute the .ps1 script lines mentioned in very start from C# code.I have tried bunch of methods including mentioned above to no use, so any help related to it would be much appreciated?
Try in this way
string script = #"
$Username = 'User'
$Password = 'Password'
$SecurePass = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass
$contents = [IO.File]::ReadAllBytes('D:\MyFile.xml')
Invoke-Command -ComputerName ComputerName -Credential $Cred {[IO.File]::WriteAllBytes('D:\\MyFile.xml',$using:contents)}
";
pipeline.Commands.AddScript(script);
Collection<PSObject> results = pipeline.Invoke();

How to send a string parameter from c# to powershell?

I'm writing 2 apps one with c# and the other with powershell 1.0, in some point of my code I want to pass a string that indicating the server name from my c# app to a powershell script file that I wrote, how do I send it? and how do i accept it?
my code :
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
String scriptfile = #"c:\test.ps1";
Command myCommand = new Command(scriptfile, false);
CommandParameter testParam = new CommandParameter("username", "serverName");
myCommand.Parameters.Add(testParam);
pipeline.Commands.Add(myCommand);
Collection<PSObject> psObjects;
psObjects = pipeline.Invoke();
runspace.Close();
and my powershell script
param([string]$Username)
write-host $username
What am I missing? I'm kinda new with powershell.
I have machines with PowerShell 2.0 and 3.0 but not 1.0, so my results may differ. When I run your code on my PowerShell 3.0 box, I get:
A command that prompts the user failed because the host program or the
command type does not support user interaction. Try a host program
that supports user interaction, such as the Windows PowerShell Console
or Windows PowerShell ISE, and remove prompt-related commands from
command types that do not support user interaction, such as Windows
PowerShell workflows.
It didn't like the Write-Host, so I changed your script to
param([string]$Username)
Get-Date
Get-ChildItem -Path $userName
Get-Date so that I could see some output without depending on the parameter and GCI to use the parameter. I modified your code to look like this:
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
using (var runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
{
runspace.Open();
String scriptfile = #"..\..\..\test.ps1";
String path = #"C:\Users\Public\";
var pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(new Command("Set-ExecutionPolicy RemoteSigned -Scope Process", true));
pipeline.Invoke();
pipeline = runspace.CreatePipeline();
var myCommand = new Command(scriptfile, false);
var testParam = new CommandParameter("username", path);
myCommand.Parameters.Add(testParam);
pipeline.Commands.Add(myCommand);
var psObjects = pipeline.Invoke();
foreach (var obj in psObjects)
{
Console.WriteLine(obj.ToString());
}
runspace.Close();
}
Console.WriteLine("Press a key to continue...");
Console.ReadKey(true);
and it ran without error and displayed the folder contents, on both PoSh 2 and 3.
For info, if you're only setting the execution policy for the current process, you don't need to run elevated, hence I was able to do it in-code.

Categories