-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Description
We use the Microsoft.PowerShell.SDK (7.0.2) NuGet package in a .NET Core 3.1 application to execute PowerShell 7 scripts in a Linux environment. We have found when the .NET application is published with the runtime linux-x64 to reduce the package size, scripts that use the ForEach-Object -Parallel ... fail with the following error message.
[Error] - The term 'Write-Host' 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.
If the .NET application is packaged without specifying runtime the script runs correctly. Also if the ForEach-Object command does not use the -Parallel parameter the script runs correctly.
Steps to reproduce
Here is the test PowerShell script attempting to run through the .NET Application.
$SharedVariable = "Hello Shared Variable"
@(1..100) | ForEach-Object -Parallel {
$i = $_
Write-Host "Running against: $i for SharedVariable: $($using:SharedVariable)"
}Here is a trimmed down version of the .NET Core 3.1 application that is used for running the PowerShell script.
using System;
using System.IO;
using System.Management.Automation;
namespace PowerShellScriptRunner
{
class Program
{
static Exception _lastException;
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.Error.WriteLine("The first argument should the path to the script to run.");
System.Environment.Exit(-1);
}
else if (!File.Exists(args[0]))
{
Console.Error.WriteLine($"Script {args[0]} can not be found.");
System.Environment.Exit(-2);
}
var executingScript = File.ReadAllText(args[0]);
Console.WriteLine("Executing script:");
Console.WriteLine(executingScript);
var ps = PowerShell.Create();
SetupStreamHandlers(ps);
ps.AddScript(executingScript);
var output = new PSDataCollection<PSObject>();
var asyncResult = ps.BeginInvoke<PSObject, PSObject>(null, output);
while (!asyncResult.IsCompleted)
{
asyncResult.AsyncWaitHandle.WaitOne(500);
}
}
private static void SetupStreamHandlers(PowerShell ps)
{
Func<string, EventHandler<DataAddingEventArgs>> _loggerFactory = (prefix) =>
{
EventHandler<DataAddingEventArgs> handler = (sender, e) =>
{
var message = e?.ItemAdded?.ToString();
LogMessage(prefix, message);
var errorRecord = e?.ItemAdded as ErrorRecord;
if (errorRecord?.Exception != null)
{
_lastException = errorRecord.Exception;
}
};
return handler;
};
ps.Streams.Verbose.DataAdding += _loggerFactory("Verbose");
ps.Streams.Information.DataAdding += _loggerFactory("Information");
ps.Streams.Warning.DataAdding += _loggerFactory("Warning");
ps.Streams.Error.DataAdding += _loggerFactory("Error");
}
private static void LogMessage(string prefix, string message)
{
if (string.IsNullOrEmpty(message))
{
return;
}
if (!string.IsNullOrEmpty(prefix))
{
message = $"[{prefix}] - {message}";
}
Console.WriteLine(message);
}
}
}On a Linux environment publish the project using the following command
dotnet publish --configuration Release --self-contained false --runtime linux-x64
then execute the program
dotnet exec .\bin\Release\netcoreapp3.1\linux-x64\publish\PowerShellScriptRunner.dll <path-to-psscriptfile>
Script fails with the following error messages.
[Error] - The term 'Write-Host' 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.
If i repeat the steps above but remove the --runtime linux-x64 switch there is no problem.
One theory I have is with the --runtime linux-x64 switch the assemblies from the publish\runtimes folder get flatten out at the root of the publish folder. But there is also the Modules in the publish\runtimes\unix\lib\netcoreapp3.1\Modules and maybe something about the modules not being direct children of the assemblies anymore is causing the failure.
On a side note the value --runtime win-x64 also doesn't seem to work either but for a different error. That error is
System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Management.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot find the file specified.
File name: 'Microsoft.Management.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
at System.Reflection.RuntimeAssembly.GetExportedTypes()
at System.Reflection.Assembly.get_ExportedTypes()
at System.Management.Automation.Runspaces.PSSnapInHelpers.GetAssemblyTypes(Assembly assembly, String name)
at System.Management.Automation.Runspaces.PSSnapInHelpers.AnalyzeModuleAssemblyWithReflection(Assembly assembly, String name, PSSnapInInfo psSnapInInfo, PSModuleInfo moduleInfo, String helpFile, Dictionary`2& cmdlets, Dictionary`2& aliases, Dictionary`2& providers)
at System.Management.Automation.Runspaces.PSSnapInHelpers.AnalyzePSSnapInAssembly(Assembly assembly, String name, PSSnapInInfo psSnapInInfo, PSModuleInfo moduleInfo, Dictionary`2& cmdlets, Dictionary`2& aliases, Dictionary`2& providers, String& helpFile)
at System.Management.Automation.Runspaces.InitialSessionState.ImportPSSnapIn(PSSnapInInfo psSnapInInfo, PSSnapInException& warning)
at System.Management.Automation.Runspaces.InitialSessionState.CreateDefault()
at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(PSHost host)
at System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace()
at System.Management.Automation.PowerShell.Worker.CreateRunspaceIfNeededAndDoWork(Runspace rsToUse, Boolean isSync)
Which I can see the Microsoft.Management.Infrastructure.dll is missing in the publish folder but Microsoft.Management.Infrastructure.CimCmdlets.dll is there. This isn't my use case as I'm on Linux but maybe related somehow.
Expected behavior
Scripts using -Parallel argument execute successfully.