Set-Execution Policy from process - c#

I'm doing a VSTO add in for Outlook in C# that calls PowerShell scripts to interact with the Exchange Online of Office 365.
It all works perfectly on my windows 10 machine with a machine level unrestricted PowerShell execution policy. However, I can't get this to run on the client's Windows 7 machine.
I think there are two issues. One that possibly his windows 7 PowerShell needs to be updated to work with my code, and second that I'm not properly setting the process execution policy. Here was my best effort to get the execution policy set to unrestricted (would bypass be better?).
using (PowerShell PowerShellInstance = PowerShell.Create())
{
StringBuilder OSScript = new StringBuilder("Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted;");
OSScript.Append(#"other really exciting stuff");
PowerShellInstance.AddScript(OSScript.ToString());
PowerShellInstance.Invoke();
}
Could someone point me the right direction? I know this doesn't work, as if I set the machine policy to restricted the other really exciting stuff doesn't happen, but if I set it to unrestricted then everything works.

I just created a new Console project and added this to Main:
using (PowerShell PowerShellInstance = PowerShell.Create())
{
string script = "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted; Get-ExecutionPolicy"; // the second command to know the ExecutionPolicy level
PowerShellInstance.AddScript(script);
var someResult = PowerShellInstance.Invoke();
someResult.ToList().ForEach(c => Console.WriteLine(c.ToString()));
Console.ReadLine();
}
This works perfectly for me, even without running the code as administrator. I'm using Visual Studio 2015 in Windows 10 with Powershell 5.
Set-ExecutionPolicy works in the same way in Powershell 4 and 5, according to the Powershell 4.0 Set-ExecutionPolicy and the Powershell 5.0 Set-ExecutionPolicy.

Trying to do this using reflection.
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Reflection;
using Microsoft.PowerShell;
...
InitialSessionState iss = InitialSessionState.CreateDefault();
// Override ExecutionPolicy
PropertyInfo execPolProp = iss.GetType().GetProperty(#"ExecutionPolicy");
if (execPolProp != null && execPolProp.CanWrite)
{
execPolProp.SetValue(iss, ExecutionPolicy.Bypass, null);
}
Runspace rs = RunspaceFactory.CreateRunspace(iss);
rs.Open();
Please note: There are 5 levels (scope) of ExecutionPolicy in PowerShell (see about_execution_policies). This will set Process's ExecutionPolicy. This means if that ExecutionPolicy was defined with Group policy or Local policy ( UserPolicy or MachinePolicy ), this will not override ExecutionPolicy.
Check Get-ExecutionPolicy -List to see list of ExecutionPolicies defined in different scopes for your current process.

Related

How to execute PowerShell script as administrator from Visual Studio C# code

I wrote a PowerShell script it works just fine when I run it in PowerShell ISE as an admin.
But when I run it from my C# code it doesn't work:
InitialSessionState runspaceConfiguration = InitialSessionState.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
string rawScript = File.ReadAllText(scriptPath);
using (PowerShell posh = PowerShell.Create().AddCommand(#scriptPath))
{
string script = "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted; Get-ExecutionPolicy"; // the second command to know the ExecutionPolicy level
posh.AddScript(script);
foreach (DictionaryEntry de in parameters)
{
posh.AddArgument($"{(string)de.Key}, {de.Value}");
}
posh.Invoke();
Collection<PSObject> psObjects = pipeline.Invoke();
runspace.Close();
}
I tried setting ExecutionPolicy to unrestricted, and running Visual Studio as an admin but it didn't work
You can use the Turn on Script Execution Group Policy setting to manage the execution policy of computers in your enterprise. The Group Policy setting overrides the execution policies set in PowerShell in all scopes.
The Turn on Script Execution policy settings are as follows:
If you disable Turn on Script Execution, scripts do not run. This is equivalent to the Restricted execution policy.
If you enable Turn on Script Execution, you can select an execution policy. The Group Policy settings are equivalent to the following execution policy settings:
The PowerShellExecutionPolicy.adm and PowerShellExecutionPolicy.admx files add the Turn on Script Execution policy to the Computer Configuration and User Configuration nodes in Group Policy Editor in the following paths.
For Windows XP and Windows Server 2003:
Administrative Templates\Windows Components\Windows PowerShell
For Windows Vista and later versions of Windows:
Administrative Templates\Classic Administrative Templates\Windows Components\Windows PowerShell
Policies set in the Computer Configuration node take precedence over policies set in the User Configuration node.

Visual Studio can't execute a Powershell script due to execution policy

I'm trying to run a basic powershell script in a c# application, but I can't seem to get it to run. I've tried this code with and without the added execution policy code in the pipeline creation and have gotten the same results.
static void Main(string[] args)
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline("Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned");
pipeline.Commands.Add(#"C:\Users\Bob\Desktop\testScript.ps1");
// Execute PowerShell script
var results = pipeline.Invoke();
}
The error I receive is this:
System.Management.Automation.PSSecurityException: 'File C:\Users\Bob\Desktop\testScript.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at https://go.microsoft.com/fwlink/?LinkID=135170.'
When I run "get-executionpolicy -list", my execution policy seems to be fine.
Normally, the persistently configured script execution policy applies to PowerShell SDK-based projects such as yours, too.
While your Get-ExecutionPolicy -List output implies that RemoteSigned is the effective policy (as reported when you omit -List), the color of your screenshots suggest that you were asking for Windows PowerShell's policy, which is separate from the execution policy for the cross-platform, install-on demand PowerShell (Core) v6+ edition.
Therefore, your inability to execute a local script - which RemoteSigned should allow - can have one of two causes:
If you project is based on the SDK for PowerShell (Core) v6+ (via the Microsoft.PowerShell.SDK NuGet package), you'd have to consult - and possibly modify - its effective execution policy (run pwsh -c Get-ExecutionPolicy to see the policy in effect)
If you don't actually have PowerShell (Core) 6+ itself installed (i.e if you're only using its SDK), use the solution in the bottom section - which may be preferable anyway.
As Mathias points out in a comment, another possible reason is that your local script may have been downloaded from the web, via a web browser, in which case it is considered a remote file for the purpose of the execution policy. Passing its file path to Unblock-File as a one-time action should fix the problem.
Taking a step back:
If you trust the local script to invoke, you can bypass the execution policy for your application only, by configuring your PowerShell SDK session accordingly - see this answer.

Executing Powershell Script Remotely - MSAccess not launching

I am trying to execute a Powershell script remotely that will launch an accdb file via MSAccess. I am able to get the Powershell script to execute successfully, but MSAccess is not launching since I know that the test.accdb file that I have is not getting updated. What am I missing in my code in order to be able to launch MSAccess? Or is it not possible?
My code is running in a Windows 2012 R2 environment in IIS and is being executed by a service account that has Admin privileges to the machine. If I run the code logged in as that service account, it works fine without issues. If I execute it remotely, only part of the code is executed
My code for the .NET app is as follows (running under service account). I've changed some of the private information, but it doesn't effect the code.
internal static HttpStatusCode ExecuteRemoteCommand()
{
WSManConnectionInfo connectioninfo = new WSManConnectionInfo();
connectioninfo.ComputerName = "testcomputer";
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectioninfo))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
var re = ps.AddScript(#"\\testcomputer\test\StartTest.ps1");
var results = re.Invoke();
}
}
return HttpStatusCode.Created;
}
My Powershell code is as follows:
try {
$msAccess = "C:\Program Files (x86)\Microsoft Office\Office14\msaccess.exe"
$fileLocation = "C:\test\DBT.accdb"
Start-Process -FilePath $msAccess -ArgumentList $fileLocation -Verb RunAs -WindowStyle Hidden -WorkingDirectory "C:\test"
$today = Get-Date
"SUCCESS: " + $today > "c:\test\TestExecutionSuccess.txt"
}
Catch
{
$_.Exception.Message > "c:\test\TestExecutionError.txt"
}
In both cases, the one where I run the script locally and the one where I execute it remotely, the TestExecutionSuccess.txt file is created.
However, in ONLY the local test run is the DBT.accdb file updated.
So lets talk about the problems
1 : You can run the code and open Excel when you run the code under your User account logged in.
2 : You can run the code and open Excel when you run the code under your the service account logged in.
3 : you can you the code But Excel will not load if you are not logged into the user.
Why?
This is about Interactive flag. When you are logged into a user then you have can load up GUI's using COM. If you are not logged into a user then you can not load a GUI.
Here is a more indeepth explanation from
Microsoft Interactive User
With ArcSet's guidance, I think I was able to figure out what the issue was. His answer led me down the path of investigating which version of powershell was running. Turned out the C# code was running Powershell x64 instead of x86 (32-bit) version. The office installation is 32-bit and hence why I couldn't run MSAccess.
I couldn't figure out how to run x86 version of Powershell from the C# code, but what I did was put the launch of MSAccess into a batch file and then executed the batch file from my powershell code. Not super clean, but it worked. Access is now running properly.
Here is the batch file code:
cd "C:\Program Files (x86)\Microsoft Office\Office14"
MSACCESS.EXE C:\test\DBT.accdb
and the new version of the PowerShell script:
try {
$msAccess = "C:\Program Files (x86)\Microsoft Office\Office14\msaccess.exe"
$fileLocation = "C:\test\executeTest.bat"
cmd.exe /c $fileLocation
$today = Get-Date
"SUCCESS: " + $today > "c:\test\TestExecutionSuccess.txt"
}
Catch
{
$_.Exception.Message > "c:\test\TestExecutionError.txt"
}

Hosting PowerShell core remoting in dot net core app? Has anyone made it work yet?

I've been trying for a month now to host powershell core remoting scripts, that use SSH protocol for remoting, in a dot net core application and so far nothing worked. One of my main issues is that AddScript method of system management automation seems to be doing absolutely nothing when you have Powershell remoting scripts in the block.
I was wondering if anyone has actually tried to host remoting scripts in dot net core C# application successfully?
I actually have an issue raised in the PowerShell github page, but no one is interested in it, hence the zero comments.
https://github.com/PowerShell/PowerShell/issues/7984
Thank you for your help.
I might have found a simple workaround that I've tested and it works.
Basically instead of asking the don net application to call the remoting script via .AddScript method you ask the dot net app to call a local powershell process that executes the remoting script. The script file should be somewhere on the server you're executing the application from. To show this as a code I'm using the following.
string script = "start-process pwsh-preview" -argument "path to script file"
using (Runspace runspace = RunspaceFactory.CreateRunspace())
using (Powershell powershell = Powershell.Create())
{
runspace.Open();
PSCommand command = new PSCommand();
command.AddScript(script);
powershell.Commands = command;
powerhell.Runspace = runspace;
Collection<PSObject> results = new Collection<PSObject>();
results = powershell.Invoke();
I'm yet to test calling the script with parameters (not sure how to do it yet maybe someone here can help). As well as hosting in docker container the entire thing. Most likely PowerShell core will need to be installed as well as dot net core in the docker image for it to work.
Hope this help.
I'm still waiting for the GitHub PowerShell guys to look at the issue.
I will post more information here once I test hosting this in docker.
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. You might have to download and install the Powershell.SDK NuGet package for the PowerShell scripts to work.
Here you can see I also get the output generated from the script.
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 quotation marks like this:
"OS Version: $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}]");
}
}
}
}
}

Import-Module won't work when launched from .net

I'm trying to run a powershell script within my C# web application.
When i run the following in powershell, it works fine.
Import-Module 'C:\\Program Files\\Microsoft Dynamics NAV\\80\\Service\\NavAdminTool.ps1'
Get-NAVTenant -ServerInstance DynamicsHost
But when i'm running it using my web application, it tells me
The term 'Get-NAVTenant -ServerInstance DynamicsHost' 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.
Here is my c# code:
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { "C:\\Program Files\\Microsoft Dynamics NAV\\80\\Service\\NavAdminTool.ps1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddCommand("Get-NAVTenant -ServerInstance DynamicsHost");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result.ToString());
}
Can someone point me in the right direction??
UPDATE:
Using runspace.SessionStateProxy.PSVariable.GetValue("Error") i could see the following error:
Cannot bind argument to parameter 'Name' because it is null.
Cannot bind argument to parameter 'Path' because it is null.
Cannot find path 'HKLM:\SOFTWARE\Microsoft\Microsoft Dynamics NAV\80\Service' because it does not exist.
A command that prompts the user failed because the host program or the command type does not support user interaction. Try a host program that supports user interaction, such as the Windows PowerShell Console or Windows PowerShell ISE, and remove prompt-related commands from command types that do not support user interaction, such as Windows PowerShell workflows.
A command that prompts the user failed because the host program or the command type does not support user interaction. Try a host program that supports user interaction, such as the Windows PowerShell Console or Windows PowerShell ISE, and remove prompt-related commands from command types that do not support user interaction, such as Windows PowerShell workflows.
A command that prompts the user failed because the host program or the command type does not support user interaction. Try a host program that supports user interaction, such as the Windows PowerShell Console or Windows PowerShell ISE, and remove prompt-related commands from command types that do not support user interaction, such as Windows PowerShell workflows.
The error message suggests that your entire command string is interpreted as the name of a (non-existent) cmdlet. According to the documentation the AddCommand() method expects the name of a cmdlet, while parameters should be added via AddParameter().
Try changing this:
ps.Commands.AddCommand("Get-NAVTenant -ServerInstance DynamicsHost");
into this:
ps.Commands.AddCommand("Get-NAVTenant");
ps.Commands.AddParameter("-ServerInstance", "DynamicsHost");
or this:
ps.AddCommand("Get-NAVTenant");
ps.AddParameter("-ServerInstance", "DynamicsHost");
I can verify the following code works, posted from above: the following code will return the DatabaseServer name of the specified Dynamics NAV service tier
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { "C:\\Program Files\\Microsoft Dynamics NAV\\80\\Service\\NavAdminTool.ps1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddCommand("Get-NAVTenant");
ps.Commands.AddParameter("-ServerInstance", "objectupgrade");
foreach (PSObject result in ps.Invoke())
{
Console.WriteLine(result.Properties["DatabaseServer"].Value);
}
Console.Read();
Console.ReadKey();
A better and a much quicker method is to use the Microsoft.Dynamics.Nav.Management snap-in directly. Significant performance improvement can be seen by using this method. Please see following code found here: Why does PowerShell class not load a snapin
var config = RunspaceConfiguration.Create();
PSSnapInException warning;
config.AddPSSnapIn("Microsoft.Dynamics.Nav.Management", out warning);
using (Runspace runspace = RunspaceFactory.CreateRunspace(config))
{
runspace.Open();
using (var ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddCommand("Get-NAVTenant");
ps.AddParameter("ServerInstance", "ObjectUpgrade");
Collection<PSObject> results = ps.Invoke();
foreach (PSObject obj in results)
{
Console.WriteLine(obj.Properties["DatabaseServer"].Value);
}
Console.Read();
Console.ReadKey();
}
}
It turned out to be that the developer web server could't handle the request. Switching to IIS as developer server solved the problem. Now both of watto's examples works.

Categories

Resources