I am using System.Management.Automation.dll to execute powershell script from c# code.
Following is my c# code
public void Execute()
{
using (var runspace = RunspaceFactory.CreateRunspace())
{
try
{
var script = File.ReadAllText("script.ps1");
runspace.Open();
var ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddScript(script);
//ps.Ad
ps.Invoke();
foreach (var result in ps.Invoke())
{
Console.WriteLine(result);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
powershell script is
logparser "SELECT top 1 *,null INTO iisLogs FROM 'D:\u_ex200621\*.log' where date >= '2020-01-01' " -i:iisw3c -o:SQL -server:172.29.182.160 -database:DBA_Work -username:azuredev -password:cybage#123 -transactionRowCount:10000 -createTable: OFF
I have added logparser.dll path to Environment Variable so I can execute it from anywhere on machine.
But I tried to execute above code it did not execute anything.Same script I tried using Power shell then it works perfectly.
Please let me know correct way to execute powershell script using C# code.I tried multiple solutions on stackoverflow but no luck.
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}]");
}
}
}
}
}
I need to execute a powershell script from my asp.net MVC Web application. My requirement is to create site collections dynamically. I have the script for it and it works perfectly.There are no arguments which are to be passed to the script. The code which I have been using has been displayed below:
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
//Here's how you add a new script with arguments
Command myCommand = new Command(scriptfiellocation);
pipeline.Commands.Add(myCommand);
pipeline.Commands.Add("Out-String");
// Execute PowerShell script
var result = pipeline.Invoke();
On executing the code, when I check the count of variable result it gives the count as 1. However on checking my site, there is no site collection that has been created. I am not able to identify where I am going wrong as there is no run time error and the Invoke command also seems to be running properly.
Could anyone tell me where I might be going haywire ? Considering that the PowerShell script works perfectly when running through Management shell.
I had to forego the pipeline approach as I was not able to figure out what the issue was. Also another problem with that approach is that it threw the error: "Get-SPWbTemplate is not recognized as an cmdlet". The following code worked perfectly fine for me and created the required site collections:
PowerShell powershell = PowerShell.Create();
//RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
//Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration)
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
//RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
//scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
powershell.Runspace = runspace;
//powershell.Commands.AddScript("Add-PsSnapin Microsoft.SharePoint.PowerShell");
System.IO.StreamReader sr = new System.IO.StreamReader(scriptfilepath);
powershell.AddScript(sr.ReadToEnd());
//powershell.AddCommand("Out-String");
var results = powershell.Invoke();
if (powershell.Streams.Error.Count > 0)
{
// error records were written to the error stream.
// do something with the items found.
}
}
Also there was no requirement to set the execution policy.
well don't know if its help but i never use pipeline to run Command shell not sure how that work.
But here a quick example
Runspace RS = RunspaceFactory.CreateRunspace(myConnection);
PowerShell PS = PowerShell.Create();
PSCommand PScmd = new PSCommand();
string cmdStr = "Enable-Mailbox -Identity " + username + " -Database DB01 -Alias " + aliasexample;
PScmd.AddScript(cmdStr);
try
{
RS.Open();
PS.Runspace = RS;
PS.Commands = PScmd;
PS.Invoke();
}
catch (Exception ex)
{
ex.ToString();
}
finally
{
RS.Dispose();
RS = null;
PS.Dispose();
PS = null;
}
with the try catch you can catch the error with debugging if something goes wrong.
If i remember correctly i had to put ACL.exe for permission to file system so i can execute the commandshell you can do a quick search on google for it.
Hope this help.
I am working on a C# project that that is supposed to grab a string variable (file path) and pass it to PowerShell script to have further commands done with it. I have been looking around online and through Stack and have not been able to find something that works for me...
Here is my C# code as it stands right now:
string script = System.IO.File.ReadAllText(#"C:\my\script\path\script.ps1");
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(script);
ps.Invoke();
ps.AddCommand("LocalCopy");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result);
}
}
Here is my PowerShell script:
Function LocalCopy
{
Get-ChildItem -path "C:\Users\file1\file2\file3\" -Filter *.tib -Recurse |
Copy-Item -Destination "C:\Users\file1\file2\local\"
}
What I want to do is have the first part of the the script: "C:\Users\file1\file2\file3\" replaced with (what i am assuming would be) a variable that I could pass from the C# code to the PowerShell script. I am very new to working with PowerShell and am not quite sure how I would go about doing something like this.
---EDIT---
I am still having issues with my code, but i am not getting any errors. I believe that it is because the variable is still not being passed through...
C# code:
string script = System.IO.File.ReadAllText(#"C:\my\script\path\script.ps1");
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(script);
ps.Invoke();
ps.AddArgument(FilePathVariable);
ps.AddCommand("LocalCopy");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result);
}
}
PowerShell code:
Function LocalCopy
{
$path = $args[0]
Get-ChildItem -path $path -Filter *.tib -Recurse |
Copy-Item -Destination "C:\Users\file1\file2\local\"
}
Any help would be much appreciated. Thanks!
I would go the route Anand has shown to pass a path into your script. But to answer the question posed by your title, here's how you pass variable from C#. Well this is really how you set the variable in the PowerShell engine.
ps.Runspace.SessionStateProxy.SetVariable("Path", #"C:\Users\file1\file2\file3\");
Note: in C# for file paths you really want to use verbatim # strings.
Update: based on your comments, try this:
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddScript(script, false); // Use false to tell PowerShell to run script in current
// scope otherwise LocalCopy function won't be available
// later when we try to invoke it.
ps.Invoke();
ps.Commands.Clear();
ps.AddCommand("LocalCopy").AddArgument(FilePathVariable);
ps.Invoke();
ps.AddArgument("C:\Users\file1\file2\file3\");
you can use args to fetch the argument in powershell.
$path = $args[0]
MSDN
Import-Module command work fine with powershell windows console but same command doesn't work on c# api.
i'm using this project for execute powershell script: http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C
it execute many of them commands but it doesn't execute "Import-Module 'c:\vm\vm.psd1'" command. i try import microsoft modules but it doesn't work too. How can i execute "Import-Module" command with c# api?
Also add-pssnapin 'virtualmachinemanager' doesn't work too.
Try load module in this way:
PowerShell powershell = PowerShell.Create();
powerShell.Commands.AddCommand("Import-Module").AddParameter("Name", "c:\vm\vm.psd1'");
or
PowerShell powershell = PowerShell.Create();
powershell.Commands.AddCommand("Add-PsSnapIn").AddParameter("Name", "virtualmachinemanager");
With a pipeline try create an InitialSessionState
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new string[] { #"C:\vm\vm.psd1"});
Runspace runSpace = RunspaceFactory.CreateRunspace(iss);
runSpace.Open();
then use your code with pipeline to run cmdlet from module loaded
Try something like this for loading the snapin and executing your commands:
using System.Management.Automation.Runspaces;
//...
var rsConfig = RunspaceConfiguration.Create();
using (var myRunSpace = RunspaceFactory.CreateRunspace(rsConfig))
{
PSSnapInException snapInException = null;
var info = rsConfig.AddPSSnapIn("FULL.SNAPIN.NAME.HERE", out snapInException);
myRunSpace.Open();
using (var pipeLine = myRunSpace.CreatePipeline())
{
Command cmd = new Command("YOURCOMMAND");
cmd.Parameters.Add("PARAM1", param1);
cmd.Parameters.Add("PARAM2", param2);
cmd.Parameters.Add("PARAM3", param3);
pipeLine.Commands.Add(cmd);
pipeLine.Invoke();
if (pipeLine.Error != null && pipeLine.Error.Count > 0)
{
//check error
}
}
}
I have created a asp.net web application for internal use that allows certain users to start and stop Virtual machines that are linked to there QA testing environments, the code behind page runs a powershell script that starts the selected server once a button is pressed on an ASP.net page.
I have reserched and implimented alot of the code from this site but i am coming up against a few problems.
everytime i click the button on the main web page the error that is fed back from the powershell script says"You cannot call a method on a null-valued expression." the only problem is if i run it from a powershell prompt like this ". \script\test.ps1 'W7EA9'" it works fine.
This is the class that calls the powershell script.
public String Startserver(String Servername)
{
String scriptText =". \\scripts\\test.ps1 " + Servername + "";
// create Powershell runspace
Runspace runspace = RunspaceFactory.CreateRunspace();
// open it
runspace.Open();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// execute the script
Collection<PSObject> results = new Collection<PSObject>();
try
{
results = pipeline.Invoke();
}
catch (Exception ex)
{
results.Add(new PSObject((object)ex.Message));
}
// close the runspace
runspace.Close();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
//return scriptText;
}
and here is the powershell script it is trying to run
Param ($server = $arg[0])
$Core = get-wmiobject -namespace root\virtualization -class Msvm_Computersystem -filter "ElementName = '$server'"
$status = $Core.RequestStateChange(2) `
It may be somthing really obvious but im just not seeing it and any help would be great.
thanks
Chris
Here is a best step-by-step guide to running PowerShell from ASP.NET.
http://devinfra-us.blogspot.com/2011/02/using-powershell-20-from-aspnet-part-1.html
HTH
I don't see where you're providing a parameter to the script anywhere.
i am passing the paramater from a button press on an asp.net page the code behind looks like this
Hypervserver Start = new Hypervserver();
String result = Start.Startserver("W7EA9");
Label1.Visible = true;
Label1.Text = result;
Below is how I ended up doing this.
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
Command myCommand = new Command("C:\\Scripts\\Test.ps1");
//I used full path name here, not sure if you have to or not
CommandParameter myParam1 = new CommandParameter("-ServerName", "myServer");
myCommand.Parameters.Add(myParam1);
//You can add as many parameters as you need to here
pipeline.Commands.Add(myCommand);
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder()
foreach (PSObject obj in results) {
stringBuilder.AppendLine(obj.ToString());
}
string thestring = stringBuilder.ToString();
A few notes. The scripts first line that is not a comment or blank line should be the parameter list formatted like this:
param([string]$ServerName,[string]$User)
You have this, I just wanted to acknowledge the fact that I could not get this working when my script file was a function with parameters.
For certain commands you may need additional privileges, in my case all of my scripts worked this way except for creating a mailbox for which I had to add credentials onto the connection.
Greg
In powershell, the default arguments collection is called $args, with an 's'; I'm pretty sure that's why $server is null when you run it via code, and thus the Get-WmiObject call returns null, causing the error when you attempt to call the RequestStateChange method on it.
I'm guessing it works fine in your normal powershell window because you already have a $server variable in the session.