Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
35519b1
WinCompat work
Oct 17, 2019
3f6d922
AnalysisCache Workaround
Oct 17, 2019
1527b83
Using NewProcessConnectionInfo
Nov 1, 2019
7410fb2
Minor updates
Nov 1, 2019
0fb4be2
Updates to ImportModuleCommand
Nov 1, 2019
7541398
Merge branch 'master' into WinCompat
Nov 4, 2019
dd67412
Addressed feedback from Ilya and Paul
Nov 5, 2019
af2bd9e
Merge branch 'WinCompat' of https://github.com/anmenaga/PowerShell in…
Nov 5, 2019
1c5cb57
fix Codacy issue
Nov 5, 2019
57e06a8
Workaround for Test-WinRMQuickConfigNeeded
Nov 7, 2019
eb8c471
Changed to 8.0 using statement syntax
Nov 7, 2019
5b0fae0
Fixed some tests
Nov 7, 2019
3fe091d
Fix AutomountVHDDrive tests
Nov 8, 2019
a7ef307
Fixed merge conflict
Nov 8, 2019
fe84efd
Merge branch 'master' into WinCompat
Nov 8, 2019
d43a499
Fix AutomountedDrives.Tests
Nov 8, 2019
d6ed0d8
Merge branch 'WinCompat' of https://github.com/anmenaga/PowerShell in…
Nov 8, 2019
b00a950
Fixed Implicit.Remoting.Tests
Nov 8, 2019
baef710
Removed aliases in Implicit.Remoting.Tests
Nov 11, 2019
87be64e
fix PSWorkflow module in DefaultCommands.Tests
Nov 11, 2019
ecd8833
Fixed typo in DefaultCommands.Tests
Nov 12, 2019
9859e59
Fixed OnlineHelp.Tests
Nov 12, 2019
ac57fb0
Remove static session property
Nov 12, 2019
df13055
Fixed ThreadJob.Tests
Nov 12, 2019
b628b9f
Fixed CompatiblePSEditions.Module.Tests
Nov 12, 2019
8efd907
Fixed Get-Process.Tests
Nov 12, 2019
51c045b
Updated CompatiblePSEditions.Module.Tests
Nov 13, 2019
08b2ee0
Address Jim's feedback
Nov 14, 2019
3932249
Address Ilya's feedback
Nov 14, 2019
e1ea2c6
Addressed Steve's feedback
Nov 14, 2019
e6a77f5
Fixed ParameterBinding on non-windows
Nov 14, 2019
66d9c53
Addressed Rob's feedback
Nov 14, 2019
5d74d8a
Addressed Steve's feedback
Nov 14, 2019
defb2f7
Fixed workingDirectory parameter for WindowsPS
Nov 15, 2019
8701cab
Merge branch 'master' into WinCompat
Nov 16, 2019
f6649dc
Merge branch 'master' into WinCompat
Nov 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,12 @@ static ExperimentalFeature()
#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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private static ConcurrentDictionary<string, CommandTypes> AnalyzeManifestModule(
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;
Expand Down Expand Up @@ -493,7 +493,7 @@ internal static void CacheModuleExports(PSModuleInfo module, ExecutionContext co

// 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public sealed class ImportModuleCommand : ModuleCmdletBase, IDisposable
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
Expand Down Expand Up @@ -82,6 +85,7 @@ public string Prefix
[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>();
Expand All @@ -91,6 +95,7 @@ public string Prefix
/// </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; }
Expand Down Expand Up @@ -226,8 +231,15 @@ public SwitchParameter Force

/// <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; }
Expand Down Expand Up @@ -263,6 +275,7 @@ public SwitchParameter AsCustomObject
[Parameter(ParameterSetName = ParameterSet_Name)]
[Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]
[Parameter(ParameterSetName = ParameterSet_ViaCimSession)]
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat)]
[Alias("Version")]
public Version MinimumVersion
{
Expand All @@ -277,6 +290,7 @@ public Version MinimumVersion
[Parameter(ParameterSetName = ParameterSet_Name)]
[Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]
[Parameter(ParameterSetName = ParameterSet_ViaCimSession)]
[Parameter("PSWindowsPowerShellCompatibility", ExperimentAction.Show, ParameterSetName = ParameterSet_ViaWinCompat)]
public string MaximumVersion
{
get
Expand Down Expand Up @@ -306,6 +320,7 @@ public string MaximumVersion
[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; }
Expand Down Expand Up @@ -406,6 +421,15 @@ public ImportModuleCommand()
[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
Expand Down Expand Up @@ -805,7 +829,8 @@ private IList<PSModuleInfo> ImportModule_RemotelyViaPsrpSession(
ImportModuleOptions importModuleOptions,
IEnumerable<string> moduleNames,
IEnumerable<ModuleSpecification> fullyQualifiedNames,
PSSession psSession)
PSSession psSession,
bool usingWinCompat = false)
{
var remotelyImportedModules = new List<PSModuleInfo>();
if (moduleNames != null)
Expand All @@ -829,7 +854,7 @@ private IList<PSModuleInfo> ImportModule_RemotelyViaPsrpSession(
// 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;
Expand Down Expand Up @@ -1795,11 +1820,15 @@ protected override void ProcessRecord()
{
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
}
}
}
}
Expand Down Expand Up @@ -1836,6 +1865,25 @@ protected override void ProcessRecord()
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");
Expand Down
160 changes: 132 additions & 28 deletions src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Reflection;
using System.Text;
using System.Xml;
using System.Diagnostics;

using Microsoft.PowerShell.Cmdletization;

Expand Down Expand Up @@ -283,6 +284,21 @@ internal List<WildcardPattern> MatchAll
"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,
Expand Down Expand Up @@ -2347,43 +2363,70 @@ internal PSModuleInfo LoadModuleManifest(
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));
if (importingModule)
{
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);

InvalidOperationException ioe = new InvalidOperationException(message);
var moduleProxies = ps.Invoke<PSModuleInfo>();

ErrorRecord er = new ErrorRecord(
ioe,
nameof(Modules) + "_" + nameof(Modules.PSEditionNotSupported),
ErrorCategory.ResourceUnavailable,
moduleManifestPath);

WriteError(er);
// we are loading by a single ManifestPath so expect max of 1
if(moduleProxies.Count > 0)
{
return moduleProxies[0];
}
else
{
return null;
}
}
}

if (bailOnFirstError)
else
{
// If we're trying to load the module, return null so that caches
// are not polluted
if (importingModule)
containedErrors = true;

if (writingErrors)
{
return null;
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,
};
}
}
}

Expand Down Expand Up @@ -4768,6 +4811,62 @@ internal static Collection<string> GetResolvedPathCollection(string filePath, Ex
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)
Expand Down Expand Up @@ -4860,6 +4959,11 @@ internal void RemoveModule(PSModuleInfo module, string moduleNameInRemoveModuleC
}
}

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)
Expand Down
Loading