winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

DscModuleV2.cs (11520B)


      1 // -----------------------------------------------------------------------------
      2 // <copyright file="DscModuleV2.cs" company="Microsoft Corporation">
      3 //     Copyright (c) Microsoft Corporation. Licensed under the MIT License.
      4 // </copyright>
      5 // -----------------------------------------------------------------------------
      6 
      7 namespace Microsoft.Management.Configuration.Processor.PowerShell.DscModules
      8 {
      9     using System;
     10     using System.Collections;
     11     using System.Collections.Generic;
     12     using System.Collections.ObjectModel;
     13     using System.Linq;
     14     using System.Management.Automation;
     15     using Microsoft.Management.Configuration.Processor.Exceptions;
     16     using Microsoft.Management.Configuration.Processor.Extensions;
     17     using Microsoft.Management.Configuration.Processor.Helpers;
     18     using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo;
     19     using Microsoft.Management.Configuration.Processor.PowerShell.Extensions;
     20     using Microsoft.Management.Configuration.Processor.PowerShell.Helpers;
     21     using Microsoft.PowerShell.Commands;
     22     using Windows.Foundation.Collections;
     23     using static Microsoft.Management.Configuration.Processor.PowerShell.Constants.PowerShellConstants;
     24 
     25     /// <summary>
     26     /// PSDesiredStateConfiguration v2.
     27     /// </summary>
     28     internal class DscModuleV2 : IDscModule
     29     {
     30         private const string InDesiredState = "InDesiredState";
     31         private const string RebootRequired = "RebootRequired";
     32 
     33         private static readonly IEnumerable<string> ExclusionResourcesParentPath = new string[]
     34         {
     35             @"C:\WINDOWS\system32\WindowsPowershell\v1.0\Modules\PsDesiredStateConfiguration\DscResources",
     36             @"C:\Program Files\WindowsPowerShell\Modules\PackageManagement\1.0.0.1",
     37         };
     38 
     39         /// <summary>
     40         /// Initializes a new instance of the <see cref="DscModuleV2"/> class.
     41         /// </summary>
     42         public DscModuleV2()
     43         {
     44             this.ModuleSpecification = PowerShellHelpers.CreateModuleSpecification(
     45                 Modules.PSDesiredStateConfiguration,
     46                 minVersion: Modules.PSDesiredStateConfigurationMinVersion,
     47                 maxVersion: Modules.PSDesiredStateConfigurationMaxVersion);
     48         }
     49 
     50         /// <inheritdoc/>
     51         public ModuleSpecification ModuleSpecification { get; private init; }
     52 
     53         /// <inheritdoc/>
     54         public string GetDscResourceCmd { get; } = Commands.GetDscResource;
     55 
     56         /// <inheritdoc/>
     57         public string InvokeDscResourceCmd { get; } = Commands.InvokeDscResource;
     58 
     59         /// <inheritdoc/>
     60         public IReadOnlyList<DscResourceInfoInternal> GetAllDscResources(PowerShell pwsh)
     61         {
     62             return this.GetDscResources(pwsh, null, null);
     63         }
     64 
     65         /// <inheritdoc/>
     66         public IReadOnlyList<DscResourceInfoInternal> GetDscResourcesInModule(
     67             PowerShell pwsh,
     68             ModuleSpecification moduleSpecification)
     69         {
     70             return this.GetDscResources(pwsh, null, moduleSpecification);
     71         }
     72 
     73         /// <inheritdoc/>
     74         public DscResourceInfoInternal? GetDscResource(
     75             PowerShell pwsh,
     76             string name,
     77             ModuleSpecification? moduleSpecification)
     78         {
     79             var dscResourceInfos = this.GetDscResources(pwsh, name, moduleSpecification);
     80 
     81             if (dscResourceInfos.Count == 0)
     82             {
     83                 return null;
     84             }
     85             else if (dscResourceInfos.Count > 1)
     86             {
     87                 throw new GetDscResourceMultipleMatches(name, moduleSpecification);
     88             }
     89 
     90             return dscResourceInfos[0];
     91         }
     92 
     93         /// <inheritdoc/>
     94         public ValueSet InvokeGetResource(
     95             PowerShell pwsh,
     96             ValueSet settings,
     97             string name,
     98             ModuleSpecification? moduleSpecification)
     99         {
    100             PSObject? getResult = null;
    101 
    102             try
    103             {
    104                 getResult = pwsh.AddCommand(this.InvokeDscResourceCmd)
    105                                 .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification))
    106                                 .AddParameter(Parameters.Method, DscMethods.Get)
    107                                 .InvokeAndStopOnError()
    108                                 .FirstOrDefault();
    109             }
    110             catch (Exception ex)
    111             {
    112                 throw new InvokeDscResourceException(InvokeDscResourceException.Get, name, moduleSpecification, ex);
    113             }
    114 
    115             string? errorMessage = pwsh.GetErrorMessage();
    116             if (errorMessage is not null)
    117             {
    118                 throw new InvokeDscResourceException(InvokeDscResourceException.Get, name, moduleSpecification, errorMessage);
    119             }
    120 
    121             if (getResult is null)
    122             {
    123                 throw new InvokeDscResourceException(InvokeDscResourceException.Get, name, moduleSpecification);
    124             }
    125 
    126             // Script based resource.
    127             if (getResult.BaseObject is Hashtable)
    128             {
    129                 Hashtable hashTable = (Hashtable)getResult.BaseObject;
    130                 var resultSettings = new ValueSet();
    131                 foreach (DictionaryEntry entry in hashTable)
    132                 {
    133                     resultSettings.Add(entry.Key as string, entry.Value);
    134                 }
    135 
    136                 return resultSettings;
    137             }
    138 
    139             // Class-based resource.
    140             return TypeHelpers.GetAllPropertiesValues(getResult.BaseObject);
    141         }
    142 
    143         /// <inheritdoc/>
    144         public bool InvokeTestResource(
    145             PowerShell pwsh,
    146             ValueSet settings,
    147             string name,
    148             ModuleSpecification? moduleSpecification)
    149         {
    150             // Returned type is InvokeDscResourceTestResult which is a PowerShell classed defined
    151             // in PSDesiredStateConfiguration.psm1.
    152             dynamic? testResult = null;
    153 
    154             try
    155             {
    156                 testResult = pwsh.AddCommand(this.InvokeDscResourceCmd)
    157                                  .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification))
    158                                  .AddParameter(Parameters.Method, DscMethods.Test)
    159                                  .InvokeAndStopOnError()
    160                                  .FirstOrDefault();
    161             }
    162             catch (Exception ex)
    163             {
    164                 throw new InvokeDscResourceException(InvokeDscResourceException.Test, name, moduleSpecification, ex);
    165             }
    166 
    167             string? errorMessage = pwsh.GetErrorMessage();
    168             if (errorMessage is not null)
    169             {
    170                 throw new InvokeDscResourceException(InvokeDscResourceException.Test, name, moduleSpecification, errorMessage);
    171             }
    172 
    173             if (testResult is null ||
    174                 !TypeHelpers.PropertyWithTypeExists<bool>(testResult, InDesiredState))
    175             {
    176                 throw new InvokeDscResourceException(InvokeDscResourceException.Test, name, moduleSpecification);
    177             }
    178 
    179             return testResult?.InDesiredState;
    180         }
    181 
    182         /// <inheritdoc/>
    183         public bool InvokeSetResource(
    184             PowerShell pwsh,
    185             ValueSet settings,
    186             string name,
    187             ModuleSpecification? moduleSpecification)
    188         {
    189             // Returned type is InvokeDscResourceSetResult which is a PowerShell classed defined
    190             // in PSDesiredStateConfiguration.psm1.
    191             dynamic? setResult = null;
    192 
    193             try
    194             {
    195                 setResult = pwsh.AddCommand(this.InvokeDscResourceCmd)
    196                                 .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification))
    197                                 .AddParameter(Parameters.Method, DscMethods.Set)
    198                                 .InvokeAndStopOnError()
    199                                 .FirstOrDefault();
    200             }
    201             catch (Exception ex)
    202             {
    203                 throw new InvokeDscResourceException(InvokeDscResourceException.Set, name, moduleSpecification, ex);
    204             }
    205 
    206             string? errorMessage = pwsh.GetErrorMessage();
    207             if (errorMessage is not null)
    208             {
    209                 throw new InvokeDscResourceException(InvokeDscResourceException.Set, name, moduleSpecification, errorMessage, pwsh.ContainsPropertyError());
    210             }
    211 
    212             if (setResult is null ||
    213                 !TypeHelpers.PropertyWithTypeExists<bool>(setResult, RebootRequired))
    214             {
    215                 throw new InvokeDscResourceException(InvokeDscResourceException.Set, name, moduleSpecification);
    216             }
    217 
    218             return setResult?.RebootRequired;
    219         }
    220 
    221         private static Dictionary<string, object> PrepareInvokeParameters(
    222             string name,
    223             ValueSet settings,
    224             ModuleSpecification? moduleSpecification)
    225         {
    226             Hashtable properties = settings.ToHashtable();
    227 
    228             var parameters = new Dictionary<string, object>()
    229             {
    230                 { Parameters.Name, name },
    231                 { Parameters.Property, properties },
    232             };
    233 
    234             if (moduleSpecification is not null)
    235             {
    236                 parameters.Add(Parameters.ModuleName, moduleSpecification);
    237             }
    238 
    239             return parameters;
    240         }
    241 
    242         private IReadOnlyList<DscResourceInfoInternal> GetDscResources(
    243             PowerShell pwsh,
    244             string? name,
    245             ModuleSpecification? moduleSpecification)
    246         {
    247             pwsh.AddCommand(this.GetDscResourceCmd);
    248 
    249             if (name is not null)
    250             {
    251                 pwsh.AddParameter(Parameters.Name, name);
    252             }
    253 
    254             if (moduleSpecification is not null)
    255             {
    256                 pwsh.AddParameter(Parameters.Module, moduleSpecification);
    257             }
    258 
    259             try
    260             {
    261                 var resources = pwsh.Invoke();
    262                 return this.ConvertToDscResourceInfoInternal(resources);
    263             }
    264             catch (RuntimeException e)
    265             {
    266                 // Detect easily this.
    267                 if (e.ErrorRecord.FullyQualifiedErrorId == "ExceptionWhenSetting,GetResourceFromKeyword")
    268                 {
    269                     throw new GetDscResourceModuleConflict(name, moduleSpecification, e);
    270                 }
    271 
    272                 throw;
    273             }
    274         }
    275 
    276         private List<DscResourceInfoInternal> ConvertToDscResourceInfoInternal(Collection<PSObject> psObjects)
    277         {
    278             var result = new List<DscResourceInfoInternal>();
    279             foreach (dynamic psObject in psObjects)
    280             {
    281                 var dscResourceInfo = new DscResourceInfoInternal(psObject);
    282 
    283                 // Explicitly don't support old DSC resources from v1 PSDesiredStateConfiguration.
    284                 // Even if the Windows System32 Windows PowerShell module path is removed they
    285                 // will show up.
    286                 if (ExclusionResourcesParentPath.Any(e => dscResourceInfo.ParentPath!.StartsWith(e)))
    287                 {
    288                     continue;
    289                 }
    290 
    291                 result.Add(dscResourceInfo);
    292             }
    293 
    294             return result;
    295         }
    296     }
    297 }