Enabling execution policy for PowerShell from C# - c#

I have an ASP.NET MVC 4 page that calls a piece of PowerShell. However, I am running into a problem as a module I am using is not signed, so I have to enable the Unrestricted policy. How can I force the PowerShell child to use Unrestricted policy?
I have enabled this in my script, but it is ignored. Also when I try to set the policy in code, an exception is thrown.
using (Runspace myRunSpace = RunspaceFactory.CreateRunspace())
{
myRunSpace.Open();
using (PowerShell powerShell = PowerShell.Create())
{
powerShell.Runspace = myRunSpace;
powerShell.AddCommand("Set-ExecutionPolicy").AddArgument("Unrestricted");
powerShell.AddScript(script);
objectRetVal = powerShell.Invoke();
}
}

If you only need to run the one script with no interactions you can set the execution policy via the command prompt like so:
string command = "/c powershell -executionpolicy unrestricted C:\script1.ps1";
System.Diagnostics.Process.Start("cmd.exe",command);

For PowerShell 5.1 and PowerShell 7 Core, you can use an ExecutionPolicy Enum to set the Execution policy, like so:
using Microsoft.PowerShell;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
...
public class MyClass
{
public void MyMethod()
{
// Create a default initial session state and set the execution policy.
InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ExecutionPolicy = ExecutionPolicy.Unrestricted;
// Create a runspace and open it. This example uses C#8 simplified using statements
using Runspace runspace = RunspaceFactory.CreateRunspace(initialSessionState);
runspace.Open();
// Create a PowerShell object
using PowerShell powerShell = PowerShell.Create(runspace);
// Add commands, parameters, etc., etc.
powerShell.AddCommand(<command>).AddParameter(<parameter>);
// Invoke the PowerShell object.
powerShell.Invoke()
}
}

You have to use parameter -Scope = CurrentUser:
powershell.AddCommand("Set-ExecutionPolicy").AddArgument("Unrestricted")
.AddParameter("Scope","CurrentUser");

This is the same as #kravits88 answer but without displaying the cmd:
static void runPowerShellScript(string path, string args) {
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.Arguments = #"/c powershell -executionpolicy unrestricted " + path + " " + args;
startInfo.UseShellExecute = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.CreateNoWindow = true;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
}

My solution was to self sign the modules and script I was running from IIS Express. I'm still developing and have found that IIS Express does not see all modules that you may have installed in the \System32\WindowsPowerShell...\Modules Path. I moved the modules I was using to another drive and used that location to import the module into my script.
Thanks for the replies :-)

Related

How can I keep an Asp.Net web app running despite a powershell call to [System.Environment]::Exit(1)

I am currently building an Asp.Net Core web app in C#. The web app needs to call a pre-existing powershell script which I cannot edit or modify. I call the script as so:
using (var pwsh = PowerShell.Create())
{
string script = $"Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process; cd \"{directory}\"; .\\script.ps1";
var results = pwsh.AddScript(script).Invoke();
}
This works without any apparent problems for the most part. There is an issue though when the script throws an error. Within the script there is a global catch which looks like this:
catch
{
[System.Environment]::Exit(1)
}
When this catch block is entered the call to Environment.Exit brings down the entire website - this is not the behaviour I would like. I was wondering if there was anyway around this (again without altering the script).
As Keith Nicholas says, we could try to start a process to run the powershell script.
About how to run powershell script, I suggest you could try to refer to below example codes:
if (System.IO.File.Exists("your powshell script path"))
{
string strCmdText = "your powshell script path";
var process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = #"C:\windows\system32\windowspowershell\v1.0\powershell.exe";
process.StartInfo.Arguments = "\"&'" + strCmdText + "'\"";
process.Start();
string s = process.StandardOutput.ReadToEnd();
process.WaitForExit();
}

Executing Powershell Script fails in c#

My powershell script fails to run with the following error:
On this line: -
"Import-AzureRmContext -Path C:\profile.json;"
If I right click the file and "Run with powershell" then the script runs fine.
If I run using PowerShellInstance, get the same error:
Found this code from another StackOverflow Post. Which you can use to execute powershell scripts using command prompt. This worked for me using the below function and call:
ExecuteCommand("powershell -command \" & C:\\PowershellFileName.ps1\"");
public void ExecuteCommand(string Command)
{
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/K " + Command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = true;
Process = Process.Start(ProcessInfo);
}

Activating conda environment from c# code (or what is the differences between manually opening cmd and opening it from c#?)

I want to run a gpu accelerated python script on windows using conda environment (dlwin36).
I’m trying to activate dlwin36 and execute a script:
1) activate dlwin36
2) set KERAS_BACKEND=tensorflow
3) python myscript.py
If I manually open cmd on my machine and write:"activate dlwin36"
it works.
But when I try opening a cmd from c# I get:
“activate is not recognized as an internal or external command, operable program or batch file.”
I tried using the following methods:
Command chaining:
var start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.Arguments = "/c activate dlwin36&&set KERAS_BACKEND=tensorflow&&python myscript.py";
Process.Start(start).WaitForExit();
(I’ve tested several variations of UseShellExecute, LoadUserProfile and WorkingDirectory)
Redirect standard input:
var commandsList = new List<string>();
commandsList.Add("activate dlwin36");
commandsList.Add("set KERAS_BACKEND=tensorflow");
commandsList.Add("python myscript.py");
var start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.UseShellExecute = false;
start.RedirectStandardInput = true;
var proc = Process.Start(start);
commandsList.ForEach(command => proc.StandardInput.WriteLine(command));
(I’ve tested several variations of LoadUserProfile and WorkingDirectory)
In both cases, I got the same error.
It seems that there is a difference between manually opening cmd and opening it from c#.
The key is to run activate.bat in your cmd.exe before doing anything else.
// Set working directory and create process
var workingDirectory = Path.GetFullPath("Scripts");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = true,
WorkingDirectory = workingDirectory
}
};
process.Start();
// Pass multiple commands to cmd.exe
using (var sw = process.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
// Vital to activate Anaconda
sw.WriteLine("C:\\PathToAnaconda\\anaconda3\\Scripts\\activate.bat");
// Activate your environment
sw.WriteLine("activate your-environment");
// Any other commands you want to run
sw.WriteLine("set KERAS_BACKEND=tensorflow");
// run your script. You can also pass in arguments
sw.WriteLine("python YourScript.py");
}
}
// read multiple output lines
while (!process.StandardOutput.EndOfStream)
{
var line = process.StandardOutput.ReadLine();
Console.WriteLine(line);
}
You need to use the python.exe from your environment. For example:
Process proc = new Process();
proc.StartInfo.FileName = #"C:\path-to-Anaconda3\envs\tensorflow-gpu\python.exe";
or in your case:
start.Arguments = "/c activate dlwin36&&set KERAS_BACKEND=tensorflow&&\"path-to-Anaconda3\envs\tensorflow-gpu\python.exe\" myscript.py";
I spent a bit of time working on this and here's the only thing that works for me: run a batch file that will activate the conda environment and then issue the commands in python, like so. Let's call this run_script.bat:
call C:\Path-to-Anaconda\Scripts\activate.bat myenv
set KERAS_BACKEND=tensorflow
python YourScript.py
exit
(Note the use of the call keyword before we invoke the activate batch file.)
After that you can run it from C# more or less as shown above.
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "cmd.exe";
start.Arguments = "/K c:\\path_to_batch\\run_script.bat";
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.WorkingDirectory = "c:\\path_to_batch";
string stdout, stderr;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
stdout = reader.ReadToEnd();
}
using (StreamReader reader = process.StandardError)
{
stderr = reader.ReadToEnd();
}
process.WaitForExit();
}
I am generating the batch file on the fly in C# to set the necessary parameters.
If this is gonna help anyone in the future. I found that you must run the activation from C:\ drive.

Powershell Error when Running process

I am working in C# working on a program to clean up the windows 10 start menu and assign a custom layout. To do so I need to run a single command from powershell and am getting an error when running it.
How I am trying to accomplish the task.
I am starting C:\.\.\powershell.exe and passing the -command arguments of: Import-StartLayout -LayoutPath C:\StartMenu.xml -MountPath C:\
Process process = new Process();
process.StartInfo.FileName = #"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
process.StartInfo.Arguments = #" -command Import-StartLayout -LayoutPath C:\StartMenu.xml -MountPath C:\";
process.Start();
Here is the error I am receiving:
Import-StartLayout : The term 'Import-StartLayout' 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.
At line:1 char:1
+ Import-StartLayout -LayoutPath C:\StartMenu.xml -MountPath C:\; Start ...
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Import-StartLayout:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Any ideas why cmd or powershell will not take the external cmdlet of Import-StartLayout??
Import-StartLayout does not exist on windows 7 and earlier if you know that, continue.
You could try using System.Diagnostics.Process like the following:
Process powerShell = new Process()
{
StartInfo =
{
Arguments = "Import-StartLayout -LayoutPath C:\\StartMenu.xml -MountPath C:\\",
FileName = "powershell"
}
};
powerShell.Start();
Another way would be to use System.Management.Automation which is not an official supported package by Microsoft.
using (Runspace runSpace = RunspaceFactory.CreateRunspace())
{
runSpace.Open();
using (Pipeline pipeline = runSpace.CreatePipeline())
{
Command importStartLayout = new Command("Import-StartLayout");
importStartLayout.Parameters.Add("LayoutPath", "C:\\StartMenu.xml");
importStartLayout.Parameters.Add("MountPath", "C:\\");
pipeline.Commands.Add(importStartLayout);
Collection<PSObject> resultsObjects = pipeline.Invoke();
StringBuilder resultString = new StringBuilder();
foreach (PSObject obj in resultsObjects)
{
resultString.AppendLine(obj.ToString());
}
}
}

Executing 3rd Party Powershell SDK commands with C#

This is an incredibly frustrating problem that nobody else seems to have the answer to so I'm trying to break my question down into the simplest thing possible. Surely there are other 3rd party Powershell SDK's out there that people have tried to access and use via C#. Does anybody know why this would get an error saying the following?
The term 'add-PSSnapin Citrix*.Admin.V* is not recognized as the name of a cmdlet, function, script file, or operable program.
The same goes for other commands that this PSSnapin provides at the command prompt.
I can type this command in manually at a powershell command prompt and it works. So do the other commands. What's the deal?
public void testPS()
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddCommand("add-PSSnapin Citrix.*.Admin.V*");
ps.Invoke();
//Commented sections below don't work either, same error.
//ps.AddCommand("Get-BrokerSession");
//ps.AddParameter("AdminAddress");
//ps.AddParameter("SERVERNAME");
//ps.Invoke();
//Collection<PSObject> psr = ps.Invoke();
//foreach (PSObject x in psr)
//{
// MessageBox.Show(x.ToString());
//}
}
}
UPDATE:
This new code as suggested in the answer below gets a new error: 'System.Management.Automation.ParameterBindingException' occurred in System.Management.Automation.dll
public void testPS()
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
PSSnapInException psex;
runspace.RunspaceConfiguration.AddPSSnapIn("Citrix.Broker.Admin.V2", out psex);
Pipeline pipeline = runspace.CreatePipeline();
Command getSession = new Command("Get-BrokerSession");
getSession.Parameters.Add("AdminAddress");
getSession.Parameters.Add("MYSERVERNAME");
//also tried the above with this code
//getSession.Parameters.Add("-AdminAddress MYSERVERNAME");
// and
//getSession.Parameters.Add("AdminAddress MYSERVERNAME");
// and other methods as well
pipeline.Commands.Add(getSession);
//This line below is where the exception occurs.
Collection<PSObject> output = pipeline.Invoke();
foreach (PSObject x in output)
{
MessageBox.Show(x.ToString());
}
}
}
UPDATE 2:
I also get this same error above trying to set the execution policy.
UPDATE 3:
Fixed, see comments in answer below. The syntax of the parameters line was incorrect.
Use RunspaceConfiguration.AddPSSnapIn to add PSSnapin and then add a command:
Runspace runSpace = RunspaceFactory.CreateRunspace();
runSpace.Open();
PSSnapInException psex;
runSpace.RunspaceConfiguration.AddPSSnapIn("Citrix.Broker.Admin.V2", out psex);
Pipeline pipeline = runSpace.CreatePipeline();
Command getSession = new Command("Get-BrokerSession");
getSession.Parameters.Add("AdminAddress", "SERVERNAME");
pipeline.Commands.Add(getSession);
Collection<PSObject> output = pipeline.Invoke();
You need to make sure that you split the command from its argument. In your case, it would be something like:
ps.AddCommand("add-PSSnapin");
Then you can always just post-append Citrix.*.Admin.V* as an argument for the command above.
For those still stuck with this like me, even as of the latest Microsoft.Powershell.Sdk 6.2.0 nuget package I was running into this issue. A colleague of mine ended up getting it to work by doing the following:
public string RunPowershell(string param1)
{
var outputString = "";
var startInfo = new ProcessStartInfo
{
FileName = #"powershell.exe",
Arguments = "C:\\path\\to\\script.ps1" + " -param1 " + param1,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var process = new Process())
{
process.StartInfo = startInfo;
process.Start();
var output = process.StandardOutput.ReadToEnd();
var errors = process.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(output))
{
outputString = output;
}
if (!string.IsNullOrEmpty(errors))
{
Console.WriteLine($"Error: {errors}");
}
}
return outputString;
}
"C:\path\to\script.ps1" can have Add-PSSnapin commands in it and still run just fine.
I couldn't find a bug filed for Microsoft.Powershell.Sdk to handle this, but if someone creates one and links it here, thanks!
Hope this helps

Categories