I have been looking around here but unable to get specifics on implementing this PowerShell cmdlet in C#. I attempted the following but failed to get it to compile and run.
The cmdlet I would like to run in PowerShell from C#:
Restart-Computer -Computername (Get-Content C:\machineslist.txt) -Credential Administrator -Force
Here is my humble attempt:
PowerShell ps = PowerShell.Create();
ps.AddCommand("Restart-Computer");
ps.AddParameter("-ComputerName");
ScriptBlock filter2 = ScriptBlock.Create("(Get-Content C:\\machineslist.txt)");
ps.AddParameter("FilterScript2", filter2);
ps.AddParameter("-Credential");
ps.AddArgument("Administrator");
//not sure how to add password
ps.AddParameter("-Force");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(
"{0,-24}{1}",
result.Members["Length"].Value,
result.Members["Name"].Value);
} // End foreach
To make this code snippet to compile and run, you will first need to reference the System.Management.Automation assembly (located under C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0).
You do not need a ScriptBlock as it increases complexity, it's easier to just add the script with AddScript.
You do not need to prefix parameters with -
To pass credentials you can use PSCredential. Normally you would provide a secure string, you can translate a string into a secure string using NetworkCredential as an helper.
You will need to handle errors as well, but this is out of scope for this question!
Enjoy :-)
using System;
using System.Management.Automation;
using System.Net;
using System.Security;
namespace Sample
{
class Program
{
static void Main(string[] args)
{
PowerShell ps = PowerShell.Create();
ps.AddScript("Get-Content C:\\machineslist.txt");
ps.AddCommand("Restart-Computer");
SecureString secureString = new NetworkCredential("", "Password").SecurePassword;
PSCredential psc = new PSCredential("Administrator", secureString);
ps.AddParameter("Credential", psc);
ps.AddParameter("Force");
// Simulation only
ps.AddParameter("WhatIf");
var results = ps.Invoke();
foreach (var error in ps.Streams.Error)
{
Console.WriteLine(error);
}
foreach (PSObject result in results)
{
Console.WriteLine(result);
//Console.WriteLine("{0,-24}{1}", result.Members["Length"].Value, result.Members["Name"].Value);
}
}
}
}
Related
I am trying to get a list of all Virtual Processes started by Microsoft AppV using C#.
I tried using Powershell in C# but I get this error:
System.Management.Automation.CommandNotFoundException: 'The 'Get-AppvVirtualProcess' command was found in the module 'AppvClient', but the module could not be loaded. For more information, run 'Import-Module AppvClient'.'
The weird thing is that if I use the Powershell command line, it works just fine and lists the virtual processes.
So in C# I did a:
ps.Commands.AddCommand("Get-Command");
and it shows Get-AppvVirtualProcess listed as a command:
The result:
Function Get-AppvVirtualProcess 1.0.0.0 A
I tried loading the module in C# manually using:
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] {#"C:\Program Files\Microsoft Application Virtualization\Client\AppvClient\AppvClient.psd1" });
and
ps.Commands.AddCommand("Import-Module").AddArgument("AppvClient");
But it still gives me the same error mentioned above.
The code in C# looks like this:
public static void powershellCommand()
{
Collection<PSObject> result;
using (Runspace myRunSpace = RunspaceFactory.CreateRunspace())
{
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] {#"C:\Program Files\Microsoft Application Virtualization\Client\AppvClient\AppvClient.psd1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddCommand("Import-Module").AddArgument("AppvClient");
ps.Commands.AddCommand("Get-AppvVirtualProcess");
result = ps.Invoke();
var builder = new StringBuilder();
foreach (PSObject psObject in result)
{
builder.Append(psObject.ToString() + "\n");
builder.ToString();
}
Console.WriteLine("Virtual Process: {0}", builder.ToString());
}
}
Instead of Runspace, I tried this as well but I get the same error:
public static void p()
{
using (var powershell = PowerShell.Create())
{
powershell.AddCommand("Get-AppvVirtualProcess");
powershell.Invoke();
}
}
You could try to iterate through all the running process, and find those that loaded either AppVEntSubsystems32.dll or AppVEntSubsystems64.dll.
You can read more about this here: https://blogs.msdn.microsoft.com/gladiator/2014/09/04/app-v-5-on-application-launch/
I am currently trying to use c# to call the Get-SMBShare which can be used in Powershell... however, it's throwing this error:
Exception:Caught: "The term 'Get-SMBShare' 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." (System.Management.Automation.CommandNotFoundException)
A System.Management.Automation.CommandNotFoundException was caught: "The term 'Get-SMBShare' 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."
Time: 25/10/2015 19:17:59
Thread:Pipeline Execution Thread[6028]
My first language is PowerShell, so I'm trying to translate a GUI tool from PowerShell to C#, and the tool uses hundreds of PS commands - is there something I should be calling? I'm testing things out in console here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
private static void GetShareNames()
{
// Call the PowerShell.Create() method to create an
// empty pipeline.
PowerShell ps = PowerShell.Create();
ps.AddCommand("Get-SmbShare");
Console.WriteLine("Name Path");
Console.WriteLine("----------------------------");
// Call the PowerShell.Invoke() method to run the
// commands of the pipeline.
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(
"{0,-24}{1}",
result.Members["Name"].Value,
result.Members["Path"].Value);
} // End foreach.
Console.ReadLine();
} // End Main.
static void Main(string[] args)
{
GetShareNames();
}
}
}
You need to import the module first. Stick in this line before you try to execute the Get-SmbShare command:
ps.AddCommand("Import-Module").AddArgument("SmbShare");
ps.Invoke();
ps.Commands.Clear();
ps.AddCommand("Get-SmbShare");
Another way is to initialize the runspace with the SmbShare module pre-loaded e.g.:
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new[] {"SmbShare"} );
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
I want to create a powershell function and use it from inside the C#
using System;
using System.Management.Automation;
using System.Text;
namespace PowerShell_eg
{
public class Program
{
public static void Main(String[] args)
{
var psFunction = #" function Get-Hostname { hostname } ";
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
??// HOW TO Add Get-Hostname & INVOKE IT ??
pipeline.Commands.AddScript(psFunction);
pipeline.Commands.Add("Get-Hostname");
var results = pipeline.Invoke();
foreach (var obj in results.Where(o => o != null))
{
Console.WriteLine("\t" + obj);
}
}
}
}
Currently I get CommandNotFound exception # Invoke.
The term 'Get-Hostname' is not recognized as the name of a cmdlet, function, script file, or operable program.
Please advice how to correctly do this!
Also it will be ideal if I can add multiple functions and cont. to use them over the life of the powershell session without having to add them again and again.
This C# code seems to work fine for me. Just add a reference to the System.Management.Automation .NET assembly.
using System;
using System.Management.Automation;
namespace PowerShellTest02
{
class Program
{
static void Main(string[] args)
{
string func = #"function Test { Write-Host 'hello' };";
PowerShell ps = PowerShell.Create();
ps.AddScript(func);
ps.Invoke();
ps.AddCommand("Test");
ps.Invoke();
Console.WriteLine("Successfully executed function");
Console.ReadLine();
}
}
}
I am trying to get information about my users' mailboxes using power shell integrated into C#, but i am getting below error:
This syntax is not supported by this run space. This might be because it is no-language mode.
Here is the code that I am using:
using System;
using System.Collections.ObjectModel;
using System.Collections;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;
using System.Globalization;
namespace Office365
{
class Program
{
static void Main(string[] args)
{
CultureInfo oldCI = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
System.Security.SecureString secureString = new System.Security.SecureString();
string myPassword = "mySecretPassword";
foreach (char c in myPassword)
secureString.AppendChar(c);
PSCredential credential = new PSCredential("my#maildomain.com", secureString);
Console.WriteLine("Forbinder....");
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://ps.outlook.com/PowerShell-LiveID?PSVersion=2.0"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
connectionInfo.SkipCACheck = true;
connectionInfo.SkipCNCheck = true;
string cmd2 = #"Get-Mailbox | Select-object Identity, displayname, ProhibitSendReceiveQuota, #{n='size';e={$MBXSTAT=Get-MailboxStatistics $_.Identity; $MBXSTAT.TotalItemSize}}";
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (Pipeline pipeline = runspace.CreatePipeline(cmd2))
{
pipeline.Commands.AddScript(cmd2);
Collection<PSObject> results = pipeline.Invoke();
foreach (PSObject obj in results)
{
// Do something with each result object
}
}
}
}
}
Any Suggestions on this? How can I overcome to this issue?
Normally i like to leave the dead alone but in this case i feel the need to resurrect this old post because i encountered the exact problem, need a place to keep this code and hope this prevents anybody else from wasting too much time.
I have found connecting to O365 with WSManConnectionInfo problematic and after wasting far too much time trying to get it to work i won't be using it unless i have no other choice.
That said the following code works for me and behaves the same as if i had opened a PowerShell prompt entered the commands i want to run :) it makes testing a lot simpler.
using (var _power_shell = PowerShell.Create()) {
_power_shell.AddScript("Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process");
_power_shell.Invoke();
var credential = new PSCredential(username, password.ToSecureString());
_power_shell.Runspace.SessionStateProxy.SetVariable("Credential", credential);
_power_shell.AddScript("$Session = New-PSSession –ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential
$Credential -Authentication Basic -AllowRedirection");
_power_shell.AddScript("Import-PSSession $Session");
_power_shell.Invoke();
_power_shell.AddScript("Get-Mailbox -RecipientTypeDetails RoomMailbox | Select-Object DisplayName, PrimarySmtpAddress");
var results = _power_shell.Invoke();
foreach (var obj in results)
{
/* Do something with the results */
}
_power_shell.AddScript("Remove-PSSession $Session");
_power_shell.Invoke();
}
The above code assumes username & password as string variables already in scope. Also an Extention method ToSecureString exists and is in scope.
As far as i understand PowerShell.Creste() provides an execution environment set to Restricted so you first need to change that. In this case becasue we scoped the Set-ExecutionPolicy to the process Admin rights are not required.
From what little i found on the subject it appears you need to remove the session when you are done or you may get throttled and lock yourself out of O365 for some time.
This is restriction from Exchange online and you need to use Commands instead of scripts in this case.
For example, running cmdlet Get-Mailbox -Identity user#mydomain.onmicrosoft.com will look like:
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://ps.outlook.com/PowerShell-LiveID?PSVersion=2.0"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell shellInstance = PowerShell.Create();
shellInstance.Runspace = runspace;
PSObject mailbox = shellInstance.Runspace.AddCommand("Get-Mailbox").AddParameter("Identity","user#mydomain.onmicrosoft.com").Invoke().First();
I didn't use complex scripts and pipelines in my project so I'm not sure how it works if you need to run something rather tricky.
I want to run Powershell command on remote machine. This is method that I am using (localhost:131 is because I use tunnel to remote machine's port 5985):
public string RunRemotePowerShellCommand(string command)
{
System.Security.SecureString password = new System.Security.SecureString();
foreach (char c in _password.ToCharArray())
{
password.AppendChar(c);
}
string schema = "http://schemas.microsoft.com/powershell/Microsoft.Powershell";
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false,
"localhost", 131, "/wsman", schema, new PSCredential(_domain + #"\" + _userName, password));
using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
remoteRunspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
powershell.Runspace = remoteRunspace;
powershell.AddCommand(command);
powershell.Invoke();
Collection<PSObject> results = powershell.Invoke();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
}
}
I'm trying to run following command:
D:\FolderName\scriptName.ps1 -action editbinding -component "comp1","comp2","comp3","comp4"
Like this:
RunRemotePowerShellCommand(#"D:\FolderName\scriptName.ps1 -action editbinding -component ""comp1"",""comp2"",""comp3"",""comp4""");
but I get:
Error: System.Management.Automation.RemoteException: The term 'D:\FolderName\scriptName.ps1 -action editbinding -component "comp1","comp2","comp3","comp4"' is not recognized as a name of cmdlet, function, script file, or operable program. Check the spelling of the name, or if the path is included, verify that the path is correct and try again.
The method works fine with simple commands, and the command that I want to run is ok when I run it on remote machine.
Thanks in advance.
Regards,
Dusan
You need to use the powershell.AddParameter() method to add the parameters for your command. The AddCommand() call should name just the command: cmdlet name, function name, path to script, etc. From the docs:
PowerShell ps = PowerShell.Create();
ps.AddCommand("Get-Process");
ps.AddArgument("wmi*");
ps.AddCommand("Sort-Object");
ps.AddParameter("descending");
ps.AddArgument("id");
I has a similar requirement.
My solution was to create a powershell function in C# code and use it over the powershell remote session like.
using System;
using System.Management.Automation;
namespace PowerShellTest
{
class Program
{
static void Main(string[] args)
{
string func = #"function Test { Write-Host 'hello' };";
PowerShell ps = PowerShell.Create();
ps.AddScript(func);
ps.Invoke();
ps.AddCommand("Test");
ps.Invoke();
Console.WriteLine("Successfully executed function");
Console.ReadLine();
}
}
}