MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Diagnostics;8using System.Diagnostics.CodeAnalysis;9using System.Globalization;10using System.IO;11using System.Linq;12using System.Management.Automation;13using System.Management.Automation.Configuration;14using System.Management.Automation.Internal;15using System.Management.Automation.Language;16using System.Management.Automation.Runspaces;17using System.Reflection;18using System.Security;19using System.Threading;20 21using Microsoft.Management.Infrastructure;22using Microsoft.PowerShell.Cmdletization;23using Microsoft.PowerShell.Telemetry;24 25using Dbg = System.Management.Automation.Diagnostics;26using Parser = System.Management.Automation.Language.Parser;27using ScriptBlock = System.Management.Automation.ScriptBlock;28using Token = System.Management.Automation.Language.Token;29 30#if LEGACYTELEMETRY31using Microsoft.PowerShell.Telemetry.Internal;32#endif33 34//35// Now define the set of commands for manipulating modules.36//37namespace Microsoft.PowerShell.Commands38{39 /// <summary>40 /// Implements a cmdlet that loads a module.41 /// </summary>42 [Cmdlet(VerbsData.Import, "Module", DefaultParameterSetName = ParameterSet_Name, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096585")]43 [OutputType(typeof(PSModuleInfo))]44 public sealed class ImportModuleCommand : ModuleCmdletBase, IDisposable45 {46 #region Cmdlet parameters47 48 private const string ParameterSet_Name = "Name";49 private const string ParameterSet_FQName = "FullyQualifiedName";50 private const string ParameterSet_ModuleInfo = "ModuleInfo";51 private const string ParameterSet_Assembly = "Assembly";52 53 private const string ParameterSet_ViaPsrpSession = "PSSession";54 private const string ParameterSet_ViaCimSession = "CimSession";55 private const string ParameterSet_FQName_ViaPsrpSession = "FullyQualifiedNameAndPSSession";56 private const string ParameterSet_ViaWinCompat = "WinCompat";57 private const string ParameterSet_FQName_ViaWinCompat = "FullyQualifiedNameAndWinCompat";58 59 /// <summary>60 /// This parameter specifies whether to import to the current session state61 /// or to the global / top-level session state.62 /// </summary>63 [Parameter]64 public SwitchParameter Global65 {66 get { return base.BaseGlobal; }67 68 set { base.BaseGlobal = value; }69 }70 71 /// <summary>72 /// This parameter specified a prefix used to modify names of imported commands.73 /// </summary>74 [Parameter]75 [ValidateNotNull]76 public string Prefix77 {78 get { return BasePrefix; }79 80 set { BasePrefix = value; }81 }82 83 /// <summary>84 /// This parameter names the module to load.85 /// </summary>86 [Parameter(ParameterSetName = ParameterSet_Name, Mandatory = true, ValueFromPipeline = true, Position = 0)]87 [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession, Mandatory = true, ValueFromPipeline = true, Position = 0)]88 [Parameter(ParameterSetName = ParameterSet_ViaCimSession, Mandatory = true, ValueFromPipeline = true, Position = 0)]89 [Parameter(ParameterSetName = ParameterSet_ViaWinCompat, Mandatory = true, ValueFromPipeline = true, Position = 0)]90 [ValidateTrustedData]91 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]92 public string[] Name { get; set; } = Array.Empty<string>();93 94 /// <summary>95 /// This parameter specifies the current pipeline object.96 /// </summary>97 [Parameter(ParameterSetName = ParameterSet_FQName, Mandatory = true, ValueFromPipeline = true, Position = 0)]98 [Parameter(ParameterSetName = ParameterSet_FQName_ViaPsrpSession, Mandatory = true, ValueFromPipeline = true, Position = 0)]99 [Parameter(ParameterSetName = ParameterSet_FQName_ViaWinCompat, Mandatory = true, ValueFromPipeline = true, Position = 0)]100 [ValidateTrustedData]101 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]102 public ModuleSpecification[] FullyQualifiedName { get; set; }103 104 /// <summary>105 /// A list of assembly objects to process as modules.106 /// </summary>107 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]108 [Parameter(ParameterSetName = ParameterSet_Assembly, Mandatory = true, ValueFromPipeline = true, Position = 0)]109 [ValidateTrustedData]110 public Assembly[] Assembly { get; set; }111 112 /// <summary>113 /// This patterns matching the names of functions to import from the module...114 /// </summary>115 [Parameter]116 [ValidateNotNull]117 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]118 public string[] Function119 {120 get121 {122 return _functionImportList;123 }124 125 set126 {127 if (value == null)128 return;129 _functionImportList = value;130 // Create the list of patterns to match at parameter bind time131 // so errors will be reported before loading the module...132 BaseFunctionPatterns = new List<WildcardPattern>();133 foreach (string pattern in _functionImportList)134 {135 BaseFunctionPatterns.Add(WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase));136 }137 }138 }139 140 private string[] _functionImportList = Array.Empty<string>();141 142 /// <summary>143 /// This patterns matching the names of cmdlets to import from the module...144 /// </summary>145 [Parameter]146 [ValidateNotNull]147 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]148 public string[] Cmdlet149 {150 get151 {152 return _cmdletImportList;153 }154 155 set156 {157 if (value == null)158 return;159 160 _cmdletImportList = value;161 // Create the list of patterns to match at parameter bind time162 // so errors will be reported before loading the module...163 BaseCmdletPatterns = new List<WildcardPattern>();164 foreach (string pattern in _cmdletImportList)165 {166 BaseCmdletPatterns.Add(WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase));167 }168 }169 }170 171 private string[] _cmdletImportList = Array.Empty<string>();172 173 /// <summary>174 /// This parameter specifies the variables to import from the module...175 /// </summary>176 [Parameter]177 [ValidateNotNull]178 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]179 public string[] Variable180 {181 get182 {183 return _variableExportList;184 }185 186 set187 {188 if (value == null)189 return;190 _variableExportList = value;191 // Create the list of patterns to match at parameter bind time192 // so errors will be reported before loading the module...193 BaseVariablePatterns = new List<WildcardPattern>();194 foreach (string pattern in _variableExportList)195 {196 BaseVariablePatterns.Add(WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase));197 }198 }199 }200 201 private string[] _variableExportList;202 203 /// <summary>204 /// This parameter specifies the aliases to import from the module...205 /// </summary>206 [Parameter]207 [ValidateNotNull]208 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]209 public string[] Alias210 {211 get212 {213 return _aliasExportList;214 }215 216 set217 {218 if (value == null)219 return;220 221 _aliasExportList = value;222 // Create the list of patterns to match at parameter bind time223 // so errors will be reported before loading the module...224 BaseAliasPatterns = new List<WildcardPattern>();225 foreach (string pattern in _aliasExportList)226 {227 BaseAliasPatterns.Add(WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase));228 }229 }230 }231 232 private string[] _aliasExportList;233 234 /// <summary>235 /// This parameter causes a module to be loaded over top of the current one...236 /// </summary>237 [Parameter]238 public SwitchParameter Force239 {240 get { return (SwitchParameter)BaseForce; }241 242 set { BaseForce = value; }243 }244 245 /// <summary>246 /// Skips the check on CompatiblePSEditions for modules loaded from the System32 module path.247 /// This is mutually exclusive with UseWindowsPowerShell parameter.248 /// </summary>249 [Parameter(ParameterSetName = ParameterSet_Name)]250 [Parameter(ParameterSetName = ParameterSet_FQName)]251 [Parameter(ParameterSetName = ParameterSet_ModuleInfo)]252 [Parameter(ParameterSetName = ParameterSet_Assembly)]253 [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]254 [Parameter(ParameterSetName = ParameterSet_ViaCimSession)]255 [Parameter(ParameterSetName = ParameterSet_FQName_ViaPsrpSession)]256 public SwitchParameter SkipEditionCheck257 {258 get { return (SwitchParameter)BaseSkipEditionCheck; }259 260 set { BaseSkipEditionCheck = value; }261 }262 263 /// <summary>264 /// This parameter causes the session state instance to be written...265 /// </summary>266 [Parameter]267 public SwitchParameter PassThru268 {269 get { return (SwitchParameter)BasePassThru; }270 271 set { BasePassThru = value; }272 }273 274 /// <summary>275 /// This parameter causes the session state instance to be written as a custom object...276 /// </summary>277 [Parameter]278 public SwitchParameter AsCustomObject279 {280 get { return (SwitchParameter)BaseAsCustomObject; }281 282 set { BaseAsCustomObject = value; }283 }284 285 /// <summary>286 /// The minimum version of the module to load.287 /// </summary>288 [Parameter(ParameterSetName = ParameterSet_Name)]289 [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]290 [Parameter(ParameterSetName = ParameterSet_ViaCimSession)]291 [Parameter(ParameterSetName = ParameterSet_ViaWinCompat)]292 [Alias("Version")]293 public Version MinimumVersion294 {295 get { return BaseMinimumVersion; }296 297 set { BaseMinimumVersion = value; }298 }299 300 /// <summary>301 /// The maximum version of the module to load.302 /// </summary>303 [Parameter(ParameterSetName = ParameterSet_Name)]304 [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]305 [Parameter(ParameterSetName = ParameterSet_ViaCimSession)]306 [Parameter(ParameterSetName = ParameterSet_ViaWinCompat)]307 public string MaximumVersion308 {309 get310 {311 if (BaseMaximumVersion == null)312 return null;313 else314 return BaseMaximumVersion.ToString();315 }316 317 set318 {319 if (string.IsNullOrWhiteSpace(value))320 {321 BaseMaximumVersion = null;322 }323 else324 {325 BaseMaximumVersion = GetMaximumVersion(value);326 }327 }328 }329 330 /// <summary>331 /// The version of the module to load.332 /// </summary>333 [Parameter(ParameterSetName = ParameterSet_Name)]334 [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession)]335 [Parameter(ParameterSetName = ParameterSet_ViaCimSession)]336 [Parameter(ParameterSetName = ParameterSet_ViaWinCompat)]337 public Version RequiredVersion338 {339 get { return BaseRequiredVersion; }340 341 set { BaseRequiredVersion = value; }342 }343 344 /// <summary>345 /// This parameter specifies the current pipeline object.346 /// </summary>347 [Parameter(ParameterSetName = ParameterSet_ModuleInfo, Mandatory = true, ValueFromPipeline = true, Position = 0)]348 [ValidateTrustedData]349 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]350 public PSModuleInfo[] ModuleInfo { get; set; } = Array.Empty<PSModuleInfo>();351 352 /// <summary>353 /// The arguments to pass to the module script.354 /// </summary>355 [Parameter]356 [Alias("Args")]357 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]358 public object[] ArgumentList359 {360 get { return BaseArgumentList; }361 362 set { BaseArgumentList = value; }363 }364 365 /// <summary>366 /// Disable warnings on cmdlet and function names that have non-standard verbs367 /// or non-standard characters in the noun.368 /// </summary>369 [Parameter]370 public SwitchParameter DisableNameChecking371 {372 get { return BaseDisableNameChecking; }373 374 set { BaseDisableNameChecking = value; }375 }376 377 /// <summary>378 /// Does not import a command if a command with same name exists on the target sessionstate.379 /// </summary>380 [Parameter, Alias("NoOverwrite")]381 public SwitchParameter NoClobber { get; set; }382 383 /// <summary>384 /// Imports a command to the scope specified.385 /// </summary>386 [Parameter]387 [ValidateSet("Local", "Global")]388 public string Scope389 {390 get391 {392 return _scope;393 }394 395 set396 {397 _scope = value;398 _isScopeSpecified = true;399 }400 }401 402 private string _scope = string.Empty;403 private bool _isScopeSpecified = false;404 405 /// <summary>406 /// If specified, then Import-Module will attempt to import PowerShell modules from a remote computer using the specified session.407 /// </summary>408 [Parameter(ParameterSetName = ParameterSet_ViaPsrpSession, Mandatory = true)]409 [Parameter(ParameterSetName = ParameterSet_FQName_ViaPsrpSession, Mandatory = true)]410 [ValidateNotNull]411 public PSSession PSSession { get; set; }412 413 /// Construct the Import-Module cmdlet object414 public ImportModuleCommand()415 {416 base.BaseDisableNameChecking = false;417 }418 419 /// <summary>420 /// If specified, then Import-Module will attempt to import PS-CIM modules from a remote computer using the specified session.421 /// </summary>422 [Parameter(ParameterSetName = ParameterSet_ViaCimSession, Mandatory = true)]423 [ValidateNotNull]424 public CimSession CimSession { get; set; }425 426 /// <summary>427 /// For interoperability with 3rd party CIM servers, user can specify custom resource URI.428 /// </summary>429 [Parameter(ParameterSetName = ParameterSet_ViaCimSession, Mandatory = false)]430 [ValidateNotNull]431 public Uri CimResourceUri { get; set; }432 433 /// <summary>434 /// For interoperability with 3rd party CIM servers, user can specify custom namespace.435 /// </summary>436 [Parameter(ParameterSetName = ParameterSet_ViaCimSession, Mandatory = false)]437 [ValidateNotNullOrEmpty]438 public string CimNamespace { get; set; }439 440 /// <summary>441 /// This parameter causes a module to be loaded into Windows PowerShell.442 /// This is mutually exclusive with SkipEditionCheck parameter.443 /// </summary>444 [Parameter(ParameterSetName = ParameterSet_ViaWinCompat, Mandatory = true)]445 [Parameter(ParameterSetName = ParameterSet_FQName_ViaWinCompat, Mandatory = true)]446 [Alias("UseWinPS")]447 public SwitchParameter UseWindowsPowerShell { get; set; }448 449 #endregion Cmdlet parameters450 451 #region Local import452 453 private void ImportModule_ViaLocalModuleInfo(ImportModuleOptions importModuleOptions, PSModuleInfo module)454 {455 try456 {457 PSModuleInfo alreadyLoadedModule = null;458 TryGetFromModuleTable(module.Path, out alreadyLoadedModule);459 if (!BaseForce && DoesAlreadyLoadedModuleSatisfyConstraints(alreadyLoadedModule))460 {461 AddModuleToModuleTables(this.Context, this.TargetSessionState.Internal, alreadyLoadedModule);462 463 // Even if the module has been loaded, import the specified members...464 ImportModuleMembers(alreadyLoadedModule, this.BasePrefix, importModuleOptions);465 466 if (BaseAsCustomObject)467 {468 if (alreadyLoadedModule.ModuleType != ModuleType.Script)469 {470 string message = StringUtil.Format(Modules.CantUseAsCustomObjectWithBinaryModule, alreadyLoadedModule.Path);471 InvalidOperationException invalidOp = new InvalidOperationException(message);472 ErrorRecord er = new ErrorRecord(invalidOp, "Modules_CantUseAsCustomObjectWithBinaryModule",473 ErrorCategory.PermissionDenied, null);474 WriteError(er);475 }476 else477 {478 WriteObject(alreadyLoadedModule.AsCustomObject());479 }480 }481 else if (BasePassThru)482 {483 WriteObject(alreadyLoadedModule);484 }485 }486 else487 {488 PSModuleInfo moduleToRemove;489 if (TryGetFromModuleTable(module.Path, out moduleToRemove, toRemove: true))490 {491 Dbg.Assert(BaseForce, "We should only remove and reload if -Force was specified");492 RemoveModule(moduleToRemove);493 }494 495 PSModuleInfo moduleToProcess = module;496 try497 {498 // If we're passing in a dynamic module, then the session state will not be499 // null and we want to just add the module to the module table. Otherwise, it's500 // a module info from Get-Module -list so we need to read the actual module file.501 if (module.SessionState == null)502 {503 if (File.Exists(module.Path))504 {505 bool found;506 moduleToProcess = LoadModule(module.Path, null, this.BasePrefix, /*SessionState*/ null,507 ref importModuleOptions,508 ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.NullOnFirstError,509 out found);510 Dbg.Assert(found, "Module should be found when referenced by its absolute path");511 }512 }513 else if (!string.IsNullOrEmpty(module.Name))514 {515 // It has a session state and a name but it's not in the module516 // table so it's ok to add it517 518 // Add it to the all module tables519 AddModuleToModuleTables(this.Context, this.TargetSessionState.Internal, moduleToProcess);520 521 if (moduleToProcess.SessionState != null)522 {523 ImportModuleMembers(moduleToProcess, this.BasePrefix, importModuleOptions);524 }525 526 if (BaseAsCustomObject && moduleToProcess.SessionState != null)527 {528 WriteObject(module.AsCustomObject());529 }530 else if (BasePassThru)531 {532 WriteObject(moduleToProcess);533 }534 }535 }536 catch (IOException)537 {538 }539 }540 }541 catch (PSInvalidOperationException e)542 {543 ErrorRecord er = new ErrorRecord(e.ErrorRecord, e);544 WriteError(er);545 }546 }547 548 private void ImportModule_ViaAssembly(ImportModuleOptions importModuleOptions, Assembly suppliedAssembly)549 {550 bool moduleLoaded = false;551 string moduleName = "dynamic_code_module_" + suppliedAssembly.FullName;552 553 // Loop through Module Cache to ensure that the module is not already imported.554 foreach (KeyValuePair<string, PSModuleInfo> pair in Context.Modules.ModuleTable)555 {556 if (pair.Value.Path == string.Empty)557 {558 // If the module in the moduleTable is an assembly module without path, the moduleName is the key.559 if (pair.Key.Equals(moduleName, StringComparison.OrdinalIgnoreCase))560 {561 moduleLoaded = true;562 if (BasePassThru)563 {564 WriteObject(pair.Value);565 }566 567 break;568 }569 570 continue;571 }572 573 if (pair.Value.Path.Equals(suppliedAssembly.Location, StringComparison.OrdinalIgnoreCase))574 {575 moduleLoaded = true;576 if (BasePassThru)577 {578 WriteObject(pair.Value);579 }580 581 break;582 }583 }584 585 if (!moduleLoaded)586 {587 PSModuleInfo module = LoadBinaryModule(588 parentModule: null,589 moduleName: null,590 fileName: null,591 suppliedAssembly,592 moduleBase: null,593 ss: null,594 importModuleOptions,595 ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.NullOnFirstError,596 BasePrefix,597 out bool found);598 599 if (found && module is not null)600 {601 // Add it to all module tables ...602 AddModuleToModuleTables(Context, TargetSessionState.Internal, module);603 if (BasePassThru)604 {605 WriteObject(module);606 }607 }608 }609 }610 611 private PSModuleInfo ImportModule_LocallyViaName_WithTelemetry(ImportModuleOptions importModuleOptions, string name)612 {613 PSModuleInfo foundModule = ImportModule_LocallyViaName(importModuleOptions, name);614 if (foundModule != null)615 {616 SetModuleBaseForEngineModules(foundModule.Name, this.Context);617 618 // report loading of the module in telemetry619 // avoid double reporting for WinCompat modules that go through CommandDiscovery\AutoloadSpecifiedModule620 if (!foundModule.IsWindowsPowerShellCompatModule)621 {622 ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, foundModule);623#if LEGACYTELEMETRY624 TelemetryAPI.ReportModuleLoad(foundModule);625#endif626 }627 }628 629 return foundModule;630 }631 632 private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModuleOptions, string name)633 {634 bool shallWriteError = !importModuleOptions.SkipSystem32ModulesAndSuppressError;635 636 try637 {638 bool found = false;639 PSModuleInfo foundModule = null;640 641 string cachedPath = null;642 string rootedPath = null;643 644 // See if we can use the cached path for the file. If a version number has been specified, then645 // we won't look in the cache646 if (this.MinimumVersion == null && this.MaximumVersion == null && this.RequiredVersion == null && PSModuleInfo.UseAppDomainLevelModuleCache && !this.BaseForce)647 {648 // See if the name is in the appdomain-level module path name cache...649 cachedPath = PSModuleInfo.ResolveUsingAppDomainLevelModuleCache(name);650 }651 652 if (!string.IsNullOrEmpty(cachedPath))653 {654 if (File.Exists(cachedPath))655 {656 rootedPath = cachedPath;657 }658 else659 {660 PSModuleInfo.RemoveFromAppDomainLevelCache(name);661 }662 }663 664 // If null check for full-qualified paths - either absolute or relative665 rootedPath ??= ResolveRootedFilePath(name, this.Context);666 667 bool alreadyLoaded = false;668 var manifestProcessingFlags = ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.NullOnFirstError;669 if (shallWriteError)670 {671 manifestProcessingFlags |= ManifestProcessingFlags.WriteErrors;672 }673 674 if (!string.IsNullOrEmpty(rootedPath))675 {676 // If the module has already been loaded, just emit it and continue...677 if (!BaseForce && TryGetFromModuleTable(rootedPath, out PSModuleInfo module))678 {679 if (module.ModuleType != ModuleType.Manifest680 || ModuleIntrinsics.IsVersionMatchingConstraints(module.Version, RequiredVersion, BaseMinimumVersion, BaseMaximumVersion))681 {682 alreadyLoaded = true;683 AddModuleToModuleTables(this.Context, this.TargetSessionState.Internal, module);684 ImportModuleMembers(module, this.BasePrefix, importModuleOptions);685 686 if (BaseAsCustomObject)687 {688 if (module.ModuleType != ModuleType.Script)689 {690 string message = StringUtil.Format(Modules.CantUseAsCustomObjectWithBinaryModule, module.Path);691 InvalidOperationException invalidOp = new InvalidOperationException(message);692 ErrorRecord er = new ErrorRecord(invalidOp, "Modules_CantUseAsCustomObjectWithBinaryModule",693 ErrorCategory.PermissionDenied, null);694 WriteError(er);695 }696 else697 {698 WriteObject(module.AsCustomObject());699 }700 }701 else if (BasePassThru)702 {703 WriteObject(module);704 }705 706 found = true;707 foundModule = module;708 }709 }710 711 if (!alreadyLoaded)712 {713 // If the path names a file, load that file...714 if (File.Exists(rootedPath))715 {716 PSModuleInfo moduleToRemove;717 if (TryGetFromModuleTable(rootedPath, out moduleToRemove, toRemove: true))718 {719 RemoveModule(moduleToRemove);720 }721 722 foundModule = LoadModule(723 fileName: rootedPath,724 moduleBase: null,725 prefix: BasePrefix,726 ss: null, /*SessionState*/727 ref importModuleOptions,728 manifestProcessingFlags,729 out found);730 }731 else if (Directory.Exists(rootedPath))732 {733 // If the path ends with a directory separator, remove it734 if (rootedPath.EndsWith(Path.DirectorySeparatorChar))735 {736 rootedPath = Path.GetDirectoryName(rootedPath);737 }738 739 // Load the latest valid version if it is a multi-version module directory740 foundModule = LoadUsingMultiVersionModuleBase(rootedPath, manifestProcessingFlags, importModuleOptions, out found);741 742 if (!found)743 {744 // If the path is a directory, double up the end of the string745 // then try to load that using extensions...746 rootedPath = Path.Combine(rootedPath, Path.GetFileName(rootedPath));747 foundModule = LoadUsingExtensions(748 parentModule: null,749 moduleName: rootedPath,750 fileBaseName: rootedPath,751 extension: null,752 moduleBase: null,753 prefix: BasePrefix,754 ss: null, /*SessionState*/755 importModuleOptions,756 manifestProcessingFlags,757 out found);758 }759 }760 }761 }762 else763 {764 // Check if module could be a snapin. This was the case for PowerShell version 2 engine modules.765 if (InitialSessionState.IsEngineModule(name))766 {767 PSSnapInInfo snapin = Context.CurrentRunspace.InitialSessionState.GetPSSnapIn(name);768 769 // Return the command if we found a module770 if (snapin != null)771 {772 // warn that this module already exists as a snapin773 string warningMessage = string.Format(774 CultureInfo.InvariantCulture,775 Modules.ModuleLoadedAsASnapin,776 snapin.Name);777 WriteWarning(warningMessage);778 found = true;779 return foundModule;780 }781 }782 783 // At this point, the name didn't resolve to an existing file or directory.784 // It may still be rooted (relative or absolute). If it is, then we'll only use785 // the extension search. If it's not rooted, use a path-based search.786 if (IsRooted(name))787 {788 // If there is no extension, we'll have to search using the extensions789 if (!string.IsNullOrEmpty(Path.GetExtension(name)))790 {791 foundModule = LoadModule(792 fileName: name,793 moduleBase: null,794 prefix: BasePrefix,795 ss: null, /*SessionState*/796 ref importModuleOptions,797 manifestProcessingFlags,798 out found);799 }800 else801 {802 foundModule = LoadUsingExtensions(803 parentModule: null,804 moduleName: name,805 fileBaseName: name,806 extension: null,807 moduleBase: null,808 prefix: BasePrefix,809 ss: null, /*SessionState*/810 importModuleOptions,811 manifestProcessingFlags,812 out found);813 }814 }815 else816 {817 IEnumerable<string> modulePath = ModuleIntrinsics.GetModulePath(false, this.Context);818 819 if (this.MinimumVersion == null && this.RequiredVersion == null && this.MaximumVersion == null)820 {821 this.AddToAppDomainLevelCache = true;822 }823 824 found = LoadUsingModulePath(825 modulePath,826 name,827 ss: null, /* SessionState*/828 importModuleOptions,829 manifestProcessingFlags,830 out foundModule);831 }832 }833 834 if (!found && shallWriteError)835 {836 ErrorRecord er = null;837 string message = null;838 if (BaseRequiredVersion != null)839 {840 message = StringUtil.Format(Modules.ModuleWithVersionNotFound, name, BaseRequiredVersion);841 }842 else if (BaseMinimumVersion != null && BaseMaximumVersion != null)843 {844 message = StringUtil.Format(Modules.MinimumVersionAndMaximumVersionNotFound, name, BaseMinimumVersion, BaseMaximumVersion);845 }846 else if (BaseMinimumVersion != null)847 {848 message = StringUtil.Format(Modules.ModuleWithVersionNotFound, name, BaseMinimumVersion);849 }850 else if (BaseMaximumVersion != null)851 {852 message = StringUtil.Format(Modules.MaximumVersionNotFound, name, BaseMaximumVersion);853 }854 855 if (BaseRequiredVersion != null || BaseMinimumVersion != null || BaseMaximumVersion != null)856 {857 FileNotFoundException fnf = new FileNotFoundException(message);858 er = new ErrorRecord(fnf, "Modules_ModuleWithVersionNotFound",859 ErrorCategory.ResourceUnavailable, name);860 }861 else862 {863 message = StringUtil.Format(Modules.ModuleNotFound, name);864 FileNotFoundException fnf = new FileNotFoundException(message);865 er = new ErrorRecord(fnf, "Modules_ModuleNotFound",866 ErrorCategory.ResourceUnavailable, name);867 }868 869 WriteError(er);870 }871 872 return foundModule;873 }874 catch (PSInvalidOperationException e)875 {876 if (shallWriteError)877 {878 WriteError(new ErrorRecord(e.ErrorRecord, e));879 }880 }881 882 return null;883 }884 885 private PSModuleInfo ImportModule_LocallyViaFQName(ImportModuleOptions importModuleOptions, ModuleSpecification modulespec)886 {887 RequiredVersion = modulespec.RequiredVersion;888 MinimumVersion = modulespec.Version;889 MaximumVersion = modulespec.MaximumVersion;890 BaseGuid = modulespec.Guid;891 892 PSModuleInfo foundModule = ImportModule_LocallyViaName(importModuleOptions, modulespec.Name);893 894 if (foundModule != null)895 {896 ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, foundModule);897 SetModuleBaseForEngineModules(foundModule.Name, this.Context);898 }899 900 return foundModule;901 }902 903 #endregion Local import904 905 #region Remote import906 907 #region PSSession parameterset908 909 private IList<PSModuleInfo> ImportModule_RemotelyViaPsrpSession(910 ImportModuleOptions importModuleOptions,911 IEnumerable<string> moduleNames,912 IEnumerable<ModuleSpecification> fullyQualifiedNames,913 PSSession psSession,914 bool usingWinCompat = false)915 {916 var remotelyImportedModules = new List<PSModuleInfo>();917 if (moduleNames != null)918 {919 foreach (string moduleName in moduleNames)920 {921 var tmp = ImportModule_RemotelyViaPsrpSession(importModuleOptions, moduleName, null, psSession);922 remotelyImportedModules.AddRange(tmp);923 }924 }925 926 if (fullyQualifiedNames != null)927 {928 foreach (var fullyQualifiedName in fullyQualifiedNames)929 {930 var tmp = ImportModule_RemotelyViaPsrpSession(importModuleOptions, null, fullyQualifiedName, psSession);931 remotelyImportedModules.AddRange(tmp);932 }933 }934 935 // Send telemetry on the imported modules936 foreach (PSModuleInfo moduleInfo in remotelyImportedModules)937 {938 ApplicationInsightsTelemetry.SendModuleTelemetryMetric(usingWinCompat ? TelemetryType.WinCompatModuleLoad : TelemetryType.ModuleLoad, moduleInfo);939 }940 941 return remotelyImportedModules;942 }943 944 private IList<PSModuleInfo> ImportModule_RemotelyViaPsrpSession(945 ImportModuleOptions importModuleOptions,946 string moduleName,947 ModuleSpecification fullyQualifiedName,948 PSSession psSession)949 {950 //951 // import the module in the remote session first952 //953 List<PSObject> remotelyImportedModules;954 using (var powerShell = System.Management.Automation.PowerShell.Create())955 {956 powerShell.Runspace = psSession.Runspace;957 powerShell.AddCommand("Import-Module");958 powerShell.AddParameter("DisableNameChecking", this.DisableNameChecking);959 powerShell.AddParameter("PassThru", true);960 961 if (fullyQualifiedName != null)962 {963 powerShell.AddParameter("FullyQualifiedName", fullyQualifiedName);964 }965 else966 {967 powerShell.AddParameter("Name", moduleName);968 969 if (this.MinimumVersion != null)970 {971 powerShell.AddParameter("Version", this.MinimumVersion);972 }973 974 if (this.RequiredVersion != null)975 {976 powerShell.AddParameter("RequiredVersion", this.RequiredVersion);977 }978 979 if (this.MaximumVersion != null)980 {981 powerShell.AddParameter("MaximumVersion", this.MaximumVersion);982 }983 }984 985 if (this.ArgumentList != null)986 {987 powerShell.AddParameter("ArgumentList", this.ArgumentList);988 }989 990 if (this.BaseForce)991 {992 powerShell.AddParameter("Force", true);993 }994 995 string errorMessageTemplate = string.Format(996 CultureInfo.InvariantCulture,997 Modules.RemoteDiscoveryRemotePsrpCommandFailed,998 string.Create(CultureInfo.InvariantCulture, $"Import-Module -Name '{moduleName}'"));999 remotelyImportedModules = RemoteDiscoveryHelper.InvokePowerShell(1000 powerShell,1001 this,1002 errorMessageTemplate,1003 this.CancellationToken).ToList();1004 }1005 1006 List<PSModuleInfo> result = new List<PSModuleInfo>();1007 foreach (PSObject remotelyImportedModule in remotelyImportedModules)1008 {1009 PSPropertyInfo nameProperty = remotelyImportedModule.Properties["Name"];1010 if (nameProperty != null)1011 {1012 string remoteModuleName = (string)LanguagePrimitives.ConvertTo(1013 nameProperty.Value,1014 typeof(string),1015 CultureInfo.InvariantCulture);1016 1017 PSPropertyInfo helpInfoProperty = remotelyImportedModule.Properties["HelpInfoUri"];1018 string remoteHelpInfoUri = null;1019 if (helpInfoProperty != null)1020 {1021 remoteHelpInfoUri = (string)LanguagePrimitives.ConvertTo(1022 helpInfoProperty.Value,1023 typeof(string),1024 CultureInfo.InvariantCulture);1025 }1026 1027 PSPropertyInfo guidProperty = remotelyImportedModule.Properties["Guid"];1028 Guid remoteModuleGuid = Guid.Empty;1029 if (guidProperty != null)1030 {1031 LanguagePrimitives.TryConvertTo(guidProperty.Value, out remoteModuleGuid);1032 }1033 1034 PSPropertyInfo versionProperty = remotelyImportedModule.Properties["Version"];1035 Version remoteModuleVersion = null;1036 if (versionProperty != null)1037 {1038 Version tmp;1039 if (LanguagePrimitives.TryConvertTo<Version>(versionProperty.Value, CultureInfo.InvariantCulture, out tmp))1040 {1041 remoteModuleVersion = tmp;1042 }1043 }1044 1045 PSModuleInfo moduleInfo = ImportModule_RemotelyViaPsrpSession_SinglePreimportedModule(1046 importModuleOptions,1047 remoteModuleName,1048 remoteModuleVersion,1049 psSession);1050 1051 // Set the HelpInfoUri and Guid as necessary, so that Save-Help can work with this module object1052 // to retrieve help files from the remote site.1053 if (moduleInfo != null)1054 {1055 // set the HelpInfoUri if it's needed1056 if (string.IsNullOrEmpty(moduleInfo.HelpInfoUri) && !string.IsNullOrEmpty(remoteHelpInfoUri))1057 {1058 moduleInfo.SetHelpInfoUri(remoteHelpInfoUri);1059 }1060 1061 // set the Guid if it's needed1062 if (remoteModuleGuid != Guid.Empty)1063 {1064 moduleInfo.SetGuid(remoteModuleGuid);1065 }1066 1067 result.Add(moduleInfo);1068 }1069 }1070 }1071 1072 return result;1073 }1074 1075 private PSModuleInfo ImportModule_RemotelyViaPsrpSession_SinglePreimportedModule(1076 ImportModuleOptions importModuleOptions,1077 string remoteModuleName,1078 Version remoteModuleVersion,1079 PSSession psSession)1080 {1081 string temporaryModulePath = RemoteDiscoveryHelper.GetModulePath(1082 remoteModuleName,1083 remoteModuleVersion,1084 psSession.ComputerName,1085 this.Context.CurrentRunspace);1086 string wildcardEscapedPath = WildcardPattern.Escape(temporaryModulePath);1087 try1088 {1089 //1090 // avoid importing a module twice1091 //1092 string localPsm1File = Path.Combine(temporaryModulePath, Path.GetFileName(temporaryModulePath) + ".psm1");1093 PSModuleInfo alreadyImportedModule = this.IsModuleImportUnnecessaryBecauseModuleIsAlreadyLoaded(1094 localPsm1File, this.BasePrefix, importModuleOptions);1095 if (alreadyImportedModule != null)1096 {1097 return alreadyImportedModule;1098 }1099 1100 //1101 // create proxy module in a temporary folder1102 //1103 using (var powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace))1104 {1105 powerShell.AddCommand("Export-PSSession");1106 powerShell.AddParameter("OutputModule", wildcardEscapedPath);1107 if (!importModuleOptions.NoClobberExportPSSession)1108 {1109 powerShell.AddParameter("AllowClobber", true);1110 }1111 powerShell.AddParameter("Module", remoteModuleName); // remoteModulePath is currently unsupported by Get-Command and implicit remoting1112 powerShell.AddParameter("Force", true);1113 powerShell.AddParameter("FormatTypeName", "*");1114 powerShell.AddParameter("Session", psSession);1115 1116 string errorMessageTemplate = string.Format(1117 CultureInfo.InvariantCulture,1118 Modules.RemoteDiscoveryFailedToGenerateProxyForRemoteModule,1119 remoteModuleName);1120 if (!RemoteDiscoveryHelper.InvokePowerShell(powerShell, this, errorMessageTemplate, this.CancellationToken).Any())1121 {1122 return null;1123 }1124 }1125 1126 //1127 // rename the psd1 file1128 //1129 string localPsd1File = Path.Combine(temporaryModulePath, remoteModuleName + ".psd1");1130 if (File.Exists(localPsd1File))1131 {1132 File.Delete(localPsd1File);1133 }1134 1135 File.Move(1136 sourceFileName: Path.Combine(temporaryModulePath, Path.GetFileName(temporaryModulePath) + ".psd1"),1137 destFileName: localPsd1File);1138 string wildcardEscapedPsd1Path = WildcardPattern.Escape(localPsd1File);1139 1140 //1141 // import the proxy module just as any other local module1142 //1143 object[] oldArgumentList = this.ArgumentList;1144 Version originalBaseMinimumVersion = BaseMinimumVersion;1145 Version originalBaseMaximumVersion = BaseMaximumVersion;1146 Version originalBaseRequiredVersion = BaseRequiredVersion;1147 try1148 {1149 this.ArgumentList = new object[] { psSession };1150 1151 // The correct module version has already been imported from the remote session and created locally.1152 // The locally created module always has a version of 1.0 regardless of the actual module version1153 // imported from the remote session, and version checking is no longer needed and will not work while1154 // importing this created local module.1155 BaseMinimumVersion = null;1156 BaseMaximumVersion = null;1157 BaseRequiredVersion = null;1158 ImportModule_LocallyViaName(importModuleOptions, wildcardEscapedPsd1Path);1159 }1160 finally1161 {1162 this.ArgumentList = oldArgumentList;1163 BaseMinimumVersion = originalBaseMinimumVersion;1164 BaseMaximumVersion = originalBaseMaximumVersion;1165 BaseRequiredVersion = originalBaseRequiredVersion;1166 }1167 1168 //1169 // make sure the temporary folder gets removed when the module is removed1170 //1171 string psm1Path = Path.Combine(temporaryModulePath, Path.GetFileName(temporaryModulePath) + ".psm1");1172 if (!TryGetFromModuleTable(psm1Path, out PSModuleInfo moduleInfo, toRemove: true))1173 {1174 if (Directory.Exists(temporaryModulePath))1175 {1176 Directory.Delete(temporaryModulePath, recursive: true);1177 }1178 1179 return null;1180 }1181 1182 const string onRemoveScriptBody = @"1183 Microsoft.PowerShell.Management\Remove-Item `1184 -LiteralPath $temporaryModulePath `1185 -Force `1186 -Recurse `1187 -ErrorAction SilentlyContinue1188 1189 if ($null -ne $previousOnRemoveScript)1190 {1191 & $previousOnRemoveScript $args1192 }1193 ";1194 ScriptBlock onRemoveScriptBlock = this.Context.Engine.ParseScriptBlock(onRemoveScriptBody, false);1195 onRemoveScriptBlock = onRemoveScriptBlock.GetNewClosure(); // create a separate scope for variables set below1196 onRemoveScriptBlock.Module.SessionState.PSVariable.Set("temporaryModulePath", temporaryModulePath);1197 onRemoveScriptBlock.Module.SessionState.PSVariable.Set("previousOnRemoveScript", moduleInfo.OnRemove);1198 moduleInfo.OnRemove = onRemoveScriptBlock;1199 1200 return moduleInfo;