I need to run powershell cmdlets using C# in Visual Studio Console.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
using System.Threading;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Collections;
namespace ConsoleApp1
{
class Program
{
private static string RunScript()
{
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
Pipeline pipeline = runSpace.CreatePipeline();
Command cmd = new Command("Connect-MsolService");
pipeline.Commands.Add(cmd);
ICollection results = pipeline.Invoke(); // Here exception occurs
runSpace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
static void Main(string[] args)
{
using (PowerShell PowerShellInstance = PowerShell.Create())
{
Console.WriteLine(RunScript());
Console.ReadKey();
}
}
}
}
When I run the code an Exception occurs:
The term 'Connect-MsolService' 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.
Even though it works when I run the commands in Powershell.
Try to use PowerShell instance, like this:
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new[] { "MSOnline" });
iss.LanguageMode = PSLanguageMode.FullLanguage;
var _o365Runspace = RunspaceFactory.CreateRunspace(iss);
_o365Runspace.Open();
var _o365Shell = PowerShell.Create();
_o365Shell.Runspace = _o365Runspace;
var connect = new Command("Connect-MsolService");
connect.Parameters.Add("Credential", new PSCredential("logon#name",
GetSecureString("Password"));
_o365Shell.Commands.AddCommand(connect);
// add some msol commands to _o365Shell.Commands as well
_o365Shell.Invoke();
You are executing it as a CMD command, not as a powershell command.
You have to execute it over an Powershell instance.
Check executing-powershell-scripts-from-c.
Related
I'm attempting to create a function which will take in specified .ps1 file which is embedded within my project, but I'm not quite sure how to change my code around to cater for this.
I've written this in my new .NET 6 C# WPF application based on a VB.net legacy application that does similar stuff:
using System;
using System.Linq;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Windows;
public class PowerShellStuff
{
private readonly string PS_UserName = "";
private readonly string PS_Password = "";
private Runspace? runspace;
private PowerShell? pipeline;
public void ConnectToExchange()
{
System.Security.SecureString securePassword = new System.Security.SecureString();
foreach (char c in PS_Password)
{
securePassword.AppendChar(c);
}
PSCredential? credential = new(PS_UserName, securePassword);
WSManConnectionInfo? connectionInfo = new(new Uri("https://outlook.office365.com/powershell-liveid/"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential)
{
AuthenticationMechanism = AuthenticationMechanism.Basic,
MaximumConnectionRedirectionCount = 2
};
using (runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
using (pipeline = PowerShell.Create())
{
runspace.Open();
pipeline.Runspace = runspace;
}
}
}
public Collection<PSObject> RunScript(PSCommand command)
{
if (runspace == null)
{
try
{
ConnectToExchange();
pipeline = PowerShell.Create();
pipeline.Runspace = runspace;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "User Information", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
if (runspace.RunspaceStateInfo.State != RunspaceState.Opened)
runspace.Open();
pipeline.Commands.Clear();
Command comand = new Command(command.Commands[0].ToString());
for (int i = 0; i <= command.Commands[0].Parameters.Count - 1; i++)
comand.Parameters.Add(command.Commands[0].Parameters[i]);
pipeline.Commands.AddCommand(comand);
Collection<PSObject> results;
try
{
results = pipeline.Invoke();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "User Information", MessageBoxButton.OK, MessageBoxImage.Error);
}
return results;
}
}
This is how its used in VB.net:
Dim command As New PSCommand
command.AddCommand("").AddParameter("", "")...
RunScript(command)
The issue I am having is that I can't even get the above working in C# as it fails with Non-invocable member 'PSCommand.Commands' cannot be used like a method.
My goal is to have a function which I can use to populate a DataTable with results from the .ps1 script, e.g. DataTable dt = new DataTable(RunScript(Resources.MyScript.ps1)) and a function which will not return any data and just execute a SET command with few parameters, which I imagine would follow the same criteria as the VB.net code with command.AddCommand("").AddParameter("", "")...
Its my first time starting PowerShell in C# from scratch as in the past I only carried out simple changes in existing VB.net code, which I used as base to write this...
EDIT 1:
Clearly C# newbie... thanks to first two commenters the issue of executing the above code is resolved, but still unsure how to execute a .ps1 file using my existing runspace & pipeline and populate a DataTable.
I was supposed to give you an example here yesterday but ended up necroing another thread that was several years old. Here goes again hehe :)
I struggled getting PS scripts to work through my app as well and made it work just a like a week ago.
Here I launch a script on a remote computer to make a Windows Toast Notification to pop up.
I didn't get this script to fully work until I downloaded and installed the PowerShell SDK package through NuGet. Hopefully some of this can get you on the right track.
In the screenshot below you can see I also get the output generated from the script in the Debug window.
For some reason Verbose output was not captured even though verbose preference was set in the script. I had to capture the output by putting the statements in the script with quotation marks like this:
"OS Version Display Name: $OsVersion"
using System;
using System.Diagnostics;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WindowsToasts
{
public class WindowsToast
{
public void Send_WindwsUpdateToast(string computerName)
{
InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted;
using Runspace runspace = RunspaceFactory.CreateRunspace(initialSessionState);
runspace.Open();
using PowerShell powerShell = PowerShell.Create(runspace);
string PSPath = #"C:\temp\ToastText.ps1";
powerShell.AddCommand("Invoke-Command");
powerShell.AddParameter("ComputerName", computerName);
powerShell.AddParameter("File", PSPath);
Collection<PSObject> PSOutput = powerShell.Invoke();
foreach (PSObject outputItem in PSOutput)
{
// if null object was dumped to the pipeline during the script then a null object may be present here
if (outputItem != null)
{
Debug.WriteLine($"Output line: [{outputItem}]");
}
}
}
}
}
PowerShell 4.0
In my application the Application class has the set of important properties, methods, and events. I want to work with that members through the app PowerShell variable (it would be like the alias of the class). But the Runspace.SessionStateProxy.SetVariable expects the instance of the class in the second parameter:
using app = CompanyName.AppName.Application;
...
using (Runspace rs = RunspaceFactory.CreateRunspace()) {
rs.ThreadOptions = PSThreadOptions.UseCurrentThread;
rs.Open();
// TODO: The problem is here (app is not the instance of
//the Application class
rs.SessionStateProxy.SetVariable("app", app);
rs.SessionStateProxy.SetVariable("docs", app.DocumentManager);
using (PowerShell ps = PowerShell.Create()) {
ps.Runspace = rs;
ps.AddScript("$docs.Count");
ps.Invoke();
}
rs.Close();
}
How can I do it?
You can use typeof operator in C# to get System.Type instance, which represent specified type. In PowerShell you can use static member operator :: to access to static members of some type.
using app = CompanyName.AppName.Application;
...
using (Runspace rs = RunspaceFactory.CreateRunspace()) {
rs.ThreadOptions = PSThreadOptions.UseCurrentThread;
rs.Open();
rs.SessionStateProxy.SetVariable("app", typeof(app));
using (PowerShell ps = PowerShell.Create()) {
ps.Runspace = rs;
ps.AddScript("$app::DocumentManager");
ps.Invoke();
}
rs.Close();
}
I have ran a debug stepping through the program and it crashes at the following line
Runspace runspace = RunspaceFactory.CreateRunspace();
It gives the following error
An unhandled exception of type 'System.InvalidProgramException' occurred in PrimarySMTP_Fix.exe
Additional information: Common Language Runtime detected an invalid program.
This is my first time working with Power Shell through C# and I'm having trouble getting some simple code execution. The point of the project is to automate a simple fix for a common issue with our company's exchange server.
The complete code is below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Management.Automation;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
namespace PrimarySMTP_Fix
{
public partial class MainWindow : Window
{
//Variable Declarations
string userName = "";
string confirmUser = "";
string primarySMTP = "";
string confirmSMTP = "";
public MainWindow()
{
InitializeComponent();
}
private string RunScript(string scriptText)
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
userName = adUser.Text;
confirmUser = confirmAD.Text;
primarySMTP = mail.Text;
confirmSMTP = confirmMail.Text;
outPut.Text = RunScript(userName);
}
}
}
The above is just setup to test. For now it's taking just the username information and running it directly as a command. If I can get that to work and output information then I can re-write it to do what I want it to do.
After playing around for a while I found that I needed to reinstall the Windows Management Framework to get it to work. Figured that out when I put the debug build on the server and ran the test there and it worked.
We are trying to set a user’s logon script from a remote machine in C#. However, we get the error “The term ‘Set-ADUser’ 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.” Do you have any thoughts on how to resolve this error?
using System;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace PowershellAdUser
{
class PowershellAdUser
{
static void Main(string[] args)
{
string runasUsername = #"login";
string runasPassword = "pass1234";
SecureString ssRunasPassword = new SecureString();
foreach (char x in runasPassword)
ssRunasPassword.AppendChar(x);
PSCredential credentials =
new PSCredential(runasUsername, ssRunasPassword);
var connInfo = new WSManConnectionInfo(
new Uri("http://1.2.3.4/PowerShell"),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credentials);
connInfo.AuthenticationMechanism =
AuthenticationMechanism.Basic;
var runspace = RunspaceFactory.CreateRunspace(connInfo);
runspace.Open();
var pipeline = runspace.CreatePipeline();
var command = new Command("Set-ADUser");
command.Parameters.Add("ScriptPath", "logonScript.bat");
command.Parameters.Add("Identity", "test.com/Users/Test User");
pipeline.Commands.Add(command);
var results = pipeline.Invoke();
runspace.Dispose();
}
}
}
We also tried adding
var command = new Command("Import-Module activedirectory");
pipeline.Commands.Add(command);
after
var pipeline = runspace.CreatePipeline();
This is what we get when we add it
“The term ‘Import-Module activedirectory’ 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.”
After all that didn't work we are trying to pass the connection information and the initial session state at the same time in order to get around the previous 'Import-Module not recognized' error. However, it seems that the function RunspaceFactory.CreateRunspace will either take a WSManConnectionInfo object or a InitialSessionState object, but not both. We also tried to set the initial session state after creating the runspace, but the Runspace's InitialSessionState member appears to be private. Is there any way to initialize a runspace with a WSManConnectionInfo object or a InitialSessionState object simultaneously?
using System;
using System.DirectoryServices;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace test
{
class Program
{
static void Main(string[] args)
{
var target = "servername";
var user = "login";
user = string.Format("{0}\\{1}", target, user);
string shell = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell";
var targetWsMan = new Uri(string.Format("http://{0}:5985/wsman", target));
var password = "pass1234";
var ssPassword = new SecureString();
foreach (char c in password)
{
ssPassword.AppendChar(c);
}
var cred = new PSCredential(user, ssPassword);
var connectionInfo = new WSManConnectionInfo(targetWsMan, shell, cred);
InitialSessionState init_state = InitialSessionState.CreateDefault();
init_state.ImportPSModule(new[] { "ActiveDirectory" });
using (var runSpace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runSpace.InitialSessionState = init_state;
var p = runSpace.CreatePipeline();
runSpace.Open();
var command = new Command("Set-ADUser");
command.Parameters.Add("ScriptPath", "logonScript.bat");
command.Parameters.Add("Identity", "test.com/Users/Test760 Blah760");
p.Commands.Add(command);
var returnValue = p.Invoke();
foreach (var v in returnValue)
Console.WriteLine(v.ToString());
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
In addition, we also experimented with using the "dsadd" command instead of the "Set-ADUser" command. If we call "dsadd" without any parameters, it will return its help information. However, if we try to pass any parameters, it does not throw any errors, but it does not appear to execute the command either. Does anyone know how to call the "dsadd" command from the Pipeline object?
using (var runSpace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runSpace.InitialSessionState = init_state;
var p = runSpace.CreatePipeline();
runSpace.Open();
Command cmd = new Command("dsadd");
cmd.Parameters.Add("ou", "\"OU=test5,OU=Users,DC=test,DC=com\"");
var returnValue = p.Invoke();
foreach (var v in returnValue)
Console.WriteLine(v.ToString());
}
Additional information: The term 'Set-ADUser' 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.
var p = runSpace.CreatePipeline();
runSpace.Open();
Command com1 = new Command("Import-Module");
com1.Parameters.Add("Name", "ActiveDirectory");
p.Commands.Add(com1);
Command command = new Command("Set-ADUser");
command.Parameters.Add("Identity", "tuser19");
command.Parameters.Add("ScriptPath", "logonScript.bat");
p.Commands.Add(command);
var returnValue = p.Invoke();
Try the Import-Module command again but don't mix in the parameter with the command name i.e. separate the parameters out and add via the Parameters collection:
var command = new Command('Import-Module').Parameters.Add('Name', 'ActiveDirectory');
Also, make sure the ActiveDirectory module is on the remote machine. And if that module only loads into a particular bitness console (32-bit or 64-bit) make sure you're using corresponding remoting endpoint.
Try this code. It worked for me
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new String[] { #"<Module name or module path>" });
using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
}
I am running a C# web application in 3.5 framework with Any CPU as target.
My Machine Config:
Windows 7 with 64bit OS
Visual Studio 2010
SharePoint 2010
I am trying to call a SharePoint PowerShell script from my web application. But failed. Below is my code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.IO;
using System.Collections.ObjectModel;
using System.Text;
namespace WebApplication3
{
public partial class test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
try
{
string scriptText = ReadPowerShellScript("D:\\SiteHierarchy_project\\scriptSC.ps1");
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
PSSnapInException snapInException = null;
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PSSnapInInfo info = rsConfig.AddPSSnapIn("Microsoft.SharePoint.Powershell", out snapInException);
Pipeline pipeline = runspace.CreatePipeline();
string scriptSnapIn = "Get-PsSnapin -Registered";
pipeline.Commands.AddScript(scriptSnapIn);
Runspace RunSpace = RunspaceFactory.CreateRunspace(rsConfig);
RunSpace.Open();
Pipeline pipeLine = RunSpace.CreatePipeline();
Command scriptCommand = new Command(scriptText);
pipeLine.Commands.AddScript(scriptText);
// execute the script
// Collection<PSObject> commandResults = pipeLine.Invoke();
pipeLine.Invoke();
// close the runspace
RunSpace.Close();
}
catch (Exception ex)
{ }
}
public string ReadPowerShellScript(string Script)
{
//Read script
//StreamReader objReader = new StreamReader(Server.MapPath(Script));
StreamReader objReader = new StreamReader(Script);
string strContent = objReader.ReadToEnd();
objReader.Close();
return strContent;
}
}
}
and getting the below error.
"The Windows PowerShell snap-in 'Microsoft.SharePoint.Powershell' is not installed on this machine."
Please let me know , where i am doing wrong
Thanks ,
Sandeep
Do you have sharepoint installed on the web server in which you are running this code? The error message is pretty straight forward.
Just an idea;
Create batch file to call .ps1 file. And instead of calling .ps1 file directly from code, you can call batch file using System.Diagnostics.ProcessStartInfo ;
The Solution is call the 64 bit powershell than only it works from the VS 2010.
I had called the Powershell script from the batch file and my batch file is calling the 64 bit poweshell.
In my batch file i wrote
%windir%\sysnative\windowspowershell\v1.0\powershell -File "C:\filepath\Poweshellfile.ps1" auto
Now application is working fine.
Sandeep Tiwari
MCTS SharePoint 2010