Unable to do waitforexit while invoking EXE - c#

I am running EXE using below code. EXE opens up properly and runs properly. I am facing two issues.
is there anything similar to Process.WaitforExit() while invoking PowerShell.Invoke.Once user completes operations on EXE and closes the same,then the remaining execution should continue.
The output of EXE is coming as System.Management.ManagementBaseObject. It should contain the executable result.
If I run the EXE using Process.Start, I can achieve both the above results. The output also coming up properly. Please help on this.
using (Runspace runSpace = RunspaceFactory.CreateRunspace())
{
string remoteScriptPath="e:\shared\test.ex";
string parameterString="p1";
runSpace.Open();
using (Pipeline pipeline = runSpace.CreatePipeline())
{
RunspaceInvoke invoke = new RunspaceInvoke();
PowerShell ps = PowerShell.Create();
ps.Runspace = runSpace;
ps.AddCommand("invoke-wmimethod");
ps.AddParameter("class", "Win32_Process");
ps.AddParameter("name", "Create");
if (string.IsNullOrEmpty(parameterString))
{
ps.AddParameter("argumentlist", remoteScriptPath);
}
else
{
ps.AddParameter("argumentlist", remoteScriptPath + " " + parameterString);
}
Collection<PSObject> psOutput = ps.Invoke();
if (ps.Streams.Error.Count == 0)
{
string result="";
foreach (PSObject psObject in psOutput)
{
if (psObject != null)
{
result += psObject.BaseObject.ToString();
result += Environment.NewLine;
}
}
return result;
}

Related

Capture the console logs of powershell script using c#

I am trying to execute powershell script and capture the formatted output from powershell into C# console window but always return null.
C# Code:
public List<SplunkEvent> events { get; set; } = new List<SplunkEvent>();
public void InvokeCrawl()
{
try
{
List<UrlTracker> urls = new List<UrlTracker>();
urls.Add(new UrlTracker() { AirId = "4812", SiteId = "6976843556", Url = "https://test.com/homepage", RequestorEnterpriseId = "asif.iqbal.khan" });
RunScript("4812", "asif", "iqbal", "pinku", "", urls);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private void RunScript(string airID, string requestorEnterpriseId, string areaLeadEnterpriseId, string mDEnterpriseId, string serviceLeadEnterpriseId, List<UrlTracker> urls)
{
string _path = AppDomain.CurrentDomain.BaseDirectory + "Script\\Test.ps1";
System.IO.StreamReader sr = new System.IO.StreamReader(_path);
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
Command myCommand = new Command(_path);
CommandParameter _airId = new CommandParameter("AirId", airID);
myCommand.Parameters.Add(_airId);
CommandParameter _url = new CommandParameter("SiteUrl", urls[0].Url);
myCommand.Parameters.Add(_url);
pipeline.Commands.Add(myCommand);
//pipeline.Commands.AddScript(sr.ReadToEnd());
pipeline.Commands.Add("Out-String");
var results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
Console.WriteLine(stringBuilder.ToString());
}
Test.ps1 Code:
Output from C#:
Executing the script directly inside windows powershell i could see the result getting printed.
In your Powershell script, use Write-Output instead of Write-Host
You can also remove this line from the C# code.
pipeline.Commands.Add("Out-String");
More info on the difference between the two here: PowerShell difference between Write-Host and Write-Output?

Exception Handling in C# for handling errors in the powershell script

I am trying to call a power shell script file from C# which automates some task.
I want to efficiently handle exception that may occur while the power shell script executes and capture it in c# code.
Below is the sample code I am currently using, but am not sure if it is correct.
Any suggestions on what needs to be changed.
I also tried pipeline.Error.Count but the value returned is 0 even if exception has occurred.
string cmdArg = "mypowerscript.ps1";
Runspace runspace = null;
Pipeline pipeline = null;
try
{
runspace = RunspaceFactory.CreateRunspace();
pipeline = runspace.CreatePipeline();
runspace.ApartmentState = System.Threading.ApartmentState.STA;
runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
runspace.Open();
pipeline.Commands.AddScript(cmdArg, true);
pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
Collection<PSObject> results = null;
results = pipeline.Invoke();
if (results.Count == 0)
MessageBox.Show(" Failed",MessageBoxButtons.OK, MessageBoxIcon.Error);
else
MessageBox.Show("Successful",MessageBoxButtons.OK, MessageBoxIcon.Information);
foreach (PSObject obj in results)
{
// Consume the results
Debug.WriteLine(obj);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
Cursor.Current = Cursors.Default;
MessageBox.Show("Failed", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
if (runspace != null)
runspace.Close();

Unable to run Disable-Mailbox Powershell in C#

I'm trying to reproduce the following working chunk of Powershell in C#.
We are connecting on an Exchange2010 instance.
$ExURI = "http://ExchangeUrl/PowerShell/"
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExURI -Authentication Kerberos
$userName = "patatem"
Import-PSSession $Session -AllowClobber -EA SilentlyContinue | Out-Null
Get-Recipient $userName
Disable-Mailbox -Identity $userName -Confirm:$False
#enable-mailbox -identity $userName -Alias $userName -database "AnExchangeDatabase"
remove-PSSession $Session
I've been following the steps referenced here : https://blogs.msdn.microsoft.com/wushuai/2016/09/18/access-exchange-online-by-powershell-in-c/
In the following block of code, I get positive results when I call Get-Mailbox, Get-Recipient.
When calling Disable-Mailbox, I get the following error
The term 'Disable-Mailbox' 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.
Why is it recognizing Get-Mailbox but not Disable-Mailbox?
(I've tried adding another chunk of code to do the Import session section of Powershell but that didn't change anything.)
public void EnableCommand(string identity, string database)
{
if (!string.IsNullOrWhiteSpace(identity))
{
using (var runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
var powershell = PowerShell.Create();
var command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", new Uri(Constants.DefaultOutLookUrl));
command.AddParameter("Authentication", "Kerberos");
powershell.Commands = command;
powershell.Runspace = runspace;
var result = powershell.Invoke();
if (powershell.Streams.Error.Count > 0 || result.Count != 1)
{
throw new Exception("Fail to establish the connection");
}
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Invoke-Command");
command.AddParameter("ScriptBlock", System.Management.Automation.ScriptBlock.Create("Get-Mailbox"));
command.AddParameter("Session", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
var mailBoxes = powershell.Invoke();
// This will give me a result
var returnValue = new StringBuilder();
foreach (var item in mailBoxes)
{
returnValue.AppendLine(item.ToString());
}
// check the other output streams (for example, the error stream)
if (powershell.Streams.Error.Count > 0)
{
returnValue.AppendLine($"{powershell.Streams.Error.Count} errors: ");
foreach (var err in powershell.Streams.Error)
{
returnValue.AppendLine($"{err.ToString()}");
}
}
// This will also work
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Invoke-Command");
command.AddParameter("ScriptBlock", System.Management.Automation.ScriptBlock.Create("Get-Recipient SomeEmail"));
command.AddParameter("Session", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
var mailBoxes2 = powershell.Invoke();
foreach (var item in mailBoxes2)
{
returnValue.AppendLine(item.ToString());
}
if (powershell.Streams.Error.Count > 0)
{
returnValue.AppendLine($"{powershell.Streams.Error.Count} errors: ");
foreach (var err in powershell.Streams.Error)
{
returnValue.AppendLine($"{err.ToString()}");
}
}
// this will give me The term 'Disable-Mailbox' 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.
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Invoke-Command");
command.AddParameter("ScriptBlock", System.Management.Automation.ScriptBlock.Create("Disable-Mailbox -Identity patatem -Confirm:$False"));
command.AddParameter("Session", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
var mailBoxes3 = powershell.Invoke();
foreach (var item in mailBoxes3)
{
returnValue.AppendLine(item.ToString());
}
// check the other output streams (for example, the error stream)
if (powershell.Streams.Error.Count > 0)
{
returnValue.AppendLine($"{powershell.Streams.Error.Count} errors: ");
foreach (var err in powershell.Streams.Error)
{
returnValue.AppendLine($"{err.ToString()}");
}
}
Console.WriteLine(returnValue);
}
}
The term 'Disable-Mailbox' 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.
If the user attempting to run the Disable-Mailbox-cmdlet has insufficient permissions to disable a mailbox (RBAC), this nonspecific error message is issued.
Run your code with sufficient permissions and it should work.
TechNet article: Understanding Role Based Access Control

Some of the PowerShell module (Cluster cmdlets) does not get loaded when I create RunSpace on a localhost without providing runspaceconnectioninfo

Please refer the sample below:
RunspaceSample(string.Empty); fails with The term 'Add-VMToCluster' is not recognized as the name of a cmdlet.
Whereas the RunspaceSample("localhost") or RunspaceSample("somecomputerName") succeed. Any pointer why is it ? Does including RunspaceConnectionInfo changes the powershell version used or RunspaceConfiguration used for execution ?
Also what would be the performance impact if I create Runspace with RunSpaceConnectionInfo with ComputerName = "localhost" even for executing powershell script on a local computer ?.
Thanks
public static void RunspaceSample(string computerName)
{
Runspace rs;
if (string.IsNullOrEmpty(computerName))
{
rs = RunspaceFactory.CreateRunspace();
}
else
{
var connectionInfo = new WSManConnectionInfo
{
OperationTimeout = 10000,
OpenTimeout = 10000,
ComputerName = computerName
};
rs = RunspaceFactory.CreateRunspace(connectionInfo);
}
rs.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
string script = #"$ComputerName = 'somevm'; $null = Add-VMToCluster -Name $ComputerName -VMName $ComputerName -ErrorAction SilentlyContinue -verbose";
ps.AddScript(script);
Console.WriteLine("Script: {0}", script);
Console.WriteLine("------------------------");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result.ToString());
}
if (ps.HadErrors)
{
var sb = new StringBuilder();
foreach (var errorRecord in ps.Streams.Error)
{
sb.AppendFormat("\nError: {0} CategoryInfo: {1}", errorRecord.Exception.Message, (errorRecord.CategoryInfo != null) ? errorRecord.CategoryInfo.ToString() : string.Empty);
}
var errorMessage = sb.ToString();
Console.WriteLine(errorMessage);
}
}
The FailoverClusters module is only available in 64-bit PowerShell sessions. Making this assembly as 64 bit assembly resolved the module not found issue.

Opening runspaces multiple times

I want to execute a powershell command on a remote computer from C#. I have achieved the same using
public Collection<PSObject> RunScript(String Command)
{
Collection<PSObject> results = null;
using (var powershell = PowerShell.Create())
{
Runspace runspace = RunspaceFactory.CreateRunspace(connection);
runspace.Open();
powershell.Runspace = runspace();
powershell.AddScript(Command);
results = powershell.Invoke();
runspace.Close();
}
return results;
}
}
I am able to execute this for first time. but when i try to executr this function for second time it gives me error, runspace is closed.actually i did close it at last but why doesnt it reopen when function is called.
You closed it but you didn't dispose of it:
using (var powershell = PowerShell.Create())
using (Runspace runspace = RunspaceFactory.CreateRunspace(connection))
{
runspace.Open();
powershell.Runspace = runspace();
powershell.AddScript(Command);
results = powershell.Invoke();
runspace.Close();
}

Categories

Resources