diff --git a/src/System.Management.Automation/DscSupport/CimDSCParser.cs b/src/System.Management.Automation/DscSupport/CimDSCParser.cs index ea7501a8a2d..3079155097f 100644 --- a/src/System.Management.Automation/DscSupport/CimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/CimDSCParser.cs @@ -469,6 +469,47 @@ internal void ValidateInstanceText(string classText) namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal { + /// + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", + Justification = "Needed Internal use only")] + internal class DscClassCacheEntry + { + /// + /// Store the RunAs Credentials that this DSC resource will use + /// + public DSCResourceRunAsCredential DscResRunAsCred; + + /// + /// If we have implicitly imported this resource, we will set this field to true. This will + /// only happen to InBox resources. + /// + public bool IsImportedImplicitly; + + /// + /// A CimClass instance for this resource + /// + public Microsoft.Management.Infrastructure.CimClass CimClassInstance; + + /// + /// Default constructor to initiale variables with default values + /// + public DscClassCacheEntry() : this(DSCResourceRunAsCredential.Default, false, null) { } + + /// + /// Constructor used in code to initialze all values + /// + /// + /// + /// + public DscClassCacheEntry(DSCResourceRunAsCredential aDSCResourceRunAsCredential, bool aIsImportedImplicitly, Microsoft.Management.Infrastructure.CimClass aCimClassInstance) + { + DscResRunAsCred = aDSCResourceRunAsCredential; + IsImportedImplicitly = aIsImportedImplicitly; + CimClassInstance = aCimClassInstance; + } + } + /// /// [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", @@ -486,6 +527,7 @@ public static class DscClassCache private const int IndexModuleName = 0; private const int IndexModuleVersion = 1; private const int IndexClassName = 2; + private const int IndexFriendlyName = 3; // Create a list of classes which are not actual DSC resources similar to what we do inside PSDesiredStateConfiguration.psm1 private static readonly string[] s_hiddenResourceList = @@ -505,15 +547,15 @@ public static class DscClassCache /// /// DSC class cache for this runspace. - /// Cache stores the DSCRunAsBehavior for the class along with actual cim class. + /// Cache stores the DSCRunAsBehavior, cim class and boolean to indicate if an Inbox resource has been implicitly imported. /// - private static Dictionary> ClassCache + private static Dictionary ClassCache { get { if (t_classCache == null) { - t_classCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); + t_classCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } return t_classCache; @@ -521,7 +563,7 @@ public static class DscClassCache } [ThreadStatic] - private static Dictionary> t_classCache; + private static Dictionary t_classCache; /// /// DSC classname to source module mapper. @@ -727,12 +769,12 @@ public static void Initialize(Collection errors, List moduleP } // Load Regular and DSC PS modules - bool isInboxResource = false; + bool importInBoxResourcesImplicitly = false; List modulePaths = new List(); if (modulePathList == null || modulePathList.Count == 0) { modulePaths.Add(Path.Combine(configSystemPath, inboxModulePath)); - isInboxResource = true; + importInBoxResourcesImplicitly = true; } else { @@ -750,7 +792,7 @@ public static void Initialize(Collection errors, List moduleP } } - LoadDSCResourceIntoCache(errors, modulePaths, isInboxResource); + LoadDSCResourceIntoCache(errors, modulePaths, importInBoxResourcesImplicitly); } } @@ -759,10 +801,10 @@ public static void Initialize(Collection errors, List moduleP /// /// Collection of any errors encountered during initialization. /// Module path from where DSC PS modules will be loaded. - /// + /// /// if module is inbox. /// - private static void LoadDSCResourceIntoCache(Collection errors, List modulePathList, bool isInboxResource) + private static void LoadDSCResourceIntoCache(Collection errors, List modulePathList, bool importInBoxResourcesImplicitly) { foreach (string moduleDir in modulePathList) { @@ -779,7 +821,7 @@ private static void LoadDSCResourceIntoCache(Collection errors, List< continue; } - Tuple moduleInfo = GetModuleInfoHelper(moduleDir, isInboxResource, isPsProviderModule: false); + Tuple moduleInfo = GetModuleInfoHelper(moduleDir, importInBoxResourcesImplicitly, isPsProviderModule: false); if (moduleInfo == null) { continue; @@ -787,7 +829,7 @@ private static void LoadDSCResourceIntoCache(Collection errors, List< foreach (string schemaFile in schemaFiles) { - ImportClasses(schemaFile, moduleInfo, errors); + ImportClasses(schemaFile, moduleInfo, errors, importInBoxResourcesImplicitly); } } } @@ -800,17 +842,17 @@ private static void LoadDSCResourceIntoCache(Collection errors, List< /// /// Path to the module folder /// - /// - /// if module is inbox. + /// + /// if module is inbox and we are importing resources implicitly /// /// /// Indicate a internal DSC module /// /// - private static Tuple GetModuleInfoHelper(string moduleFolderPath, bool isInboxResource, bool isPsProviderModule) + private static Tuple GetModuleInfoHelper(string moduleFolderPath, bool importInBoxResourcesImplicitly, bool isPsProviderModule) { string moduleName = "PsDesiredStateConfiguration"; - if (!isInboxResource) + if (!importInBoxResourcesImplicitly) { moduleName = Path.GetFileName(moduleFolderPath); } @@ -875,12 +917,12 @@ private static Tuple GetModuleInfoHelper(string moduleFolderPat // Callback implementation... private static CimClass MyClassCallback(string serverName, string namespaceName, string className) { - foreach (KeyValuePair> cimClass in ClassCache) + foreach (KeyValuePair cimClass in ClassCache) { string cachedClassName = cimClass.Key.Split(Utils.Separators.Backslash)[IndexClassName]; if (string.Compare(cachedClassName, className, StringComparison.OrdinalIgnoreCase) == 0) { - return cimClass.Value.Item2; + return cimClass.Value.CimClassInstance; } } @@ -893,8 +935,9 @@ private static CimClass MyClassCallback(string serverName, string namespaceName, /// /// /// + /// /// - public static List ImportClasses(string path, Tuple moduleInfo, Collection errors) + public static List ImportClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly = false) { if (string.IsNullOrEmpty(path)) { @@ -926,12 +969,14 @@ public static List ImportClasses(string path, Tuple m { // Only add the class once... var className = c.CimSystemProperties.ClassName; - string moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleInfo.Item1, moduleInfo.Item2.ToString(), className); - Tuple cimClassInfo; + string alias = GetFriendlyName(c); + var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; + string moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleInfo.Item1, moduleInfo.Item2.ToString(), className, friendlyName); + DscClassCacheEntry cimClassInfo; if (ClassCache.TryGetValue(moduleQualifiedResourceName, out cimClassInfo)) { - CimClass cimClass = cimClassInfo.Item2; + CimClass cimClass = cimClassInfo.CimClassInstance; // If this is a nested object and we already have exactly same nested object, we will // allow sharing of nested objects. if (!IsSameNestedObject(cimClass, c)) @@ -956,14 +1001,19 @@ public static List ImportClasses(string path, Tuple m if (!CacheResourcesFromMultipleModuleVersions) { // Find & remove the previous version of the resource. - List>> resourceList = FindResourceInCache(moduleInfo.Item1, className); + List> resourceList = FindResourceInCache(moduleInfo.Item1, className, friendlyName); if (resourceList.Count > 0 && !string.IsNullOrEmpty(resourceList[0].Key)) { ClassCache.Remove(resourceList[0].Key); + // keyword is already defined and it is a Inbox resource, remove it + if (DynamicKeyword.ContainsKeyword(friendlyName) && resourceList[0].Value.IsImportedImplicitly) + { + DynamicKeyword.RemoveKeyword(friendlyName); + } } } - ClassCache[moduleQualifiedResourceName] = new Tuple(DSCResourceRunAsCredential.Default, c); + ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(DSCResourceRunAsCredential.Default, importInBoxResourcesImplicitly, c); ByClassModuleCache[className] = moduleInfo; } @@ -1023,10 +1073,11 @@ public static void ClearCache() /// /// /// + /// /// - private static string GetModuleQualifiedResourceName(string moduleName, string moduleVersion, string className) + private static string GetModuleQualifiedResourceName(string moduleName, string moduleVersion, string className, string resourceName) { - return string.Format(CultureInfo.InvariantCulture, "{0}\\{1}\\{2}", moduleName, moduleVersion, className); + return String.Format(CultureInfo.InvariantCulture, "{0}\\{1}\\{2}\\{3}", moduleName, moduleVersion, className, resourceName); } /// @@ -1034,22 +1085,25 @@ private static string GetModuleQualifiedResourceName(string moduleName, string m /// /// Module name. /// Resource type name. + /// Resource friendly name /// List of found resources in the form of Dictionary{moduleQualifiedName, cimClass}, otherwise empty list. - private static List>> FindResourceInCache(string moduleName, string className) + private static List> FindResourceInCache(string moduleName, string className, string resourceName) { return (from cacheEntry in ClassCache let splittedName = cacheEntry.Key.Split(Utils.Separators.Backslash) let cachedClassName = splittedName[IndexClassName] let cachedModuleName = splittedName[IndexModuleName] - where string.Compare(cachedClassName, className, StringComparison.OrdinalIgnoreCase) == 0 - && string.Compare(cachedModuleName, moduleName, StringComparison.OrdinalIgnoreCase) == 0 + let cachedResourceName = splittedName[IndexFriendlyName] + where ((string.Compare(cachedResourceName, resourceName, StringComparison.OrdinalIgnoreCase) == 0) + || (string.Compare(cachedClassName, className, StringComparison.OrdinalIgnoreCase) == 0 + && string.Compare(cachedModuleName, moduleName, StringComparison.OrdinalIgnoreCase) == 0)) select cacheEntry).ToList(); } /// /// /// - public static List> GetCachedClasses() + private static List GetCachedClasses() { return ClassCache.Values.ToList(); } @@ -1063,11 +1117,11 @@ where string.Compare(cachedClassName, className, StringComparison.OrdinalIgnoreC { List cachedClasses = new List(); var moduleQualifiedName = string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", module.Name, module.Version.ToString()); - foreach (var pair in ClassCache) + foreach (var dscClassCacheEntry in ClassCache) { - if (pair.Key.StartsWith(moduleQualifiedName, StringComparison.OrdinalIgnoreCase)) + if(dscClassCacheEntry.Key.StartsWith(moduleQualifiedName, StringComparison.OrdinalIgnoreCase)) { - cachedClasses.Add(pair.Value.Item2); + cachedClasses.Add(dscClassCacheEntry.Value.CimClassInstance); } } @@ -1231,13 +1285,13 @@ public static Collection GetCachedKeywords() { Collection keywords = new Collection(); - foreach (KeyValuePair> cachedClass in ClassCache) + foreach (KeyValuePair cachedClass in ClassCache) { string[] splittedName = cachedClass.Key.Split(Utils.Separators.Backslash); string moduleName = splittedName[IndexModuleName]; string moduleVersion = splittedName[IndexModuleVersion]; - var keyword = CreateKeywordFromCimClass(moduleName, Version.Parse(moduleVersion), cachedClass.Value.Item2, null, cachedClass.Value.Item1); + var keyword = CreateKeywordFromCimClass(moduleName, Version.Parse(moduleVersion), cachedClass.Value.CimClassInstance, null, cachedClass.Value.DscResRunAsCred); if (keyword != null) { keywords.Add(keyword); @@ -1577,9 +1631,9 @@ private static void LoadDefaultCimKeywords(Dictionary funct foreach (var cimClass in GetCachedClasses()) { - var className = cimClass.Item2.CimSystemProperties.ClassName; + var className = cimClass.CimClassInstance.CimSystemProperties.ClassName; var moduleInfo = ByClassModuleCache[className]; - CreateAndRegisterKeywordFromCimClass(moduleInfo.Item1, moduleInfo.Item2, cimClass.Item2, functionsToDefine, cimClass.Item1); + CreateAndRegisterKeywordFromCimClass(moduleInfo.Item1, moduleInfo.Item2, cimClass.CimClassInstance, functionsToDefine, cimClass.DscResRunAsCred); } // And add the Node keyword definitions @@ -3090,20 +3144,26 @@ private static void ProcessMofForDynamicKeywords(PSModuleInfo module, ICollectio foreach (var c in parser.ParseSchemaMofFileBuffer(mof)) { var className = c.CimSystemProperties.ClassName; + string alias = GetFriendlyName(c); + var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; if (!CacheResourcesFromMultipleModuleVersions) { // Find & remove the previous version of the resource. - List>> resourceList = - FindResourceInCache(module.Name, className); + List> resourceList = FindResourceInCache(module.Name, className, friendlyName); if (resourceList.Count > 0 && !string.IsNullOrEmpty(resourceList[0].Key)) { ClassCache.Remove(resourceList[0].Key); + // keyword is already defined and it is a Inbox resource, remove it + if (DynamicKeyword.ContainsKeyword(friendlyName) && resourceList[0].Value.IsImportedImplicitly) + { + DynamicKeyword.RemoveKeyword(friendlyName); + } } } - var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className); - ClassCache[moduleQualifiedResourceName] = new Tuple(runAsBehavior, c); + var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); + ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c); ByClassModuleCache[className] = new Tuple(module.Name, module.Version); resourcesFound.Add(className); CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, runAsBehavior); @@ -3171,6 +3231,7 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou foreach (var c in classes) { CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, DSCResourceRunAsCredential.Default); + ClearImplicitlyImportedFlagFromResourceInClassCache(module, c); } } @@ -3204,6 +3265,7 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou if (string.Equals(alias, resourceName, StringComparison.OrdinalIgnoreCase)) { CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, DSCResourceRunAsCredential.Default); + ClearImplicitlyImportedFlagFromResourceInClassCache(module, c); return true; } } @@ -3221,6 +3283,21 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou return false; } + + /// + /// Clear the 'IsImportedImplicitly' flag when explicitly importing a resource + /// + /// + /// + private static void ClearImplicitlyImportedFlagFromResourceInClassCache(PSModuleInfo module, CimClass cimClass) + { + var className = cimClass.CimSystemProperties.ClassName; + var alias = GetFriendlyName(cimClass); + var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; + var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); + ClassCache[moduleQualifiedResourceName].IsImportedImplicitly = false; + } + /// /// Imports configuration keywords from a .psm1 file. ///