mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Support using non-compatible Windows PowerShell modules in PowerShell Core (#10973)
This commit is contained in:
@@ -134,7 +134,12 @@ namespace System.Management.Automation
|
||||
#endif
|
||||
new ExperimentalFeature(
|
||||
name: "PSNullConditionalOperators",
|
||||
description: "Support the null conditional member access operators in PowerShell language")
|
||||
description: "Support the null conditional member access operators in PowerShell language"),
|
||||
#if !UNIX
|
||||
new ExperimentalFeature(
|
||||
name: "PSWindowsPowerShellCompatibility",
|
||||
description: "Load non-PSCore-compartible modules into Windows PowerShell over PS Remoting")
|
||||
#endif
|
||||
};
|
||||
EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace System.Management.Automation
|
||||
var moduleManifestProperties = PsUtils.GetModuleManifestProperties(modulePath, PsUtils.FastModuleManifestAnalysisPropertyNames);
|
||||
if (moduleManifestProperties != null)
|
||||
{
|
||||
if (ModuleIsEditionIncompatible(modulePath, moduleManifestProperties))
|
||||
if (!ExperimentalFeature.IsEnabled("PSWindowsPowerShellCompatibility") && ModuleIsEditionIncompatible(modulePath, moduleManifestProperties))
|
||||
{
|
||||
ModuleIntrinsics.Tracer.WriteLine($"Module lies on the Windows System32 legacy module path and is incompatible with current PowerShell edition, skipping module: {modulePath}");
|
||||
return null;
|
||||
@@ -493,7 +493,7 @@ namespace System.Management.Automation
|
||||
|
||||
// Don't cache incompatible modules on the system32 module path even if loaded with
|
||||
// -SkipEditionCheck, since it will break subsequent sessions.
|
||||
if (!module.IsConsideredEditionCompatible)
|
||||
if (!ExperimentalFeature.IsEnabled("PSWindowsPowerShellCompatibility") && !module.IsConsideredEditionCompatible)
|
||||
{
|
||||
ModuleIntrinsics.Tracer.WriteLine($"Module '{module.Name}' not edition compatible and not cached.");
|
||||
return;
|
||||
|
||||
@@ -51,6 +51,9 @@ namespace Microsoft.PowerShell.Commands
|
||||
private const string ParameterSet_ViaPsrpSession = "PSSession";
|
||||
private const string ParameterSet_ViaCimSession = "CimSession";
|
||||
private const string ParameterSet_FQName_ViaPsrpSession = "FullyQualifiedNameAndPSSession";
|
||||
private const string ParameterSet_ViaWinCompat = "WinCompat";
|
||||
private const string ParameterSet_FQName_ViaWinCompat = "FullyQualifiedNameAndWinCompat";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This parameter specifies whether to import to the current session state
|
||||
@@ -82,6 +85,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
[Parameter(ParameterSetName = ParameterSet_Name, Mandatory = true, ValueFromPipeline = true, Position = 0)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaPsrpSession, Mandatory = true, ValueFromPipeline = true, Position = 0)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaCimSession, Mandatory = true, ValueFromPipeline = true, Position = 0)]
|
||||
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat, Mandatory = true, ValueFromPipeline = true, Position = 0)]
|
||||
[ValidateTrustedData]
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]
|
||||
public string[] Name { set; get; } = Array.Empty<string>();
|
||||
@@ -91,6 +95,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
/// </summary>
|
||||
[Parameter(ParameterSetName = ParameterSet_FQName, Mandatory = true, ValueFromPipeline = true, Position = 0)]
|
||||
[Parameter(ParameterSetName = ParameterSet_FQName_ViaPsrpSession, Mandatory = true, ValueFromPipeline = true, Position = 0)]
|
||||
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_FQName_ViaWinCompat, Mandatory = true, ValueFromPipeline = true, Position = 0)]
|
||||
[ValidateTrustedData]
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]
|
||||
public ModuleSpecification[] FullyQualifiedName { get; set; }
|
||||
@@ -226,8 +231,15 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
/// <summary>
|
||||
/// Skips the check on CompatiblePSEditions for modules loaded from the System32 module path.
|
||||
/// This is mutually exclusive with UseWindowsPowerShell parameter.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[Parameter(ParameterSetName = ParameterSet_Name)]
|
||||
[Parameter(ParameterSetName = ParameterSet_FQName)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ModuleInfo)]
|
||||
[Parameter(ParameterSetName = ParameterSet_Assembly)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaCimSession)]
|
||||
[Parameter(ParameterSetName = ParameterSet_FQName_ViaPsrpSession)]
|
||||
public SwitchParameter SkipEditionCheck
|
||||
{
|
||||
get { return (SwitchParameter)BaseSkipEditionCheck; }
|
||||
@@ -263,6 +275,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
[Parameter(ParameterSetName = ParameterSet_Name)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaCimSession)]
|
||||
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat)]
|
||||
[Alias("Version")]
|
||||
public Version MinimumVersion
|
||||
{
|
||||
@@ -277,6 +290,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
[Parameter(ParameterSetName = ParameterSet_Name)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaCimSession)]
|
||||
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat)]
|
||||
public string MaximumVersion
|
||||
{
|
||||
get
|
||||
@@ -306,6 +320,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
[Parameter(ParameterSetName = ParameterSet_Name)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]
|
||||
[Parameter(ParameterSetName = ParameterSet_ViaCimSession)]
|
||||
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat)]
|
||||
public Version RequiredVersion
|
||||
{
|
||||
get { return BaseRequiredVersion; }
|
||||
@@ -406,6 +421,15 @@ namespace Microsoft.PowerShell.Commands
|
||||
[ValidateNotNullOrEmpty]
|
||||
public string CimNamespace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This parameter causes a module to be loaded into Windows PowerShell.
|
||||
/// This is mutually exclusive with SkipEditionCheck parameter.
|
||||
/// </summary>
|
||||
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat, Mandatory = true)]
|
||||
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_FQName_ViaWinCompat, Mandatory = true)]
|
||||
[Alias("UseWinPS")]
|
||||
public SwitchParameter UseWindowsPowerShell { get; set; }
|
||||
|
||||
#endregion Cmdlet parameters
|
||||
|
||||
#region Local import
|
||||
@@ -805,7 +829,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
ImportModuleOptions importModuleOptions,
|
||||
IEnumerable<string> moduleNames,
|
||||
IEnumerable<ModuleSpecification> fullyQualifiedNames,
|
||||
PSSession psSession)
|
||||
PSSession psSession,
|
||||
bool usingWinCompat = false)
|
||||
{
|
||||
var remotelyImportedModules = new List<PSModuleInfo>();
|
||||
if (moduleNames != null)
|
||||
@@ -829,7 +854,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
// Send telemetry on the imported modules
|
||||
foreach (PSModuleInfo moduleInfo in remotelyImportedModules)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, moduleInfo.Name);
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(usingWinCompat ? TelemetryType.WinCompatModuleLoad : TelemetryType.ModuleLoad, moduleInfo.Name);
|
||||
}
|
||||
|
||||
return remotelyImportedModules;
|
||||
@@ -1795,11 +1820,15 @@ namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
SetModuleBaseForEngineModules(foundModule.Name, this.Context);
|
||||
|
||||
// Telemetry here - report module load
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name);
|
||||
// report loading of the module in telemetry
|
||||
// avoid double reporting for WinCompat modules that go through CommandDiscovery\AutoloadSpecifiedModule
|
||||
if (!foundModule.IsWindowsPowerShellCompatModule)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name);
|
||||
#if LEGACYTELEMETRY
|
||||
TelemetryAPI.ReportModuleLoad(foundModule);
|
||||
TelemetryAPI.ReportModuleLoad(foundModule);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1836,6 +1865,25 @@ namespace Microsoft.PowerShell.Commands
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, modulespec.Name);
|
||||
}
|
||||
}
|
||||
else if (this.ParameterSetName.Equals(ParameterSet_ViaWinCompat, StringComparison.OrdinalIgnoreCase)
|
||||
|| this.ParameterSetName.Equals(ParameterSet_FQName_ViaWinCompat, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (this.UseWindowsPowerShell)
|
||||
{
|
||||
var WindowsPowerShellCompatRemotingSession = CreateWindowsPowerShellCompatResources();
|
||||
if (WindowsPowerShellCompatRemotingSession != null)
|
||||
{
|
||||
foreach(PSModuleInfo moduleProxy in ImportModule_RemotelyViaPsrpSession(importModuleOptions, this.Name, this.FullyQualifiedName, WindowsPowerShellCompatRemotingSession, true))
|
||||
{
|
||||
moduleProxy.IsWindowsPowerShellCompatModule = true;
|
||||
System.Threading.Interlocked.Increment(ref s_WindowsPowerShellCompatUsageCounter);
|
||||
|
||||
string message = StringUtil.Format(Modules.WinCompatModuleWarning, moduleProxy.Name, WindowsPowerShellCompatRemotingSession.Name);
|
||||
WriteWarning(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Dbg.Assert(false, "Unrecognized parameter set");
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Management.Automation.Security;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Microsoft.PowerShell.Cmdletization;
|
||||
|
||||
@@ -283,6 +284,21 @@ namespace Microsoft.PowerShell.Commands
|
||||
"Desktop"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A counter for modules that are loaded using WindowsPS compat session.
|
||||
/// </summary>
|
||||
internal static int s_WindowsPowerShellCompatUsageCounter = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Session name for WindowsPS compat remoting session.
|
||||
/// </summary>
|
||||
internal const string WindowsPowerShellCompatRemotingSessionName = "WinPSCompatSession";
|
||||
|
||||
/// <summary>
|
||||
/// Synchronization object for creation/cleanup of WindowsPS compat remoting session.
|
||||
/// </summary>
|
||||
internal static object s_WindowsPowerShellCompatSyncObject = new object();
|
||||
|
||||
private Dictionary<string, PSModuleInfo> _currentlyProcessingModules = new Dictionary<string, PSModuleInfo>();
|
||||
|
||||
internal bool LoadUsingModulePath(bool found, IEnumerable<string> modulePath, string name, SessionState ss,
|
||||
@@ -2347,43 +2363,70 @@ namespace Microsoft.PowerShell.Commands
|
||||
bool isConsideredCompatible = ModuleUtils.IsPSEditionCompatible(moduleManifestPath, inferredCompatiblePSEditions);
|
||||
if (!BaseSkipEditionCheck && !isConsideredCompatible)
|
||||
{
|
||||
containedErrors = true;
|
||||
|
||||
if (writingErrors)
|
||||
if (ExperimentalFeature.IsEnabled("PSWindowsPowerShellCompatibility"))
|
||||
{
|
||||
message = StringUtil.Format(
|
||||
Modules.PSEditionNotSupported,
|
||||
moduleManifestPath,
|
||||
PSVersionInfo.PSEdition,
|
||||
string.Join(',', inferredCompatiblePSEditions));
|
||||
|
||||
InvalidOperationException ioe = new InvalidOperationException(message);
|
||||
|
||||
ErrorRecord er = new ErrorRecord(
|
||||
ioe,
|
||||
nameof(Modules) + "_" + nameof(Modules.PSEditionNotSupported),
|
||||
ErrorCategory.ResourceUnavailable,
|
||||
moduleManifestPath);
|
||||
|
||||
WriteError(er);
|
||||
}
|
||||
|
||||
if (bailOnFirstError)
|
||||
{
|
||||
// If we're trying to load the module, return null so that caches
|
||||
// are not polluted
|
||||
if (importingModule)
|
||||
{
|
||||
return null;
|
||||
var commandInfo = new CmdletInfo("Import-Module", typeof(ImportModuleCommand));
|
||||
using var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
|
||||
ps.AddCommand(commandInfo);
|
||||
ps.AddParameter("PassThru", true);
|
||||
ps.AddParameter("Name", moduleManifestPath);
|
||||
ps.AddParameter("UseWindowsPowerShell", true);
|
||||
|
||||
var moduleProxies = ps.Invoke<PSModuleInfo>();
|
||||
|
||||
// we are loading by a single ManifestPath so expect max of 1
|
||||
if(moduleProxies.Count > 0)
|
||||
{
|
||||
return moduleProxies[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
containedErrors = true;
|
||||
|
||||
if (writingErrors)
|
||||
{
|
||||
message = StringUtil.Format(
|
||||
Modules.PSEditionNotSupported,
|
||||
moduleManifestPath,
|
||||
PSVersionInfo.PSEdition,
|
||||
string.Join(',', inferredCompatiblePSEditions));
|
||||
|
||||
InvalidOperationException ioe = new InvalidOperationException(message);
|
||||
|
||||
ErrorRecord er = new ErrorRecord(
|
||||
ioe,
|
||||
nameof(Modules) + "_" + nameof(Modules.PSEditionNotSupported),
|
||||
ErrorCategory.ResourceUnavailable,
|
||||
moduleManifestPath);
|
||||
|
||||
WriteError(er);
|
||||
}
|
||||
|
||||
// If we return null with Get-Module, a fake module info will be created. Since
|
||||
// we want to suppress output of the module, we need to do that here.
|
||||
return new PSModuleInfo(moduleManifestPath, context: null, sessionState: null)
|
||||
if (bailOnFirstError)
|
||||
{
|
||||
HadErrorsLoading = true,
|
||||
IsConsideredEditionCompatible = false,
|
||||
};
|
||||
// If we're trying to load the module, return null so that caches
|
||||
// are not polluted
|
||||
if (importingModule)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we return null with Get-Module, a fake module info will be created. Since
|
||||
// we want to suppress output of the module, we need to do that here.
|
||||
return new PSModuleInfo(moduleManifestPath, context: null, sessionState: null)
|
||||
{
|
||||
HadErrorsLoading = true,
|
||||
IsConsideredEditionCompatible = false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4768,6 +4811,62 @@ namespace Microsoft.PowerShell.Commands
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
internal PSSession GetWindowsPowerShellCompatRemotingSession()
|
||||
{
|
||||
PSSession result = null;
|
||||
var commandInfo = new CmdletInfo("Get-PSSession", typeof(GetPSSessionCommand));
|
||||
using var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
|
||||
ps.AddCommand(commandInfo);
|
||||
ps.AddParameter("Name", WindowsPowerShellCompatRemotingSessionName);
|
||||
var results = ps.Invoke<PSSession>();
|
||||
if (results.Count > 0)
|
||||
{
|
||||
result = results[0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal PSSession CreateWindowsPowerShellCompatResources()
|
||||
{
|
||||
PSSession compatSession = null;
|
||||
lock(s_WindowsPowerShellCompatSyncObject)
|
||||
{
|
||||
compatSession = GetWindowsPowerShellCompatRemotingSession();
|
||||
if (compatSession == null)
|
||||
{
|
||||
var commandInfo = new CmdletInfo("New-PSSession", typeof(NewPSSessionCommand));
|
||||
using var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
|
||||
ps.AddCommand(commandInfo);
|
||||
ps.AddParameter("UseWindowsPowerShell", true);
|
||||
ps.AddParameter("Name", WindowsPowerShellCompatRemotingSessionName);
|
||||
var results = ps.Invoke<PSSession>();
|
||||
if (results.Count > 0)
|
||||
{
|
||||
compatSession = results[0];
|
||||
System.Threading.Interlocked.Exchange(ref s_WindowsPowerShellCompatUsageCounter, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return compatSession;
|
||||
}
|
||||
|
||||
internal void CleanupWindowsPowerShellCompatResources()
|
||||
{
|
||||
lock(s_WindowsPowerShellCompatSyncObject)
|
||||
{
|
||||
var compatSession = GetWindowsPowerShellCompatRemotingSession();
|
||||
if (compatSession != null)
|
||||
{
|
||||
var commandInfo = new CmdletInfo("Remove-PSSession", typeof(RemovePSSessionCommand));
|
||||
using var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
|
||||
ps.AddCommand(commandInfo);
|
||||
ps.AddParameter("Session", compatSession);
|
||||
ps.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveTypesAndFormatting(
|
||||
IList<string> formatFilesToRemove,
|
||||
IList<string> typeFilesToRemove)
|
||||
@@ -4860,6 +4959,11 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
}
|
||||
|
||||
if (ExperimentalFeature.IsEnabled("PSWindowsPowerShellCompatibility") && module.IsWindowsPowerShellCompatModule && (System.Threading.Interlocked.Decrement(ref s_WindowsPowerShellCompatUsageCounter) == 0))
|
||||
{
|
||||
CleanupWindowsPowerShellCompatResources();
|
||||
}
|
||||
|
||||
// First remove cmdlets from the session state
|
||||
// (can't just go through module.ExportedCmdlets
|
||||
// because the names of the cmdlets might have been changed by the -Prefix parameter of Import-Module)
|
||||
|
||||
@@ -282,6 +282,8 @@ namespace System.Management.Automation
|
||||
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")]
|
||||
public string HelpInfoUri { get; private set; }
|
||||
|
||||
internal bool IsWindowsPowerShellCompatModule { get; set; }
|
||||
|
||||
internal void SetHelpInfoUri(string uri)
|
||||
{
|
||||
HelpInfoUri = uri;
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace System.Management.Automation.Runspaces
|
||||
private bool _processExited;
|
||||
|
||||
internal static readonly string PwshExePath;
|
||||
internal static readonly string WinPwshExePath;
|
||||
|
||||
#endregion Fields
|
||||
|
||||
@@ -37,6 +38,7 @@ namespace System.Management.Automation.Runspaces
|
||||
PwshExePath = Path.Combine(Utils.DefaultPowerShellAppBase, "pwsh");
|
||||
#else
|
||||
PwshExePath = Path.Combine(Utils.DefaultPowerShellAppBase, "pwsh.exe");
|
||||
WinPwshExePath = Path.Combine(Utils.GetApplicationBaseFromRegistry(Utils.DefaultPowerShellShellID), "powershell.exe");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -50,11 +52,38 @@ namespace System.Management.Automation.Runspaces
|
||||
/// <param name="workingDirectory">Specifies the initial working directory for the new powershell process.</param>
|
||||
public PowerShellProcessInstance(Version powerShellVersion, PSCredential credential, ScriptBlock initializationScript, bool useWow64, string workingDirectory)
|
||||
{
|
||||
string exePath = PwshExePath;
|
||||
bool startingWindowsPowerShell51 = false;
|
||||
#if !UNIX
|
||||
// if requested PS version was "5.1" then we start Windows PS instead of PS Core
|
||||
startingWindowsPowerShell51 = (powerShellVersion != null) && (powerShellVersion.Major == 5) && (powerShellVersion.Minor == 1);
|
||||
if (startingWindowsPowerShell51)
|
||||
{
|
||||
exePath = WinPwshExePath;
|
||||
|
||||
if (useWow64)
|
||||
{
|
||||
string procArch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
|
||||
|
||||
if ((!string.IsNullOrEmpty(procArch)) && (procArch.Equals("amd64", StringComparison.OrdinalIgnoreCase) ||
|
||||
procArch.Equals("ia64", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
exePath = WinPwshExePath.ToLowerInvariant().Replace("\\system32\\", "\\syswow64\\");
|
||||
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
string message = PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.WowComponentNotPresent, exePath);
|
||||
throw new PSInvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// 'WindowStyle' is used only if 'UseShellExecute' is 'true'. Since 'UseShellExecute' is set
|
||||
// to 'false' in our use, we can ignore the 'WindowStyle' setting in the initialization below.
|
||||
_startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = PwshExePath,
|
||||
FileName = exePath,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
@@ -65,11 +94,17 @@ namespace System.Management.Automation.Runspaces
|
||||
#endif
|
||||
};
|
||||
|
||||
if (startingWindowsPowerShell51)
|
||||
{
|
||||
_startInfo.ArgumentList.Add("-Version");
|
||||
_startInfo.ArgumentList.Add("5.1");
|
||||
}
|
||||
|
||||
_startInfo.ArgumentList.Add("-s");
|
||||
_startInfo.ArgumentList.Add("-NoLogo");
|
||||
_startInfo.ArgumentList.Add("-NoProfile");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(workingDirectory))
|
||||
if (!string.IsNullOrWhiteSpace(workingDirectory) && !startingWindowsPowerShell51)
|
||||
{
|
||||
_startInfo.ArgumentList.Add("-wd");
|
||||
_startInfo.ArgumentList.Add(workingDirectory);
|
||||
|
||||
@@ -297,6 +297,10 @@ namespace System.Management.Automation.Runspaces
|
||||
ConfigurationName = "DefaultShell";
|
||||
break;
|
||||
|
||||
case NewProcessConnectionInfo _:
|
||||
ComputerType = TargetMachineType.RemoteMachine;
|
||||
break;
|
||||
|
||||
default:
|
||||
Dbg.Assert(false, "Invalid Runspace");
|
||||
break;
|
||||
|
||||
@@ -4010,6 +4010,11 @@ Set-PSSessionConfiguration $args[0] $args[1] $args[2] $args[3] $args[4] $args[5]
|
||||
|
||||
function Test-WinRMQuickConfigNeeded
|
||||
{{
|
||||
# see issue #11005 - Function Test-WinRMQuickConfigNeeded needs to be updated:
|
||||
# 1) currently this function always returns $True
|
||||
# 2) checking for a firewall rule using Get-NetFirewallRule engages WinCompat code and has significant perf impact on Enable-PSRemoting; maybe change to Get-CimInstance -ClassName MSFT_NetFirewallRule
|
||||
return $True
|
||||
|
||||
# Checking the following items
|
||||
#1. Starting or restarting (if already started) the WinRM service
|
||||
#2. Setting the WinRM service startup type to Automatic
|
||||
|
||||
@@ -194,6 +194,11 @@ namespace Microsoft.PowerShell.Commands
|
||||
/// </summary>
|
||||
protected const string SessionParameterSet = "Session";
|
||||
|
||||
/// <summary>
|
||||
/// Parameter set to use Windows PowerShell.
|
||||
/// </summary>
|
||||
protected const string UseWindowsPowerShellParameterSet = "UseWindowsPowerShellParameterSet";
|
||||
|
||||
/// <summary>
|
||||
/// Default shellname.
|
||||
/// </summary>
|
||||
|
||||
@@ -161,6 +161,13 @@ namespace Microsoft.PowerShell.Commands
|
||||
[Parameter(ValueFromPipelineByPropertyName = true,
|
||||
ParameterSetName = NewPSSessionCommand.VMNameParameterSet)]
|
||||
public string ConfigurationName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets parameter value that creates connection to a Windows PowerShell process.
|
||||
/// </summary>
|
||||
[Experimental("PSWindowsPowerShellCompatibility", ExperimentAction.Show)]
|
||||
[Parameter(Mandatory = true, ParameterSetName = NewPSSessionCommand.UseWindowsPowerShellParameterSet)]
|
||||
public SwitchParameter UseWindowsPowerShell { get; set; }
|
||||
|
||||
#endregion Parameters
|
||||
|
||||
@@ -257,6 +264,13 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
break;
|
||||
|
||||
case NewPSSessionCommand.UseWindowsPowerShellParameterSet:
|
||||
{
|
||||
remoteRunspaces = CreateRunspacesForUseWindowsPowerShellParameterSet();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
Dbg.Assert(false, "Missing parameter set in switch statement");
|
||||
@@ -1136,6 +1150,28 @@ namespace Microsoft.PowerShell.Commands
|
||||
return remoteRunspaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create remote runspace based on UseWindowsPowerShell parameter set.
|
||||
/// </summary>
|
||||
/// <returns>Remote runspace that was created.</returns>
|
||||
private List<RemoteRunspace> CreateRunspacesForUseWindowsPowerShellParameterSet()
|
||||
{
|
||||
var remoteRunspaces = new List<RemoteRunspace>();
|
||||
|
||||
NewProcessConnectionInfo connectionInfo = new NewProcessConnectionInfo(this.Credential);
|
||||
connectionInfo.AuthenticationMechanism = this.Authentication;
|
||||
connectionInfo.PSVersion = new Version(5, 1);
|
||||
|
||||
var typeTable = TypeTable.LoadDefaultTypeFiles();
|
||||
string runspaceName = GetRunspaceName(0, out int runspaceIdUnused);
|
||||
remoteRunspaces.Add(RunspaceFactory.CreateRunspace(connectionInfo: connectionInfo,
|
||||
host: this.Host,
|
||||
typeTable: typeTable,
|
||||
applicationArguments: null,
|
||||
name: runspaceName) as RemoteRunspace);
|
||||
return remoteRunspaces;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to either get a user supplied runspace/session name
|
||||
/// or to generate one along with a unique Id.
|
||||
|
||||
@@ -510,6 +510,9 @@
|
||||
<data name="MixedModuleOverCimSessionWarning" xml:space="preserve">
|
||||
<value>Some commands from module {0} cannot be imported over a CimSession. To get all the commands, verify that the remote server has PowerShell remote management enabled, and then try adding the PSSession parameter to an Import-Module cmdlet.</value>
|
||||
</data>
|
||||
<data name="WinCompatModuleWarning" xml:space="preserve">
|
||||
<value>Module {0} is loaded in Windows PowerShell using {1} remoting session; please note that all input and output of commands from this module will be deserialized objects. If you want to load this module into PowerShell Core please use 'Import-Module -SkipEditionCheck' syntax.</value>
|
||||
</data>
|
||||
<data name="PsModuleOverCimSessionError" xml:space="preserve">
|
||||
<value>The module {0} cannot be imported over a CimSession. Try using the PSSession parameter of the Import-Module cmdlet.</value>
|
||||
</data>
|
||||
|
||||
@@ -1655,6 +1655,9 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro
|
||||
<data name="InvalidProcessorArchitecture" xml:space="preserve">
|
||||
<value>PowerShell 6+ does not support WOW64. The binary must match the architecture of the processor.</value>
|
||||
</data>
|
||||
<data name="WowComponentNotPresent" xml:space="preserve">
|
||||
<value>The "{0}" executable file was not found. Verify that the WOW64 feature is installed.</value>
|
||||
</data>
|
||||
<data name="UnableToInstallPlugin" xml:space="preserve">
|
||||
<value>Unable to install plugin {0} to directory {1}.</value>
|
||||
</data>
|
||||
|
||||
@@ -28,6 +28,12 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
/// will be reported, otherwise it will be "anonymous".
|
||||
/// </summary>
|
||||
ModuleLoad,
|
||||
|
||||
/// <summary>
|
||||
/// Send telemetry when we load a module using Windows compatibility feature, only module names in the s_knownModules list
|
||||
/// will be reported, otherwise it will be "anonymous".
|
||||
/// </summary>
|
||||
WinCompatModuleLoad,
|
||||
|
||||
/// <summary>
|
||||
/// Send telemetry for experimental module feature activation.
|
||||
@@ -159,6 +165,7 @@ namespace Microsoft.PowerShell.Telemetry
|
||||
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, experimentalFeatureName);
|
||||
break;
|
||||
case TelemetryType.ModuleLoad:
|
||||
case TelemetryType.WinCompatModuleLoad:
|
||||
string moduleName = GetModuleName(data); // This will return anonymous if the modulename is not on the report list
|
||||
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, moduleName);
|
||||
break;
|
||||
|
||||
+143
-26
@@ -41,7 +41,7 @@ function New-EditionCompatibleModule
|
||||
|
||||
New-Item -Path $modulePath -ItemType Directory
|
||||
|
||||
New-Item -Path $psm1Path -Value "function Test-$ModuleName { `$true }"
|
||||
New-Item -Path $psm1Path -Value "function Test-$ModuleName { `$true } function Test-${ModuleName}PSEdition { `$PSVersionTable.PSEdition }" -Force
|
||||
|
||||
if ($CompatiblePSEditions)
|
||||
{
|
||||
@@ -117,11 +117,11 @@ function New-TestNestedModule
|
||||
if ($UseRootModule)
|
||||
{
|
||||
$newManifestCmd += "-RootModule $RootModuleFilename "
|
||||
$newManifestCmd += "-FunctionsToExport @('Test-RootModule') "
|
||||
$newManifestCmd += "-FunctionsToExport @('Test-RootModule','Test-RootModulePSEdition') "
|
||||
}
|
||||
else
|
||||
{
|
||||
$newManifestCmd += "-FunctionsToExport @('Test-ScriptModule') "
|
||||
$newManifestCmd += "-FunctionsToExport @('Test-ScriptModule','Test-ScriptModulePSEdition') "
|
||||
}
|
||||
|
||||
$newManifestCmd += "-CmdletsToExport @() -VariablesToExport @() -AliasesToExport @() "
|
||||
@@ -248,6 +248,10 @@ Describe "Import-Module from CompatiblePSEditions-checked paths" -Tag "CI" {
|
||||
|
||||
$allModules = ($successCases + $failCases).ModuleName
|
||||
|
||||
# make sure there are no ImplicitRemoting leftovers from previous tests
|
||||
Get-Module | Where-Object {$_.PrivateData.ImplicitRemoting} | Remove-Module -Force
|
||||
Get-PSSession -Name WinPSCompatSession -ErrorAction SilentlyContinue | Remove-PSSession
|
||||
|
||||
# Emulate the System32 module path for tests
|
||||
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("TestWindowsPowerShellPSHomeLocation", $basePath)
|
||||
}
|
||||
@@ -276,12 +280,11 @@ Describe "Import-Module from CompatiblePSEditions-checked paths" -Tag "CI" {
|
||||
& "Test-$ModuleName" | Should -Be $Result
|
||||
}
|
||||
|
||||
It "Fails to import incompatible modules from the module path with PSEdition <Editions>" -TestCases $failCases -Skip:(-not $IsWindows) {
|
||||
It "Successfully imports incompatible modules from the module path with PSEdition <Editions> using WinCompat" -TestCases $failCases -Skip:(-not $IsWindows) {
|
||||
param($Editions, $ModuleName, $Result)
|
||||
|
||||
{
|
||||
Import-Module $ModuleName -Force -ErrorAction 'Stop'; & "Test-$ModuleName"
|
||||
} | Should -Throw -ErrorId "Modules_PSEditionNotSupported,Microsoft.PowerShell.Commands.ImportModuleCommand"
|
||||
Import-Module $ModuleName -Force -ErrorAction 'Stop'
|
||||
& "Test-$ModuleName" | Should -Be $Result
|
||||
}
|
||||
|
||||
It "Imports an incompatible module from the module path with -SkipEditionCheck with PSEdition <Editions>" -TestCases ($successCases + $failCases) -Skip:(-not $IsWindows) {
|
||||
@@ -290,6 +293,13 @@ Describe "Import-Module from CompatiblePSEditions-checked paths" -Tag "CI" {
|
||||
Import-Module $ModuleName -SkipEditionCheck -Force
|
||||
& "Test-$ModuleName" | Should -Be $Result
|
||||
}
|
||||
|
||||
It "Imports any module using WinCompat from the module path with -UseWindowsPowerShell with PSEdition <Editions>" -TestCases ($successCases + $failCases) -Skip:(-not $IsWindows) {
|
||||
param($Editions, $ModuleName, $Result)
|
||||
|
||||
Import-Module $ModuleName -UseWindowsPowerShell -Force
|
||||
& "Test-${ModuleName}PSEdition" | Should -Be 'Desktop'
|
||||
}
|
||||
}
|
||||
|
||||
Context "Imports from absolute path" {
|
||||
@@ -302,14 +312,13 @@ Describe "Import-Module from CompatiblePSEditions-checked paths" -Tag "CI" {
|
||||
& "Test-$ModuleName" | Should -Be $Result
|
||||
}
|
||||
|
||||
It "Fails to import incompatible modules from an absolute path with PSEdition <Editions>" -TestCases $failCases -Skip:(-not $IsWindows) {
|
||||
It "Successfully imports incompatible modules from an absolute path with PSEdition <Editions> using WinCompat" -TestCases $failCases -Skip:(-not $IsWindows) {
|
||||
param($Editions, $ModuleName, $Result)
|
||||
|
||||
$path = Join-Path -Path $basePath -ChildPath $ModuleName
|
||||
|
||||
{
|
||||
Import-Module $path -Force -ErrorAction 'Stop'; & "Test-$ModuleName"
|
||||
} | Should -Throw -ErrorId "Modules_PSEditionNotSupported,Microsoft.PowerShell.Commands.ImportModuleCommand"
|
||||
Import-Module $path -Force -ErrorAction 'Stop'
|
||||
& "Test-$ModuleName" | Should -Be $Result
|
||||
}
|
||||
|
||||
It "Imports an incompatible module from an absolute path with -SkipEditionCheck with PSEdition <Editions>" -TestCases ($successCases + $failCases) -Skip:(-not $IsWindows) {
|
||||
@@ -320,6 +329,37 @@ Describe "Import-Module from CompatiblePSEditions-checked paths" -Tag "CI" {
|
||||
Import-Module $path -SkipEditionCheck -Force
|
||||
& "Test-$ModuleName" | Should -Be $Result
|
||||
}
|
||||
|
||||
It "Imports any module using WinCompat from an absolute path with -UseWindowsPowerShell with PSEdition <Editions>" -TestCases ($successCases + $failCases) -Skip:(-not $IsWindows) {
|
||||
param($Editions, $ModuleName, $Result)
|
||||
|
||||
$path = Join-Path -Path $basePath -ChildPath $ModuleName
|
||||
|
||||
Import-Module $path -UseWindowsPowerShell -Force
|
||||
& "Test-${ModuleName}PSEdition" | Should -Be 'Desktop'
|
||||
}
|
||||
}
|
||||
|
||||
Context "Imports using CommandDiscovery\ModuleAutoload" {
|
||||
BeforeAll {
|
||||
Add-ModulePath $basePath
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
Restore-ModulePath
|
||||
}
|
||||
|
||||
It "Successfully auto-imports compatible modules from the module path with PSEdition <Editions>" -TestCases $successCases -Skip:(-not $IsWindows) {
|
||||
param($Editions, $ModuleName, $Result)
|
||||
|
||||
& "Test-$ModuleName" | Should -Be $Result
|
||||
}
|
||||
|
||||
It "Successfully auto-imports incompatible modules from the module path with PSEdition <Editions> using WinCompat" -TestCases $failCases -Skip:(-not $IsWindows) {
|
||||
param($Editions, $ModuleName, $Result)
|
||||
|
||||
& "Test-${ModuleName}PSEdition" | Should -Be 'Desktop'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,6 +713,7 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
UseRootModule = @($true, $false)
|
||||
UseAbsolutePath = @($true, $false)
|
||||
MarkedEdition = @($null, "Desktop", "Core", @("Desktop","Core"))
|
||||
UseWindowsPowerShell = @($true, $false)
|
||||
}
|
||||
|
||||
# Combine all the test conditions into a list of test cases
|
||||
@@ -693,12 +734,12 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
# Define nested script module
|
||||
$scriptModuleName = "NestedScriptModule"
|
||||
$scriptModuleFile = "$scriptModuleName.psm1"
|
||||
$scriptModuleContent = 'function Test-ScriptModule { return $true }'
|
||||
$scriptModuleContent = 'function Test-ScriptModule { return $true } function Test-ScriptModulePSEdition { $PSVersionTable.PSEdition }'
|
||||
|
||||
# Define root module definition
|
||||
$rootModuleName = "RootModule"
|
||||
$rootModuleFile = "$rootModuleName.psm1"
|
||||
$rootModuleContent = 'function Test-RootModule { Test-ScriptModule }'
|
||||
$rootModuleContent = 'function Test-RootModule { Test-ScriptModule } function Test-RootModulePSEdition { Test-ScriptModulePSEdition }'
|
||||
|
||||
# Module directory structure: $TestDrive/$compatibility/$guid/$moduleName/{module parts}
|
||||
$compatibleDir = "Compatible"
|
||||
@@ -710,6 +751,10 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
{
|
||||
New-Item -Path $basePath -ItemType Directory
|
||||
}
|
||||
|
||||
# make sure there are no ImplicitRemoting leftovers from previous tests
|
||||
Get-Module | Where-Object {$_.PrivateData.ImplicitRemoting} | Remove-Module -Force
|
||||
Get-PSSession -Name WinPSCompatSession -ErrorAction SilentlyContinue | Remove-PSSession
|
||||
}
|
||||
|
||||
Context "Modules ON the System32 test path" {
|
||||
@@ -737,8 +782,8 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
Restore-ModulePath
|
||||
}
|
||||
|
||||
It "Import-Module when SkipEditionCheck: <SkipEditionCheck>, using root module: <UseRootModule>, using absolute path: <UseAbsolutePath>, CompatiblePSEditions: <MarkedEdition>" -TestCases $testCases -Skip:(-not $IsWindows) {
|
||||
param([bool]$SkipEditionCheck, [bool]$UseRootModule, [bool]$UseAbsolutePath, [string[]]$MarkedEdition)
|
||||
It "Import-Module when SkipEditionCheck: <SkipEditionCheck>, using root module: <UseRootModule>, using absolute path: <UseAbsolutePath>, CompatiblePSEditions: <MarkedEdition>, UseWindowsPowerShell: <UseWindowsPowerShell>" -TestCases $testCases -Skip:(-not $IsWindows) {
|
||||
param([bool]$SkipEditionCheck, [bool]$UseRootModule, [bool]$UseAbsolutePath, [string[]]$MarkedEdition, [bool]$UseWindowsPowerShell)
|
||||
|
||||
New-TestNestedModule `
|
||||
-ModuleBase $moduleBase `
|
||||
@@ -754,16 +799,25 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
{
|
||||
if ((-not $SkipEditionCheck) -and (-not ($MarkedEdition -contains "Core")))
|
||||
{
|
||||
{
|
||||
Import-Module $moduleBase -ErrorAction Stop
|
||||
} | Should -Throw -ErrorId "Modules_PSEditionNotSupported,Microsoft.PowerShell.Commands.ImportModuleCommand"
|
||||
# this goes through WinCompat code
|
||||
{ Import-Module $moduleBase -ErrorAction Stop } | Should Not Throw
|
||||
Get-Module -Name $moduleName | Should Not BeNullOrEmpty
|
||||
return
|
||||
}
|
||||
|
||||
if ($SkipEditionCheck)
|
||||
if ($SkipEditionCheck -and $UseWindowsPowerShell)
|
||||
{
|
||||
{ Import-Module $moduleBase -SkipEditionCheck -UseWindowsPowerShell } | Should -Throw -ErrorId "AmbiguousParameterSet"
|
||||
return
|
||||
}
|
||||
elseif ($SkipEditionCheck)
|
||||
{
|
||||
Import-Module $moduleBase -SkipEditionCheck
|
||||
}
|
||||
elseif ($UseWindowsPowerShell)
|
||||
{
|
||||
Import-Module $moduleBase -UseWindowsPowerShell
|
||||
}
|
||||
else
|
||||
{
|
||||
Import-Module $moduleBase
|
||||
@@ -773,26 +827,46 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
{
|
||||
Test-RootModule | Should -BeTrue
|
||||
{ Test-ScriptModule } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
if ($UseWindowsPowerShell)
|
||||
{
|
||||
Test-RootModulePSEdition | Should -Be 'Desktop'
|
||||
{ Test-ScriptModulePSEdition } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Test-ScriptModule | Should -BeTrue
|
||||
{ Test-RootModule } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
if ($UseWindowsPowerShell)
|
||||
{
|
||||
Test-ScriptModulePSEdition | Should -Be 'Desktop'
|
||||
{ Test-RootModulePSEdition } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ((-not $SkipEditionCheck) -and (-not ($MarkedEdition -contains "Core")))
|
||||
{
|
||||
{
|
||||
Import-Module $moduleName -ErrorAction Stop
|
||||
} | Should -Throw -ErrorId "Modules_PSEditionNotSupported,Microsoft.PowerShell.Commands.ImportModuleCommand"
|
||||
# this goes through WinCompat code
|
||||
{ Import-Module $moduleName -ErrorAction Stop } | Should Not Throw
|
||||
Get-Module -Name $moduleName | Should Not BeNullOrEmpty
|
||||
return
|
||||
}
|
||||
|
||||
if ($SkipEditionCheck)
|
||||
|
||||
if ($SkipEditionCheck -and $UseWindowsPowerShell)
|
||||
{
|
||||
{ Import-Module $moduleName -SkipEditionCheck -UseWindowsPowerShell } | Should -Throw -ErrorId "AmbiguousParameterSet"
|
||||
return
|
||||
}
|
||||
elseif ($SkipEditionCheck)
|
||||
{
|
||||
Import-Module $moduleName -SkipEditionCheck
|
||||
}
|
||||
elseif ($UseWindowsPowerShell)
|
||||
{
|
||||
Import-Module $moduleName -UseWindowsPowerShell
|
||||
}
|
||||
else
|
||||
{
|
||||
Import-Module $moduleName
|
||||
@@ -802,11 +876,21 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
{
|
||||
Test-RootModule | Should -BeTrue
|
||||
{ Test-ScriptModule } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
if ($UseWindowsPowerShell)
|
||||
{
|
||||
Test-RootModulePSEdition | Should -Be 'Desktop'
|
||||
{ Test-ScriptModulePSEdition } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Test-ScriptModule | Should -BeTrue
|
||||
{ Test-RootModule } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
if ($UseWindowsPowerShell)
|
||||
{
|
||||
Test-ScriptModulePSEdition | Should -Be 'Desktop'
|
||||
{ Test-RootModulePSEdition } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -827,8 +911,13 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
Restore-ModulePath
|
||||
}
|
||||
|
||||
It "Import-Module when SkipEditionCheck: <SkipEditionCheck>, using root module: <UseRootModule>, using absolute path: <UseAbsolutePath>, CompatiblePSEditions: <MarkedEdition>" -TestCases $testCases {
|
||||
param([bool]$SkipEditionCheck, [bool]$UseRootModule, [bool]$UseAbsolutePath, [string[]]$MarkedEdition)
|
||||
It "Import-Module when SkipEditionCheck: <SkipEditionCheck>, using root module: <UseRootModule>, using absolute path: <UseAbsolutePath>, CompatiblePSEditions: <MarkedEdition>, UseWindowsPowerShell: <UseWindowsPowerShell>" -TestCases $testCases {
|
||||
param([bool]$SkipEditionCheck, [bool]$UseRootModule, [bool]$UseAbsolutePath, [string[]]$MarkedEdition, [bool]$UseWindowsPowerShell)
|
||||
|
||||
if ($UseWindowsPowerShell -and (-not $IsWindows))
|
||||
{
|
||||
Set-ItResult -Skipped -Because 'UseWindowsPowerShell parameter is supported only on Windows'
|
||||
}
|
||||
|
||||
New-TestNestedModule `
|
||||
-ModuleBase $moduleBase `
|
||||
@@ -842,19 +931,37 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
|
||||
if ($UseAbsolutePath)
|
||||
{
|
||||
if ($SkipEditionCheck)
|
||||
if ($SkipEditionCheck -and $UseWindowsPowerShell)
|
||||
{
|
||||
{ Import-Module $moduleBase -SkipEditionCheck -UseWindowsPowerShell } | Should -Throw -ErrorId "AmbiguousParameterSet"
|
||||
return
|
||||
}
|
||||
elseif ($SkipEditionCheck)
|
||||
{
|
||||
Import-Module $moduleBase -SkipEditionCheck
|
||||
}
|
||||
elseif ($UseWindowsPowerShell)
|
||||
{
|
||||
Import-Module $moduleBase -UseWindowsPowerShell
|
||||
}
|
||||
else
|
||||
{
|
||||
Import-Module $moduleBase
|
||||
}
|
||||
}
|
||||
elseif ($SkipEditionCheck -and $UseWindowsPowerShell)
|
||||
{
|
||||
{ Import-Module $moduleName -SkipEditionCheck -UseWindowsPowerShell } | Should -Throw -ErrorId "AmbiguousParameterSet"
|
||||
return
|
||||
}
|
||||
elseif ($SkipEditionCheck)
|
||||
{
|
||||
Import-Module $moduleName -SkipEditionCheck
|
||||
}
|
||||
elseif ($UseWindowsPowerShell)
|
||||
{
|
||||
Import-Module $moduleName -UseWindowsPowerShell
|
||||
}
|
||||
else
|
||||
{
|
||||
Import-Module $moduleName
|
||||
@@ -864,11 +971,21 @@ Describe "Import-Module nested module behaviour with Edition checking" -Tag "Fea
|
||||
{
|
||||
Test-RootModule | Should -BeTrue
|
||||
{ Test-ScriptModule } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
if ($UseWindowsPowerShell)
|
||||
{
|
||||
Test-RootModulePSEdition | Should -Be 'Desktop'
|
||||
{ Test-ScriptModulePSEdition } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Test-ScriptModule | Should -BeTrue
|
||||
{ Test-RootModule } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
if ($UseWindowsPowerShell)
|
||||
{
|
||||
Test-ScriptModulePSEdition | Should -Be 'Desktop'
|
||||
{ Test-RootModulePSEdition } | Should -Throw -ErrorId "CommandNotFoundException"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +97,9 @@ Describe "Enter-PSHostProcess tests" -Tag Feature {
|
||||
}
|
||||
|
||||
It "Can enter, exit, and re-enter another Windows PowerShell PSHost" -Skip:(!$IsWindows) {
|
||||
# Start a Windows PowerShell job where the first thing it does is return $pid. After that, spin forever.
|
||||
# Start a PowerShell job where the first thing it does is return $pid. After that, spin forever.
|
||||
# We will use this job as the target process for Enter-PSHostProcess
|
||||
$powershellJob = Start-Job -PSVersion 5.1 {
|
||||
$powershellJob = Start-Job {
|
||||
$pid
|
||||
while ($true) {
|
||||
Start-Sleep -Seconds 30 | Out-Null
|
||||
|
||||
@@ -92,7 +92,16 @@ Describe "Get-Process" -Tags "CI" {
|
||||
}
|
||||
|
||||
Describe "Get-Process Formatting" -Tags "Feature" {
|
||||
It "Should not have Handle in table format header" {
|
||||
BeforeAll {
|
||||
$skip = $false
|
||||
if ($IsWindows)
|
||||
{
|
||||
# on Windows skip this test until issue #11016 is resolved
|
||||
$skip = $true
|
||||
}
|
||||
}
|
||||
|
||||
It "Should not have Handle in table format header" -Skip:$skip {
|
||||
$types = "System.Diagnostics.Process","System.Diagnostics.Process#IncludeUserName"
|
||||
|
||||
foreach ($type in $types) {
|
||||
|
||||
@@ -754,7 +754,7 @@ try
|
||||
}
|
||||
|
||||
It "Helper functions should not be imported" {
|
||||
(Get-Item function:*PSImplicitRemoting* -ErrorAction SilentlyContinue) | Should -BeNullOrEmpty
|
||||
Get-Item function:*PSImplicitRemoting* -ErrorAction SilentlyContinue | Where-Object {$_.ModuleName -eq $module.Name} | Should -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It "Calls implicit remoting proxies 'MyFunction'" {
|
||||
@@ -813,7 +813,7 @@ try
|
||||
}
|
||||
|
||||
It "Private functions from the implicit remoting module shouldn't get imported into global scope" {
|
||||
@(Get-ChildItem function:*Implicit* -ErrorAction SilentlyContinue).Count | Should -Be 0
|
||||
@(Get-ChildItem function:*Implicit* -ErrorAction SilentlyContinue | Where-Object {$_.ModuleName -eq $module.Name}).Count | Should -Be 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1870,7 +1870,7 @@ try
|
||||
$oldNumberOfHandlers | Should -Be $newNumberOfHandlers
|
||||
|
||||
## Private functions from the implicit remoting module shouldn't get imported into global scope
|
||||
@(Get-ChildItem function:*Implicit* -ErrorAction SilentlyContinue).Count | Should -Be 0
|
||||
@(Get-ChildItem function:*Implicit* -ErrorAction SilentlyContinue | Where-Object {$_.ModuleName -eq $module.Name}).Count | Should -Be 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ Describe 'Basic ThreadJob Tests' -Tags 'CI' {
|
||||
It 'ThreadJob Runspaces should be cleaned up at completion' {
|
||||
|
||||
$script = $WaitForCountFnScript + @'
|
||||
|
||||
$WarningPreference = 'SilentlyContinue'
|
||||
try
|
||||
{
|
||||
Get-Job | Where-Object PSJobTypeName -eq "ThreadJob" | Remove-Job -Force
|
||||
@@ -296,7 +296,7 @@ Describe 'Basic ThreadJob Tests' -Tags 'CI' {
|
||||
It 'ThreadJob Runspaces should be cleaned up after job removal' {
|
||||
|
||||
$script = $WaitForCountFnScript + @'
|
||||
|
||||
$WarningPreference = 'SilentlyContinue'
|
||||
try {
|
||||
Get-Job | Where-Object PSJobTypeName -eq "ThreadJob" | Remove-Job -Force
|
||||
$rsStartCount = @(Get-Runspace).Count
|
||||
|
||||
@@ -27,6 +27,8 @@ Describe "Test suite for validating automounted PowerShell drives" -Tags @('Feat
|
||||
$tmpVhdPath = Join-Path $TestDrive 'TestVHD.vhd'
|
||||
New-VHD -path $tmpVhdPath -SizeBytes 5mb -Dynamic -ErrorAction Stop
|
||||
Remove-Item $tmpVhdPath
|
||||
$VHDToolsNotFound = (Get-Module Hyper-V).PrivateData.ImplicitRemoting -eq $True
|
||||
Remove-Module Hyper-V
|
||||
}
|
||||
catch
|
||||
{ $VHDToolsNotFound = $true }
|
||||
|
||||
@@ -124,7 +124,7 @@ Describe "Verify approved aliases list" -Tags "CI" {
|
||||
"Alias", "npssc", "New-PSSessionConfigurationFile", $($FullCLR ), "ReadOnly", "", ""
|
||||
"Alias", "nsn", "New-PSSession", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", ""
|
||||
"Alias", "nv", "New-Variable", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", ""
|
||||
"Alias", "nwsn", "New-PSWorkflowSession", $($FullCLR ), "ReadOnly", "", ""
|
||||
"Alias", "nwsn", "New-PSWorkflowSession", $($FullCLR -or $CoreWindows ), "", "", ""
|
||||
"Alias", "ogv", "Out-GridView", $($FullCLR -or $CoreWindows ), "ReadOnly", "", ""
|
||||
"Alias", "oh", "Out-Host", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", ""
|
||||
"Alias", "popd", "Pop-Location", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "AllScope", ""
|
||||
|
||||
@@ -31,7 +31,13 @@ Describe 'Online help tests for PowerShell Cmdlets' -Tags "CI" {
|
||||
foreach ($cmdlet in $cmdletList)
|
||||
{
|
||||
# If the cmdlet is not preset in CoreCLR, skip it.
|
||||
$skipTest = $null -eq (Get-Command $cmdlet.TopicTitle -ErrorAction SilentlyContinue)
|
||||
$command = Get-Command $cmdlet.TopicTitle -ErrorAction SilentlyContinue
|
||||
$skipTest = $null -eq ($command)
|
||||
if ((-not $skipTest) -and $command.Module.PrivateData.ImplicitRemoting)
|
||||
{
|
||||
$skipTest = $true
|
||||
Remove-Module $command.Module
|
||||
}
|
||||
|
||||
# TopicTitle - is the cmdlet name in the csv file
|
||||
# HelpURI - is the expected help URI in the csv file
|
||||
|
||||
Reference in New Issue
Block a user