How to use powershell invoke-command within C#? - c#

If I run the below command inside of Powershell, it works as expected
invoke-command -computername [name] -scriptblock { ipconfig.exe > c:\ipconfig.txt }
But when I try to incorporate this into a c# function, I'm getting this error
{"Cannot bind parameter 'ScriptBlock'. Cannot convert the
\"ipconfig.exe > c:\ipconfig.txt\" value of type \"System.String\" to
type \"System.Management.Automation.ScriptBlock\"."}
even though I'm converting the scriptblock parameter value to a System.Management.Automation.ScriptBlock object? What am I doing incorrect?
private void btnInstallTest_Click(object sender, EventArgs e)
{
List<string> commands = new List<string>();
commands.Add("invoke-command");
List<Tuple<String, String>> parameters = new List<Tuple<string, string>>();
parameters.Add(new Tuple<string, string>("computername", #"server"));
parameters.Add(new Tuple<string, string>("ScriptBlock", #"ipconfig.exe > c:\ipconfig.txt"));
Collection<PSObject> psobject = runRemotePowerShellCommands(commands, parameters, "server", #"domain\user", convertToSecureString("password"));
}
private Collection<PSObject> runRemotePowerShellCommands(List<string> commands, List<Tuple<String, String>> parameters, string remoteMachineName, string domainAndUsername, SecureString securePW)
{
Collection<PSObject> psobjs = new Collection<PSObject>();
string result = "";
string shellUri = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
PSCredential psCredential = new PSCredential(domainAndUsername, securePW);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false, remoteMachineName, 5985, "/wsman", shellUri, psCredential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos; //.Basic;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
PowerShell powershell = PowerShell.Create();
for (int i = 0; i < commands.Count; i++)
{
if (commands[i].Contains(";"))
{
string[] commandsSplit = commands[i].Split(';');
}
else
{
powershell.AddCommand(commands[i]);
}
System.Management.Automation.ScriptBlock sb = null;
if (parameters != null)
{
foreach (Tuple<string, string> param in parameters)
{
if (param.Item1.ToLower() == "scriptblock")
{
sb = ScriptBlock.Create(param.Item2);
powershell.AddParameter(param.Item1, sb);
//powershell.AddParameter(param.Item1, param.Item2);
}
else
{
powershell.AddParameter(param.Item1, param.Item2);
}
}
}
if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)
{
// do nothing, the runspace is already open
}
else
{
runspace.Open();
powershell.Runspace = runspace;
}
try
{
psobjs = powershell.Invoke();
if (powershell.HadErrors == true)
{
result = "Failed - " + powershell.Streams.Error[0].ToString();
result = result.Replace("\"", "*");
psobjs.Add(result);
}
}
catch (Exception ex)
{
result = "Failed: " + ex.Message;
psobjs.Add(result);
}
}
powershell.Commands.Clear();
}
return psobjs;
}

I feel like you've super over-complicated this:
using (PowerShell ps = PowerShell.Create())
{
ps.AddScript($#"Invoke-Command -ComputerName {name} -ScriptBlock {{ipconfig > C:\ipconfig.txt}}")
ps.Invoke()
}
Alternatively you can chain .AddCommand().AddParameter()

Related

How to retrieve results of Poweshell script back in C# objects?

I want to execute a PowerShell script through C#. My script will create a .csv file at a location specified. The below code creates a file at the location specified, but I want the code to return an object which has all the content/data the file has. Is that possible?
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
{
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
Command scriptCommand = new Command(#"C:\powershell.ps1");
Collection<CommandParameter> commandParameters = new Collection<CommandParameter>();
pipeline.Commands.Add(scriptCommand);
Collection<PSObject> psObjects;
psObjects = pipeline.Invoke();
}
Can you rather make use of Powershell APIs?
For example:
PowerShell psinstance = PowerShell.Create();
psinstance.AddScript(scriptPath);
var results = psinstance.Invoke();
More details can be found here:
https://blogs.msdn.microsoft.com/kebab/2014/04/28/executing-powershell-scripts-from-c/
You can return file content from powershell with this command and then write powershell output to the PSDataCollection.
private async Task<IEnumerable<string>> Process(string command)
{
var output = new List<string>();
using (var powerShell = System.Management.Automation.PowerShell.Create())
{
powerShell.AddScript(command);
var outputCollection = new PSDataCollection<PSObject>();
await Task.Factory.FromAsync(
powerShell.BeginInvoke<PSObject, PSObject>(null, outputCollection),
result =>
{
OnDeployEnd?.Invoke(this, EventArgs.Empty);
foreach (var data in outputCollection)
{
output.Add(data.ToString());
}
}
);
if (powerShell.HadErrors)
{
var errorsReport = powerShell.Streams.Error.GetErrorsReport();
throw new Exception(errorsReport);
}
}
return output;
}

How to run powershell script using c# in SharePoint solution

i am getting below error while i am trying to run power-shell script in my SharePoint solution
The type initializer for
'System.Management.Automation.SessionStateScope' threw an exception.
just a reference
private void RunPowershellScript(string scriptFile, List<string> parameters)
{
PowerShell OPowerShell = null;
Runspace OSPRunSpace = null;
try
{
RunspaceConfiguration OSPRSConfiguration = RunspaceConfiguration.Create();
PSSnapInException OExSnapIn = null;
//Add a snap in for SharePoint. This will include all the power shell commands for SharePoint
PSSnapInInfo OSnapInInfo = OSPRSConfiguration.AddPSSnapIn("Microsoft.SharePoint.PowerShell", out OExSnapIn);
OSPRunSpace = RunspaceFactory.CreateRunspace(OSPRSConfiguration);
OPowerShell = PowerShell.Create();
OPowerShell.Runspace = OSPRunSpace;
Command Cmd1 = new Command("backup-spsite");
Cmd1.Parameters.Add("identity", "Your Site Coll URL");
Cmd1.Parameters.Add("path", "Back up file path");
OPowerShell.Commands.AddCommand(Cmd1 );
OSPRunSpace.Open();
OPowerShell.Invoke();
OSPRunSpace.Close();
}
catch (Exception Ex)
{
//Handle exception
}
finally
{
if (OSPRunSpace != null)
{
OSPRunSpace.Dispose();
OSPRunSpace = null;
}
if (OPowerShell != null)
{
OPowerShell.Dispose();
OPowerShell = null;
}
}

Executing PowerShell Script with Parameters

I'm adapting this solution to a Windows Forms solution. So far I've been able to execute the Get-WUList command with no problems. But it doesn't seem to go well with the Hide-WUUpdate. This is what I've tried so far:
public class PowerShellController : IPowerShell
{
//Created at a global scope so anyone can fetch it.
InitialSessionState initial;
RunspaceInvoke scriptInvoker;
Runspace runspace;
PowerShell ps;
//The View to Control
IView view;
//The Helper GridViewProcessor class
IGridViewProcessor gp;
//Initializing the Controller - Loads the Module.
public PowerShellController()
{
initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { #"C:\Users\Jose\Documents\WindowsPowerShell\Modules\PSWindowsUpdate\PSWindowsUpdate.psd1" });
scriptInvoker = new RunspaceInvoke();
scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted -Scope Process");
runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
using (ps = PowerShell.Create())
{
ps.Runspace = runspace;
}
//Console.WriteLine("Please Wait. This will take a while to load.");
}
public void SetView(IView view, IGridViewProcessor gp)
{
this.view = view;
this.gp = gp;
}
public void GetAvailableUpdates()
{
MessageBox.Show("Ok. The program will kind of hang. This is normal." +
"This Means that it will start looking for updates "
);
IEnumerable<PSObject> WUList; //Placeholder for the PS Executed Command
using (ps = PowerShell.Create())
{
//Adds the PowerShell Command
ps.Commands.AddCommand("Get-WUList");
//Executes the PowerShell command
WUList = ps.Invoke();
}
//Loads the Model - Can be later on rewritten for Ninject Support.
List<WindowsUpdate> model = new List<WindowsUpdate>();
int id = 1;
foreach (PSObject result in WUList)
{
WindowsUpdate item = new WindowsUpdate
{
Id = id,
Name = result.Members["Title"].Value.ToString(),
Size = result.Members["Size"].Value.ToString(),
Type = UpdateType.Undefined,
};
model.Add(item);
id++; //Icnrease ID count
//Console.WriteLine("Update Name {0} --- Size: {1}", result.Members["Title"].Value.ToString(), result.Members["Size"].Value.ToString());
}
//Adds it to the view:
view.AddUpdateToGrid(model);
}
public void HideSelectedUpdates(DataGridView grid)
{
//Gets SelectedUpdates to the WindowsUpdate model
var SelectedUpdates = gp.GetSelectedUpdates(grid);
using (ps = PowerShell.Create())
{
foreach (var update in SelectedUpdates)
{
ps.Commands.Clear();
ps.Commands.AddCommand("Hide-WUUpdate").AddParameter("Title",update.Name).AddParameter("Confirm", false);
//ps.Commands.AddCommand("Hide-WUUpdate -Title \""+update.Name+"\"");
var result = ps.Invoke();
}
}
MessageBox.Show("Updates Have been hidden");
}
}
The method I can't seem to work is the HideSelectedUpdates(DataGridView grid).
Script gets executed and no exceptions are thrown, but it doesn't seem to reflect any changes at all.
Any suggestions?

Uninstalling product with WinRM and C#

I'm using WSManAutomation to remotely manage servers.
I need to install and uninstall applications on remote servers that have WinRM over HTTPS enabled. The connection is not a problem
So far the code below executes msiexec.exe in the remote host as I can see it in the list of processes but it does not execute the uninstall command.
public void UninstallProduct(string path, string target, string username = null, string password = null)
{
IWSMan wsman = new WSManClass();
IWSManConnectionOptions options = (IWSManConnectionOptions)wsman.CreateConnectionOptions();
if (options != null)
{
try
{
options.UserName = username;
options.Password = password;
int iFlags = (int)_WSManSessionFlags.WSManFlagCredUsernamePassword;
IWSManSession session = (IWSManSession)wsman.CreateSession(string.Format("https://{0}:5986/wsman", target), iFlags, options);
// IWSManSession session = (IWSManSession)wsman.CreateSession(string.Format("http://{0}/wsman", target), 0, options);
if (session != null)
{
try
{
string strResource = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process";
string strInputParameters =string.Format("<p:Create_INPUT xmlns:p=\"{0}\"><p:CommandLine>\"{1}\"</p:CommandLine></p:Create_INPUT>", "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process",path);
var reply = session.Invoke("Create", strResource, strInputParameters);
Console.WriteLine(reply);
Console.WriteLine();
}
finally
{
Marshal.ReleaseComObject(session);
}
}
}
finally
{
Marshal.ReleaseComObject(options);
}
}
}
The call to the method above would be:
obj.UninstallProduct(#"C:\Windows\System32\msiexec.exe /x {E499AB77-9B27-416CB9B6F-4A171D02BB31} /passive", "hostname", #"hostname\Administrator", "password");
Do you know why the command does not get executed?
Should I use another way to uninstall a product?
Thanks in advance.
Finally I came across a way to do this using remote PowerShell
string command = string.Format("(Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -match {0}}).Uninstall()", productName);
PSCredential credential = new PSCredential(username, securePassword);
string shellUri = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(true, target, 5986, "/wsman", shellUri, credential);
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline(command);
try
{
Collection<PSObject> results = pipeline.Invoke("Set-ExecutionPolicy Unrestricted -Scope Process");
}
finally
{
runspace.Close();
}
}

How I can update the extensionAttribute3 Attribute in Active Directory with Powershell class in c#?

hi i want update the active directory with powershell and asp.net for this i use this code:
costCenter = "777";
public string SetADUser(string Auth_Password,string sAMAccountName, string CostCenter)
{
Security key = new Security();
try
{
SecureString pw = new SecureString();
foreach (char x in Auth_Password)
{
pw.AppendChar(x);
}
InitialSessionState initial = InitialSessionState.CreateDefault();
Environment.SetEnvironmentVariable("ADPS_LoadDefaultDrive", "0");
initial.ImportPSModule(new string[] { "ActiveDirectory" });
PSCredential crend = new PSCredential(ADNAME, pw);
using (Runspace runspace = RunspaceFactory.CreateRunspace(initial))
{
runspace.Open();
using (Pipeline p = runspace.CreatePipeline())
{
Command command = new Command("Set-ADUser");
command.Parameters.Add("Identity", sAMAccountName);
command.Parameters.Add("Replace","#{extensionAttribute3=" + "'" + CostCenter + "'" + "}"); ?? what is here wrong
//command.Parameters.Add("extensionAttribute3", CostCenter);
command.Parameters.Add("Credential", crend);
p.Commands.Add(command);
p.Invoke();
return string.Empty;
}
}
}
catch (Exception ex)
{
return ex.Message;
}
finally
{
}
}
this is the error:
The Parameter "Replace" can not bind. The Value "#{extensionAttribute3='777'}" from typ "System.String" can not convert to "System.Collections.Hashtable".
How I can do this?
#{} is a powershell-shortcut to create a hashtable. In C# you should create a hashtable in the normal C#-way.
var ht = new Hashtable { { "extensionAttribute3", CostCenter } };
command.Parameters.Add("Replace", ht);

Categories

Resources