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();
}
}
}
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 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);
}
}
}
}
I am new to c# from vb and having a hard time figuring out how to run a method.
My full code is below. My question is what should be in the main method. I got the functions from forums that is supposed to let me remote execute exchange admin commands. I am trying to run basic powershell command with remote exchange to see if I can get this to work.
Any help would be greatly appreciated.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Remoting;
using System.Management.Automation.Runspaces;
namespace ExchPwrShellCmdletsTest
{
class Program
{
static void Main(string[] args)
{
}
public Collection<PSObject> GetUsersUsingBasicAuth(string liveIDConnectionUri, string schemaUri, PSCredential credentials, int count)
{
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
new Uri(liveIDConnectionUri),
schemaUri, credentials);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
return GetUserInformation(count, runspace);
}
}
public Collection<PSObject> GetUserInformation(int count, Runspace runspace)
{
using (PowerShell powershell = PowerShell.Create())
{
powershell.AddCommand("Get-Users");
powershell.AddParameter("ResultSize", count);
runspace.Open();
powershell.Runspace = runspace;
return powershell.Invoke();
}
}
}
}
public Collection<PSObject> GetUsersUsingBasicAuth(string liveIDConnectionUri, string schemaUri, PSCredential credentials, int count) is defining a new method in your code. Because it specified Collection<PSObject> your return type of this method will be in the form of a Collection<PSObject>. To call the method you reference it by name, then pass in any usable parameters.
i.e
var coll = GetUsersUsingBasicAuth("liveidconnectionurl","schemauri",new PSCredentials(...), 5 ); for your first method defined.
You would use that above code snippet (with actual values) inside of:
static void Main(string[] args)
{
var coll = GetUsersUsingBasicAuth("liveidconnectionurl","schemauri",new PSCredentials(...), 5 );
}
Description of Issue:
I've created a host for Powershell 2.0. The host uses WPF and C#.
The problem is, much output to my WPF host is not formatted the same way as what is displayed when using Powershell ISE.
When I run the script ls alias: in Powershell ISE, the results are in a table form, with CommandType, Name, and Definition column headers. When I run the same script in my WPF host, the results are simply a list of the names of aliases. A similar issue happens with many other scripts, commandlets, and aliases.
Question:
How do I get the same formatting functionality that seems to be built into Powershell ISE? Is this an issue with the Powershell side of things, or do I need to include extra code on the WPF side of things?
Code Sample:
Here is the class I've built to handle Powershell input and output, from within WPF (framework courtesy of example at code.msdn.microsoft.com, although my class is not asynchronous):
internal class PSInterfacer
{
private Runspace runspace { get; set; }
public PSInterfacer()
{
runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
}
public string RunScript(string script)
{
string tempOut = "";
// create powershell instance
using (PowerShell ps = PowerShell.Create())
{
// add existing runspace
ps.Runspace = runspace;
// add script
ps.AddScript(script);
// pipe errors to the same output as normal results
ps.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error,
PipelineResultTypes.Output);
// put output in list
List<PSObject> psOutputs = ps.Invoke().ToList();
// display output elements, seperated by newlines
foreach (PSObject psObject in psOutputs)
{
if (psObject != null)
{
tempOut += psObject.BaseObject;
}
if (psObject != psOutputs.Last())
{
tempOut += Environment.NewLine;
}
}
return tempOut;
}
}
}
After working for a few days, found this solution. Basically, use the .AddCommand method (in the Powershell class) to pipe to "Out-String." That formats everything properly.
Replace RunScript method with this:
public string RunScript(string script)
{
string tempOut = "";
try
{
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript(script);
ps.AddCommand("Out-String");
ps.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
List<PSObject> psOutputs = ps.Invoke().ToList();
foreach (PSObject psObject in psOutputs)
{
if (psObject != null)
{
tempOut += psObject.BaseObject;
}
}
}
}
catch(Exception ex)
{
tempOut = string.Format("Error when invoking powershell commands:\n{0}", ex.Message);
tempOut += "\nThis function did not complete.";
}
return tempOut;
}
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();
}
}
}