Invoke Powershell Command from .net framework 2.0 / 3.5 project - c#

I'm trying to invoke a Powershell Command Get-DSCLocalConfigurationManager from a windows service project (.net framework 2.0). using System.Management.Automation
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
try
{
runspace.Open();
var ps = PowerShell.Create();
ps.Runspace = runspace;
ps.AddCommand("Get-DSCLocalConfigurationManager");
var results = ps.Invoke();
foreach (PSObject result in ps.Invoke())
{
_eventLog.WriteEntry(result.Members["AgentId"].Value.ToString());
}
}
catch (Exception e)
{
_eventLog.WriteEntry($"Powershell Command failed with: {e.Message}", EventLogEntryType.Error);
}
}
Invoking the command in a powershell window works as expected.
The problem is that pretty much every command i try to invoke this way returns an exception:
"The term 'Get-DSCLocalConfigurationManager' is not recognized as the name of a cmdlet,..."
I tried the commands Get-Module -ListAvailable $PSVersionTable.PSVersion with the same result. There are commands that work like Get-Process, so i assume there are missing modules.
Importing the module and invoking the command like this
ps.AddScript(#"Import -Module 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psd1'; Get-DSCLocalConfigurationManager");
also doesn't work as it returns the following error:
The 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psd1' module cannot be imported because its manifest contains one or more members that are not valid. The valid manifest members are ('ModuleToProcess', 'NestedModules', 'GUID', 'Author', 'CompanyName', 'Copyright', 'ModuleVersion', 'Description', 'PowerShellVersion', 'PowerShellHostName', 'PowerShellHostVersion', 'CLRVersion', 'DotNetFrameworkVersion', 'ProcessorArchitecture', 'RequiredModules', 'TypesToProcess', 'FormatsToProcess', 'ScriptsToProcess', 'PrivateData', 'RequiredAssemblies', 'ModuleList', 'FileList', 'FunctionsToExport', 'VariablesToExport', 'AliasesToExport', 'CmdletsToExport'). Remove the members that are not valid ('DscResourcesToExport', 'HelpInfoURI'), then try to import the module again.
What Powershell version is used when invoking commands like this? Is it tied to the .net framework version I'm using or is it the installed version of the system? What Modules are loaded? What can i do to invoke this command?
Edit: I created a simple console application and found out, that this is connected with the framework version. With .net framework 4.0 this works as expected, but as soon as i use 3.5 or lower the problem i describe arises.

I had same problem.
It comes from version of Powershell.
When you target project to .Net 2.0/3.5 you using System.Management.Automation.dll version 1.0.0.0, that runs Powershell 2.0, but when your project targets to .Net 4+, using dll version 3.0.0.0 and runs Powershell 3.0.
I pass to my runtime Powershell command to call shell Powershell and execute command, like this:
var results = powerShell.AddScript("powershell Get-AppxPackage -allusers -name 'Microsoft.WindowsStore'").Invoke();
if (powerShell.Streams.Error.Any())
{
foreach (var errorRecord in powerShell.Streams.Error)
throw new Exception();
}
Called Ppowershell redirects streams to runtime Powershell.

Related

powershell on linux docker image

I'm using a linux docker image with powershell and few modules installed on it.
Programmatically, my code do not see the modules.
For verification, if I start the image and instanciate powershell, I can run commands and confirm that the modules are installed:
kubectl run -i --tty test --image=urlofimage:20211008.8 --command pwsh
However, my code do not see the modules.
The extract of code below works in a windows machine, but returns the following error in the docker image:
This parameter set requires WSMan, and no supported WSMan client library was found. WSMan is either not installed or unavailable for this system.
internal const string NewSessionScript = "New-PSSession -ConfigurationName:Microsoft.Exchange -Authentication:Basic -ConnectionUri:{0} -Credential $cred -AllowRedirection";
this._runspace.SetCredential(userName, password);
var command = new PSCommand();
command.AddScript(string.Format(NewSessionScript, connectionUri));
var result = this._runspace.ExecuteCommand<PSSession>(command);
Question:
It looks like the code is instanciating powershell "in memory" but does not use the powershell that is installed on the machine.
How can I use it and leverage an image with all my powershell dependancies installed?

Powershell Cmdlet Invocation Inconsistency

I am trying to call the specific cmdlet "Get-VpnConnection" from a PowerShell instance in a Windows service (.NET Framework 4.7).
I have been able to call this cmdlet successfully in a command-line project (also .NET Framework 4.7) and have been able to call other cmdlets from within this service (e.g. "Get-Date"). I have selected the highest account permissions for the package installer (LocalSystem).
Here is an excerpt of what I've tried:
using (PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Get-VpnConnection");
Collection<PSObject> results = ps.Invoke();
WriteToFile(results.Count.ToString());
}
When I run this from within the service, WriteToFile (a method I've created to write to a log text file in the program's base directory) will write "0" to the log whereas for "Get-Date" it would write "1". When I run this from within the main function of a command-line project, I get "1" as expected. No exceptions are thrown and no PowerShell Stream (Error or Verbose) contains anything. It's almost as if the cmdlet isn't being recognized as valid since providing a gibberish argument to AddCommand will produce an identical result.
Thank you for you time.

Script runs correctly via PowerShell Console, fails when running via C#

When I run the following command via the PowerShell console, it works just fine:
Get-ProcessMitigation -Name iexplore.exe | Select-Object -ExpandProperty ASLR | Select-Object -ExpandProperty BottomUp
However, if I run the same command within a C# method like the following:
public static List<PSObject> RunLocalPowerShellScript(string p_script)
{
using var psInstance = PowerShell.Create();
psInstance.AddScript(p_script);
IEnumerable<PSObject> commandOutput = null;
try
{
commandOutput = psInstance.Invoke();
}
catch (Exception e)
{
var exc = e.Message;
}
if (psInstance.HadErrors)
{
var errorOutput = string.Join(Environment.NewLine, psInstance.Streams.Error.Select(e => e.ToString()));
}
return commandOutput.ToList();
}
and pass the command as the argument, it fails (note the above code needs C# 8 to run properly, but wrapping it in a using block C# 7-style causes the same issue).
The error says This script must be run on Windows 10 or greater.. The issue being that I'm running on Windows 10 1903. It happens both in a .NET Framework project using the Microsoft.PowerShell.5.ReferenceAssemblies package from NuGet, and in .NET Core 3 using the Microsoft.PowerShell.SDK package from NuGet. Other PowerShell commands run without any issue using the exact same method, and the same command runs without issues in a slightly tweaked remote version of our method where we set the remote runspace to run against remote machines. This problem seems to only be limited to running on the local machine.
Does anyone have any insight on this issue? It wouldn't be the first bug we've found at the office in the Get/Set-ProcessMitigation modules, but I figure it's always good to get some outside eyes looking at things.
Seems the Get-ProcessMitigation cmdlet checks the OS version before executing. This would be reasonable, as it might need a new library or is dealing with some new features, but the problem is that by default your app is being told it is running on Windows 8 (6.2), even when the OS is really Windows 10:
Targeting your application for Windows
To get around this, add an app.manifest to your project and uncomment this section:
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

Check if Powershell Module is installed in C#

Is it possible to check if a powershell module ist installed in C#?
I need a C# condition if a module is / or is not installed.
I know how to use powershell in C#, but how become a response from Powershell?
Thanks for reading my question and i hope anyone can give me some hints.
You can add a reference to System.Management.Automation and use the PowerShell to invoke Get-Module cmdlet to check if a specific module has been installed:
var exists = false;
using (PowerShell ps = PowerShell.Create())
{
string moduleName = "something";
ps.AddScript($"Get-Module -ListAvailable -Name {moduleName}");
var result = ps.Invoke();
exists = result.Count > 0;
}
Note: To add a reference to System.Management.Automation, you can install the NuGet package using Install-Package System.Management.Automation.dll Or if you want to see where the dll is located on your system, using powershell console, you can see [PSObject].Assembly.Location and pick it.

Call a specific version of PowerShell from C#

I'm trying to use the Get-VM Cmdlet called from C# on a Hyper-V host.
Obviously, the according PowerShell module Hyper-V has to be imported first. The import fails, however - apparently because the module is supported only on PowerShell 3.0 (at least that's what I figure from this article). The PowerShell used by System.Management.Automation seems to be version 2.0, though.
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(new string[] { "Hyper-V" });
Runspace runSpace = RunspaceFactory.CreateRunspace(iss);
runSpace.Open();
foreach (var err in (ArrayList)runSpace
.SessionStateProxy.PSVariable.GetValue("Error"))
Console.WriteLine(err.ToString());
runSpace.Close();
returns
The
'C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Hyper-V\Hyper-V.psd1'
module cannot be imported because its manifest contains one or more members
that are not valid. The valid manifest members are ('ModuleToProcess', ...).
Remove the members that are not valid ('HelpInfoUri'),
then try to import the module again.
Is there a way to use a specific version of PowerShell in C#?
A colleague figured it out:
Apparently, .NET 4+ comes with an all new common language runtime: the CLR4
This runtime uses its own assemblies loaded from a new assembly cache located at C:\Windows\Microsoft.NET\assembly.
The System.Management.Automation version 3.0.0.0, which will automatically use PowerShell 3.0, exists for the CLR4 only. Because I configured my application to run under .NET 3.5, it would use the old CLR2 and could not even see the newer assembly.
To make sure the application would still run on .NET 3.5, add this to the App.config file in the project folder:
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
If CLR4 is available, it'll load the according GAC, find a policy file that redirects all references to System.Management.Automation version 1.0.0.0 to version 3.0.0.0 and the PowerShell-Modules work as expected.
If you only have .NET 3.5, the older version will be loaded; PowerShell still works, but only up to version 2.0.
Have you looked at this yet?
http://code.msdn.microsoft.com/windowsdesktop/Windows-PowerShell-30-SDK-9a34641d
You might just need the new SDK to call Powershell 3 even if PSv3 is installed on your system already, but I'm usually just a straight Powershell guy.

Categories