Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
GetModuleCommand.cs599 linesDownload Raw Back to Modules
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Diagnostics.CodeAnalysis;8using System.Globalization;9using System.IO;10using System.Linq;11using System.Management.Automation;12using System.Management.Automation.Internal;13using System.Management.Automation.Language;14using System.Management.Automation.Runspaces;15using System.Threading;16 17using Microsoft.Management.Infrastructure;18 19using Dbg = System.Management.Automation.Diagnostics;20 21//22// Now define the set of commands for manipulating modules.23//24 25namespace Microsoft.PowerShell.Commands26{27    /// <summary>28    /// Implements a cmdlet that gets the list of loaded modules...29    /// </summary>30    [Cmdlet(VerbsCommon.Get, "Module", DefaultParameterSetName = ParameterSet_Loaded,31        HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096696")]32    [OutputType(typeof(PSModuleInfo))]33    public sealed class GetModuleCommand : ModuleCmdletBase, IDisposable34    {35        #region Cmdlet parameters36 37        private const string ParameterSet_Loaded = "Loaded";38        private const string ParameterSet_AvailableLocally = "Available";39        private const string ParameterSet_AvailableInPsrpSession = "PsSession";40        private const string ParameterSet_AvailableInCimSession = "CimSession";41 42        /// <summary>43        /// This parameter specifies the current pipeline object.44        /// </summary>45        [Parameter(ParameterSetName = ParameterSet_Loaded, ValueFromPipeline = true, Position = 0)]46        [Parameter(ParameterSetName = ParameterSet_AvailableLocally, ValueFromPipeline = true, Position = 0)]47        [Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession, ValueFromPipeline = true, Position = 0)]48        [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession, ValueFromPipeline = true, Position = 0)]49        [ValidateNotNullOrEmpty]50        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays",51            Justification = "Cmdlets use arrays for parameters.")]52        public string[] Name { get; set; }53 54        /// <summary>55        /// This parameter specifies the current pipeline object.56        /// </summary>57        [Parameter(ParameterSetName = ParameterSet_Loaded, ValueFromPipelineByPropertyName = true)]58        [Parameter(ParameterSetName = ParameterSet_AvailableLocally, ValueFromPipelineByPropertyName = true)]59        [Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession, ValueFromPipelineByPropertyName = true)]60        [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession, ValueFromPipelineByPropertyName = true)]61        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays",62            Justification = "Cmdlets use arrays for parameters.")]63        public ModuleSpecification[] FullyQualifiedName { get; set; }64 65        /// <summary>66        /// If specified, all loaded modules should be returned, otherwise only the visible67        /// modules should be returned.68        /// </summary>69        [Parameter(ParameterSetName = ParameterSet_Loaded)]70        [Parameter(ParameterSetName = ParameterSet_AvailableLocally)]71        public SwitchParameter All { get; set; }72 73        /// <summary>74        /// If specified, then Get-Module will return the set of available modules...75        /// </summary>76        [Parameter(ParameterSetName = ParameterSet_AvailableLocally, Mandatory = true)]77        [Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession)]78        [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession)]79        public SwitchParameter ListAvailable { get; set; }80 81        /// <summary>82        /// If specified, then Get-Module will return the set of available modules which supports the specified PowerShell edition...83        /// </summary>84        [Parameter(ParameterSetName = ParameterSet_AvailableLocally)]85        [Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession)]86        [ArgumentCompleter(typeof(PSEditionArgumentCompleter))]87        public string PSEdition { get; set; }88 89        /// <summary>90        /// When set, CompatiblePSEditions checking is disabled for modules in the System32 (Windows PowerShell) module directory.91        /// </summary>92        [Parameter(ParameterSetName = ParameterSet_AvailableLocally)]93        [Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession)]94        [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession)]95        public SwitchParameter SkipEditionCheck96        {97            get { return (SwitchParameter)BaseSkipEditionCheck; }98 99            set { BaseSkipEditionCheck = value; }100        }101 102        /// <summary>103        /// If specified, then Get-Module refreshes the internal cmdlet analysis cache.104        /// </summary>105        [Parameter(ParameterSetName = ParameterSet_AvailableLocally)]106        [Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession)]107        [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession)]108        public SwitchParameter Refresh { get; set; }109 110        /// <summary>111        /// If specified, then Get-Module will attempt to discover PowerShell modules on a remote computer using the specified session.112        /// </summary>113        [Parameter(ParameterSetName = ParameterSet_AvailableInPsrpSession, Mandatory = true)]114        [ValidateNotNull]115        public PSSession PSSession { get; set; }116 117        /// <summary>118        /// If specified, then Get-Module will attempt to discover PS-CIM modules on a remote computer using the specified session.119        /// </summary>120        [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession, Mandatory = true)]121        [ValidateNotNull]122        public CimSession CimSession { get; set; }123 124        /// <summary>125        /// For interoperability with 3rd party CIM servers, user can specify custom resource URI.126        /// </summary>127        [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession, Mandatory = false)]128        [ValidateNotNull]129        public Uri CimResourceUri { get; set; }130 131        /// <summary>132        /// For interoperability with 3rd party CIM servers, user can specify custom namespace.133        /// </summary>134        [Parameter(ParameterSetName = ParameterSet_AvailableInCimSession, Mandatory = false)]135        [ValidateNotNullOrEmpty]136        public string CimNamespace { get; set; }137 138        #endregion Cmdlet parameters139 140        #region Remote discovery141 142        private IEnumerable<PSModuleInfo> GetAvailableViaPsrpSessionCore(string[] moduleNames, Runspace remoteRunspace)143        {144            Dbg.Assert(remoteRunspace != null, "Caller should verify remoteRunspace != null");145 146            using (var powerShell = System.Management.Automation.PowerShell.Create())147            {148                powerShell.Runspace = remoteRunspace;149                powerShell.AddCommand("Get-Module");150                powerShell.AddParameter("ListAvailable", true);151 152                if (Refresh.IsPresent)153                {154                    powerShell.AddParameter("Refresh", true);155                }156 157                if (moduleNames != null)158                {159                    powerShell.AddParameter("Name", moduleNames);160                }161 162                string errorMessageTemplate = string.Format(163                    CultureInfo.InvariantCulture,164                    Modules.RemoteDiscoveryRemotePsrpCommandFailed,165                    "Get-Module");166                foreach (167                    PSObject outputObject in168                        RemoteDiscoveryHelper.InvokePowerShell(powerShell, this, errorMessageTemplate,169                                                               this.CancellationToken))170                {171                    PSModuleInfo moduleInfo = RemoteDiscoveryHelper.RehydratePSModuleInfo(outputObject);172                    yield return moduleInfo;173                }174            }175        }176 177        private static PSModuleInfo GetModuleInfoForRemoteModuleWithoutManifest(RemoteDiscoveryHelper.CimModule cimModule)178        {179            return new PSModuleInfo(cimModule.ModuleName, null, null);180        }181 182        private PSModuleInfo ConvertCimModuleInfoToPSModuleInfo(RemoteDiscoveryHelper.CimModule cimModule,183                                                                string computerName)184        {185            try186            {187                bool containedErrors = false;188 189                if (cimModule.MainManifest == null)190                {191                    return GetModuleInfoForRemoteModuleWithoutManifest(cimModule);192                }193 194                string temporaryModuleManifestPath = Path.Combine(195                    RemoteDiscoveryHelper.GetModulePath(cimModule.ModuleName, null, computerName,196                                                        this.Context.CurrentRunspace),197                    Path.GetFileName(cimModule.ModuleName));198 199                Hashtable mainData = null;200                if (!containedErrors)201                {202                    mainData = RemoteDiscoveryHelper.ConvertCimModuleFileToManifestHashtable(203                        cimModule.MainManifest,204                        temporaryModuleManifestPath,205                        this,206                        ref containedErrors);207                    if (mainData == null)208                    {209                        return GetModuleInfoForRemoteModuleWithoutManifest(cimModule);210                    }211                }212 213                if (!containedErrors)214                {215                    mainData = RemoteDiscoveryHelper.RewriteManifest(mainData);216                }217 218                Hashtable localizedData = mainData; // TODO/FIXME - this needs full path support from the provider219 220                PSModuleInfo moduleInfo = null;221                if (!containedErrors)222                {223                    ImportModuleOptions throwAwayOptions = new ImportModuleOptions();224                    moduleInfo = LoadModuleManifest(225                        temporaryModuleManifestPath,226                        null, // scriptInfo227                        mainData,228                        localizedData,229                        0 /* - don't write errors, don't load elements, don't return null on first error */,230                        this.BaseMinimumVersion,231                        this.BaseMaximumVersion,232                        this.BaseRequiredVersion,233                        this.BaseGuid,234                        ref throwAwayOptions,235                        ref containedErrors);236                }237 238                if ((moduleInfo == null) || containedErrors)239                {240                    moduleInfo = GetModuleInfoForRemoteModuleWithoutManifest(cimModule);241                }242 243                return moduleInfo;244            }245            catch (Exception e)246            {247                ErrorRecord errorRecord = RemoteDiscoveryHelper.GetErrorRecordForProcessingOfCimModule(e, cimModule.ModuleName);248                this.WriteError(errorRecord);249                return null;250            }251        }252 253        private IEnumerable<PSModuleInfo> GetAvailableViaCimSessionCore(IEnumerable<string> moduleNames,254                                                                        CimSession cimSession, Uri resourceUri,255                                                                        string cimNamespace)256        {257            IEnumerable<RemoteDiscoveryHelper.CimModule> remoteModules = RemoteDiscoveryHelper.GetCimModules(258                cimSession,259                resourceUri,260                cimNamespace,261                moduleNames,262                true /* onlyManifests */,263                this,264                this.CancellationToken);265 266            IEnumerable<PSModuleInfo> remoteModuleInfos = remoteModules267                .Select(cimModule => this.ConvertCimModuleInfoToPSModuleInfo(cimModule, cimSession.ComputerName))268                .Where(static moduleInfo => moduleInfo != null);269 270            return remoteModuleInfos;271        }272 273        #endregion Remote discovery274 275        #region Cancellation support276 277        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();278 279        private CancellationToken CancellationToken280        {281            get { return _cancellationTokenSource.Token; }282        }283 284        /// <summary>285        /// When overridden in the derived class, interrupts currently286        /// running code within the command. It should interrupt BeginProcessing,287        /// ProcessRecord, and EndProcessing.288        /// Default implementation in the base class just returns.289        /// </summary>290        protected override void StopProcessing()291        {292            _cancellationTokenSource.Cancel();293        }294 295        #endregion296 297        #region IDisposable Members298 299        /// <summary>300        /// Release all resources.301        /// </summary>302        public void Dispose()303        {304            if (_disposed)305            {306                return;307            }308 309            _cancellationTokenSource.Dispose();310            311            _disposed = true;312        }313 314        private bool _disposed;315 316        #endregion317 318        private void AssertListAvailableMode()319        {320            if (!this.ListAvailable.IsPresent)321            {322                string errorMessage = Modules.RemoteDiscoveryWorksOnlyInListAvailableMode;323                ArgumentException argumentException = new ArgumentException(errorMessage);324                ErrorRecord errorRecord = new ErrorRecord(325                    argumentException,326                    "RemoteDiscoveryWorksOnlyInListAvailableMode",327                    ErrorCategory.InvalidArgument,328                    null);329                this.ThrowTerminatingError(errorRecord);330            }331        }332 333        /// <summary>334        /// Write out the specified modules...335        /// </summary>336        protected override void ProcessRecord()337        {338            // Name and FullyQualifiedName should not be specified at the same time.339            // Throw out terminating error if this is the case.340            if ((Name != null) && (FullyQualifiedName != null))341            {342                string errMsg = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, "Name", "FullyQualifiedName");343                ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "NameAndFullyQualifiedNameCannotBeSpecifiedTogether", ErrorCategory.InvalidOperation, null);344                ThrowTerminatingError(error);345            }346 347            // -SkipEditionCheck only makes sense for -ListAvailable (otherwise the module is already loaded)348            if (SkipEditionCheck && !ListAvailable)349            {350                ErrorRecord error = new ErrorRecord(351                    new InvalidOperationException(Modules.SkipEditionCheckNotSupportedWithoutListAvailable),352                    nameof(Modules.SkipEditionCheckNotSupportedWithoutListAvailable),353                    ErrorCategory.InvalidOperation,354                    targetObject: null);355 356                ThrowTerminatingError(error);357            }358 359            var strNames = new List<string>();360            if (Name != null)361            {362                strNames.AddRange(Name);363            }364 365            var moduleSpecTable = new Dictionary<string, ModuleSpecification>(StringComparer.OrdinalIgnoreCase);366            if (FullyQualifiedName != null)367            {368                for (int modSpecIndex = 0; modSpecIndex < FullyQualifiedName.Length; modSpecIndex++)369                {370                    FullyQualifiedName[modSpecIndex] = FullyQualifiedName[modSpecIndex].WithNormalizedName(Context, SessionState.Path.CurrentLocation.Path);371                }372 373                moduleSpecTable = FullyQualifiedName.ToDictionary(static moduleSpecification => moduleSpecification.Name, StringComparer.OrdinalIgnoreCase);374                strNames.AddRange(FullyQualifiedName.Select(static spec => spec.Name));375            }376 377            string[] names = strNames.Count > 0 ? strNames.ToArray() : null;378 379            if (ParameterSetName.Equals(ParameterSet_Loaded, StringComparison.OrdinalIgnoreCase))380            {381                AssertNameDoesNotResolveToAPath(names,382                                                Modules.ModuleDiscoveryForLoadedModulesWorksOnlyForUnQualifiedNames,383                                                "ModuleDiscoveryForLoadedModulesWorksOnlyForUnQualifiedNames");384                GetLoadedModules(names, moduleSpecTable, this.All);385            }386            else if (ParameterSetName.Equals(ParameterSet_AvailableLocally, StringComparison.OrdinalIgnoreCase))387            {388                if (ListAvailable.IsPresent)389                {390                    GetAvailableLocallyModules(names, moduleSpecTable, this.All);391                }392                else393                {394                    AssertNameDoesNotResolveToAPath(names,395                                                    Modules.ModuleDiscoveryForLoadedModulesWorksOnlyForUnQualifiedNames,396                                                    "ModuleDiscoveryForLoadedModulesWorksOnlyForUnQualifiedNames");397                    GetLoadedModules(names, moduleSpecTable, this.All);398                }399            }400            else if (ParameterSetName.Equals(ParameterSet_AvailableInPsrpSession, StringComparison.OrdinalIgnoreCase))401            {402                AssertListAvailableMode();403                AssertNameDoesNotResolveToAPath(names,404                                                Modules.RemoteDiscoveryWorksOnlyForUnQualifiedNames,405                                                "RemoteDiscoveryWorksOnlyForUnQualifiedNames");406 407                GetAvailableViaPsrpSession(names, moduleSpecTable, this.PSSession);408            }409            else if (ParameterSetName.Equals(ParameterSet_AvailableInCimSession, StringComparison.OrdinalIgnoreCase))410            {411                AssertListAvailableMode();412                AssertNameDoesNotResolveToAPath(names,413                                                Modules.RemoteDiscoveryWorksOnlyForUnQualifiedNames,414                                                "RemoteDiscoveryWorksOnlyForUnQualifiedNames");415 416                GetAvailableViaCimSession(names, moduleSpecTable, this.CimSession,417                                          this.CimResourceUri, this.CimNamespace);418            }419            else420            {421                Dbg.Assert(false, "Unrecognized parameter set");422            }423        }424 425        private void AssertNameDoesNotResolveToAPath(string[] names, string stringFormat, string resourceId)426        {427            if (names != null)428            {429                foreach (var n in names)430                {431                    if (n.Contains(StringLiterals.DefaultPathSeparator) || n.Contains(StringLiterals.AlternatePathSeparator))432                    {433                        string errorMessage = StringUtil.Format(stringFormat, n);434                        var argumentException = new ArgumentException(errorMessage);435                        var errorRecord = new ErrorRecord(436                            argumentException,437                            resourceId,438                            ErrorCategory.InvalidArgument,439                            n);440                        this.ThrowTerminatingError(errorRecord);441                    }442                }443            }444        }445 446        private void GetAvailableViaCimSession(IEnumerable<string> names, IDictionary<string, ModuleSpecification> moduleSpecTable,447                                               CimSession cimSession, Uri resourceUri, string cimNamespace)448        {449            IEnumerable<PSModuleInfo> remoteModules = GetAvailableViaCimSessionCore(names, cimSession, resourceUri, cimNamespace);450 451            foreach (PSModuleInfo remoteModule in FilterModulesForEditionAndSpecification(remoteModules, moduleSpecTable))452            {453                RemoteDiscoveryHelper.AssociatePSModuleInfoWithSession(remoteModule, cimSession, resourceUri,454                                                                       cimNamespace);455                this.WriteObject(remoteModule);456            }457        }458 459        private void GetAvailableViaPsrpSession(string[] names, IDictionary<string, ModuleSpecification> moduleSpecTable, PSSession session)460        {461            IEnumerable<PSModuleInfo> remoteModules = GetAvailableViaPsrpSessionCore(names, session.Runspace);462 463            foreach (PSModuleInfo remoteModule in FilterModulesForEditionAndSpecification(remoteModules, moduleSpecTable))464            {465                RemoteDiscoveryHelper.AssociatePSModuleInfoWithSession(remoteModule, session);466                this.WriteObject(remoteModule);467            }468        }469 470        private void GetAvailableLocallyModules(string[] names, IDictionary<string, ModuleSpecification> moduleSpecTable, bool all)471        {472            IEnumerable<PSModuleInfo> modules = GetModule(names, all, Refresh);473            foreach (PSModuleInfo module in FilterModulesForEditionAndSpecification(modules, moduleSpecTable))474            {475                var psModule = new PSObject(module);476                psModule.TypeNames.Insert(0, "ModuleInfoGrouping");477                WriteObject(psModule);478            }479        }480 481        private void GetLoadedModules(string[] names, IDictionary<string, ModuleSpecification> moduleSpecTable, bool all)482        {483            var modulesToWrite = Context.Modules.GetModules(names, all);484 485            foreach (PSModuleInfo moduleInfo in FilterModulesForEditionAndSpecification(modulesToWrite, moduleSpecTable))486            {487                WriteObject(moduleInfo);488            }489        }490 491        /// <summary>492        /// Filter an enumeration of PowerShell modules based on the required PowerShell edition493        /// and the module specification constraints set for each module (if any).494        /// </summary>495        /// <param name="modules">The modules to filter through.</param>496        /// <param name="moduleSpecificationTable">Module constraints, keyed by module name, to filter modules of that name by.</param>497        /// <returns>All modules from the original input that meet both any module edition and module specification constraints provided.</returns>498        private IEnumerable<PSModuleInfo> FilterModulesForEditionAndSpecification(499            IEnumerable<PSModuleInfo> modules,500            IDictionary<string, ModuleSpecification> moduleSpecificationTable)501        {502#if !UNIX503            // Edition check only applies to Windows System32 module path504            if (!SkipEditionCheck && ListAvailable && !All)505            {506                modules = modules.Where(static module => module.IsConsideredEditionCompatible);507            }508#endif509 510            if (!string.IsNullOrEmpty(PSEdition))511            {512                modules = modules.Where(module => module.CompatiblePSEditions.Contains(PSEdition, StringComparer.OrdinalIgnoreCase));513            }514 515            if (moduleSpecificationTable != null && moduleSpecificationTable.Count > 0)516            {517                modules = FilterModulesForSpecificationMatch(modules, moduleSpecificationTable);518            }519 520            return modules;521        }522 523        /// <summary>524        /// Take an enumeration of modules and only return those that match a specification525        /// in the given specification table, or have no corresponding entry in the specification table.526        /// </summary>527        /// <param name="modules">The modules to filter by specification match.</param>528        /// <param name="moduleSpecificationTable">The specification lookup table to filter the modules on.</param>529        /// <returns>The modules that match their corresponding table entry, or which have no table entry.</returns>530        private static IEnumerable<PSModuleInfo> FilterModulesForSpecificationMatch(531            IEnumerable<PSModuleInfo> modules,532            IDictionary<string, ModuleSpecification> moduleSpecificationTable)533        {534            Dbg.Assert(moduleSpecificationTable != null, $"Caller to verify that {nameof(moduleSpecificationTable)} is not null");535            Dbg.Assert(moduleSpecificationTable.Count != 0, $"Caller to verify that {nameof(moduleSpecificationTable)} is not empty");536 537            foreach (PSModuleInfo module in modules)538            {539                IEnumerable<ModuleSpecification> candidateModuleSpecs = GetCandidateModuleSpecs(moduleSpecificationTable, module);540 541                // Modules with table entries only get returned if they match them542                // We skip the name check since modules have already been prefiltered base on the moduleSpec path/name543                foreach (ModuleSpecification moduleSpec in candidateModuleSpecs)544                {545                    if (ModuleIntrinsics.IsModuleMatchingModuleSpec(module, moduleSpec, skipNameCheck: true))546                    {547                        yield return module;548                    }549                }550            }551        }552 553        /// <summary>554        /// Take a dictionary of module specifications and return those that potentially match the module555        /// passed in as a parameter (checks on names and paths).556        /// </summary>557        /// <param name="moduleSpecTable">The module specifications to filter candidates from.</param>558        /// <param name="module">The module to find candidates for from the module specification table.</param>559        /// <returns>The module specifications matching the module based on name, path and subpath.</returns>560        private static IEnumerable<ModuleSpecification> GetCandidateModuleSpecs(561            IDictionary<string, ModuleSpecification> moduleSpecTable,562            PSModuleInfo module)563        {564            const WildcardOptions options = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant;565            foreach (ModuleSpecification moduleSpec in moduleSpecTable.Values)566            {567                WildcardPattern namePattern = WildcardPattern.Get(moduleSpec.Name, options);568                if (namePattern.IsMatch(module.Name) || moduleSpec.Name == module.Path || module.Path.Contains(moduleSpec.Name))569                {570                    yield return moduleSpec;571                }572            }573        }574    }575 576    /// <summary>577    /// Provides argument completion for PSEdition parameter.578    /// </summary>579    public class PSEditionArgumentCompleter : IArgumentCompleter580    {581        /// <summary>582        /// Returns completion results for PSEdition parameter.583        /// </summary>584        /// <param name="commandName">The command name.</param>585        /// <param name="parameterName">The parameter name.</param>586        /// <param name="wordToComplete">The word to complete.</param>587        /// <param name="commandAst">The command AST.</param>588        /// <param name="fakeBoundParameters">The fake bound parameters.</param>589        /// <returns>List of completion results.</returns>590        public IEnumerable<CompletionResult> CompleteArgument(591            string commandName,592            string parameterName,593            string wordToComplete,594            CommandAst commandAst,595            IDictionary fakeBoundParameters)596                => CompletionHelpers.GetMatchingResults(wordToComplete, possibleCompletionValues: Utils.AllowedEditionValues);597    }598}599