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();
}
Related
I am calling a PowerShell script from c# within an ASP page. The script executes just fine but the output (write-host) of my PowerShell does not get captured. I would like to be able to capture any output back into the ASP page.
The following is is my current code.
protected void ExecuteInputClick(object sender, EventArgs e)
{
Result.Text = string.Empty;
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
string scriptfile = "C:\\PRJ_Templates\\CreateProject.ps1";
Command myCommand = new Command(scriptfile);
CommandParameter DestDirParam = new CommandParameter("DestinationDirectory", DropDownList1.SelectedValue);
CommandParameter ProjNamParam = new CommandParameter("ProjectName", Input.Text);
myCommand.Parameters.Add(DestDirParam);
myCommand.Parameters.Add(ProjNamParam);
pipeline.Commands.Add(myCommand);
try
{
System.Collections.ObjectModel.Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
Result.Text = Server.HtmlEncode(stringBuilder.ToString());
}
catch (ActionPreferenceStopException Error) { Result.Text = Error.Message; }
catch (RuntimeException Error) { Result.Text = Error.Message; }
}
Any thought why the write-host is not getting put into Results?
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();
I'm attempting to connect to an Office 365 Group and list the membership of the Group using Powershell through a C# Project.
From this article, I've determined the command I need to use is
Get-UnifiedGroupLinks –Identity groupalias –LinkType Members
Here is my current code:
string connectionUri = "https://outlook.office365.com/powershell-liveid/";
SecureString secpassword = new SecureString();
foreach (char c in Password)
{
secpassword.AppendChar(c);
}
PSCredential credential = new PSCredential(UserName, secpassword);
Runspace runspace = RunspaceFactory.CreateRunspace();
PSObject SessionHolder = null;
using (PowerShell powershell = PowerShell.Create())
{
PSCommand command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", new Uri(connectionUri));
command.AddParameter("Credential", credential);
command.AddParameter("Authentication", "Basic");
powershell.Commands = command;
runspace.Open();
powershell.Runspace = runspace;
Collection<System.Management.Automation.PSObject> result = powershell.Invoke();
if (powershell.Streams.Error.Count > 0 || result.Count != 1)
{
throw new Exception("Fail to establish the connection");
}
else
SessionHolder = result[0];
}
using (PowerShell powershell = PowerShell.Create())
{
PSCommand command = new PSCommand();
// –Identity groupalias –LinkType Members
command = new PSCommand();
command.AddCommand("Invoke-Command");
command.AddParameter("ScriptBlock", System.Management.Automation.ScriptBlock.Create("Get-UnifiedGroupLinks"));
command.AddParameter("Session", SessionHolder);
command.AddParameter("Identity", groupAddress);
command.AddParameter("LinkType", "Members");
powershell.Commands = command;
powershell.Runspace = runspace;
Collection<PSObject> PSOutput = powershell.Invoke();
// loop through each output object item
foreach (PSObject outputItem in PSOutput)
{
}
}
Variables used above, declaration not shown: "UserName", "Password", "groupAddress"
I am able to make a connection to the service, but when I try to get the group members I get the error "A parameter cannot be found that matches parameter name 'Identity'"
I'm not sure how to proceed in troubleshooting my code. I've tried the Group email, Group Alias, and Group Display Name in the Identity parameter. Perhaps I have something else wrong?
I'm using the following Libraries in Visual Studio 2017 on a Windows 10 machine:
using System.Management.Automation;
using System.Management.Automation.Runspaces;
Usually I get that error when I'm passing the variable with some extra characters. Check how the variable is being passed, if its just "email" or if it contains other info like #{"email"}. Very common in Powershell.
Hope that sheds some light. Because the command is very simple:
Get-UnifiedGroupLinks -Identity [name] -LinkType members
I found a bit of code that makes things work... It slows it down, so I'm not sure if there is a better way, but here is my updated code:
string connectionUri = "https://outlook.office365.com/powershell-liveid/";
SecureString secpassword = new SecureString();
foreach (char c in Password)
{
secpassword.AppendChar(c);
}
PSCredential credential = new PSCredential(UserName, secpassword);
Runspace runspace = RunspaceFactory.CreateRunspace();
PSObject SessionHolder = null;
using (PowerShell powershell = PowerShell.Create())
{
string connectionUri = "https://outlook.office365.com/powershell-liveid/";
SecureString secpassword = new SecureString();
foreach (char c in Password)
{
secpassword.AppendChar(c);
}
PSCredential credential = new PSCredential(UserName, secpassword);
PSCommand command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", new Uri(connectionUri));
command.AddParameter("Credential", credential);
command.AddParameter("Authentication", "Basic");
powershell.Commands = command;
runspace.Open();
powershell.Runspace = runspace;
Collection<System.Management.Automation.PSObject> result = powershell.Invoke();
if (powershell.Streams.Error.Count > 0 || result.Count != 1)
throw new Exception("Fail to establish the connection");
else
SessionHolder = result[0];
PSCommand ImportSession = new PSCommand();
ImportSession.AddCommand("Import-PSSession");
ImportSession.AddParameter("Session", SessionHolder);
powershell.Commands = ImportSession;
powershell.Invoke();
PSCommand GrabGroup = new PSCommand();
GrabGroup.AddCommand("Get-UnifiedGroupLinks");
GrabGroup.AddParameter("Identity", GroupAddress);
GrabGroup.AddParameter("LinkType", "Members");
powershell.Commands = GrabGroup;
Collection<PSObject> PSOutput_GroupMembers = powershell.Invoke();
foreach (PSObject outputItem in PSOutput_GroupMembers)
{
//Process Members
}
}
The key seems to be that I need to Import the session before I can begin using it.
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;
}
I have tried executing AD commandlets in powershell using C# from the same machine. This works fine.
static void Main(string[] args)
{
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new string[] { "activedirectory" });
Runspace myRunSpace = RunspaceFactory.CreateRunspace(iss);
myRunSpace.Open();
Pipeline pipeLine = myRunSpace.CreatePipeline();
Command myCommand = new Command("Get-ADUser");
myCommand.Parameters.Add("Filter", "sAMAccountName -eq 'user1'");
//myCommand.Parameters.Add("IncludeDeletedObjects");
pipeLine.Commands.Add(myCommand);
//Command restoreCommand = new Command("Restore-ADObject");
//pipeLine.Commands.Add(restoreCommand);
Console.WriteLine("Before Invoke");
Collection<PSObject> commandResults = pipeLine.Invoke();
Console.WriteLine("After Invoke");
foreach (PSObject cmdlet in commandResults)
{
//Console.WriteLine("Inside foreach");
string cmdletName = cmdlet.BaseObject.ToString();
System.Diagnostics.Debug.Print(cmdletName);
Console.WriteLine(cmdletName);
}
Console.ReadLine();
}
But while trying to run the same command remotely using the invoke command it gives the error The term 'Get-ADUser' is not recognized as the name of a cmdlet, function, script file, or operable program.
The following is my program :
static void Main(string[] args)
{
string shellUri = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
string userName = "Domain\\Administrator";
string password = "Password";
SecureString securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
PSCredential credential = new PSCredential(userName, securePassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false, "machinename", 5985, "/wsman", shellUri, credential);
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
powershell.Runspace = runspace;
PSCommand new1 = new PSCommand();
new1.AddCommand("Get-ADUser");
new1.AddParameter("identity", "CN=user1,DC=example,DC=com");
powershell.Commands = new1;
Collection<PSObject> results = powershell.Invoke();
foreach (PSObject obj in results)
{
PSMemberInfoCollection<PSPropertyInfo> propInfos = obj.Properties;
Console.WriteLine("********************");
foreach (PSPropertyInfo propInfo in propInfos)
{
string propInfoValue = (propInfo.Value == null) ? "" : propInfo.Value.ToString();
Console.WriteLine("{0} --> {1}", propInfo.Name, propInfoValue);
}
}
}
}
}
How can I achieve calling AD commandlets remotely?
Is there a way to invoke commands remotely using InitialSessionState rather than WSManConnectionInfo .
If I use invoke-command -computername $DC -ScriptBlock {Remove-ADUser -identity "user1"} -credential $cred - i get the error The term 'Remove-ADUser' is not recognized as the name of a cmdlet, function, script file, or operable program.
But it is possible to use command Remove-ADUser -identity "user1" -server $DC -credential $cred .How to directly execute the AD command in powershell from C# client?
You need to import the ActiveDirectory module in the remote runspace before executing the AD command e.g.:
powershell.Commands.AddCommand("Import-Module").AddArgument("ActiveDirectory");
powershell.Invoke();
powershell.Commands.Clear();