commit 0d542092b79f3940a5cd4abf682dfeb48bea8b17 parent cda7a08e1480417cdb83f724829c1f5e4d663192 Author: Ruben Guerrero <rubengu@microsoft.com> Date: Thu, 23 Mar 2023 15:59:50 -0700 Processor improvements (#3101) Relax restrictions for Write-Error. Log PowerShell's error stream with verbose level. Create new exception for the case when the same module version exists in different module paths. Diffstat:
12 files changed, 364 insertions(+), 179 deletions(-)
diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -172,7 +172,7 @@ #define WINGET_CONFIG_ERROR_UNIT_INVOKE_GET ((HRESULT)0x8A15C104) #define WINGET_CONFIG_ERROR_UNIT_INVOKE_TEST ((HRESULT)0x8A15C105) #define WINGET_CONFIG_ERROR_UNIT_INVOKE_SET ((HRESULT)0x8A15C106) - +#define WINGET_CONFIG_ERROR_UNIT_MODULE_CONFLICT ((HRESULT)0x8A15C107) namespace AppInstaller { diff --git a/src/Microsoft.Management.Configuration.Processor/DscModules/DscModuleV2.cs b/src/Microsoft.Management.Configuration.Processor/DscModules/DscModuleV2.cs @@ -11,10 +11,8 @@ namespace Microsoft.Management.Configuration.Processor.DscModule using System.Collections.ObjectModel; using System.Linq; using System.Management.Automation; - using System.Management.Automation.Runspaces; using Microsoft.Management.Configuration.Processor.DscResourcesInfo; using Microsoft.Management.Configuration.Processor.Exceptions; - using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.PowerShell.Commands; using Windows.Foundation.Collections; @@ -54,51 +52,26 @@ namespace Microsoft.Management.Configuration.Processor.DscModule public string InvokeDscResourceCmd { get; } = Commands.InvokeDscResource; /// <inheritdoc/> - public IReadOnlyList<DscResourceInfoInternal> GetAllDscResources(Runspace runspace) + public IReadOnlyList<DscResourceInfoInternal> GetAllDscResources(PowerShell pwsh) { - var result = new List<DscResourceInfoInternal>(); - - using PowerShell pwsh = PowerShell.Create(runspace); - var resources = pwsh.AddCommand(this.GetDscResourceCmd) - .InvokeAndStopOnError(); - - return this.ConvertToDscResourceInfoInternal(resources); + return this.GetDscResources(pwsh, null, null); } /// <inheritdoc/> public IReadOnlyList<DscResourceInfoInternal> GetDscResourcesInModule( - Runspace runspace, + PowerShell pwsh, ModuleSpecification moduleSpecification) { - var result = new List<DscResourceInfoInternal>(); - - using PowerShell pwsh = PowerShell.Create(runspace); - - var resources = pwsh.AddCommand(this.GetDscResourceCmd) - .AddParameter(Parameters.Module, moduleSpecification) - .InvokeAndStopOnError(); - - return this.ConvertToDscResourceInfoInternal(resources); + return this.GetDscResources(pwsh, null, moduleSpecification); } /// <inheritdoc/> public DscResourceInfoInternal? GetDscResource( - Runspace runspace, + PowerShell pwsh, string name, ModuleSpecification? moduleSpecification) { - using PowerShell pwsh = PowerShell.Create(runspace); - pwsh.AddCommand(this.GetDscResourceCmd) - .AddParameter(Parameters.Name, name); - - if (moduleSpecification is not null) - { - pwsh.AddParameter(Parameters.Module, moduleSpecification); - } - - var resources = pwsh.InvokeAndStopOnError(); - - var dscResourceInfos = this.ConvertToDscResourceInfoInternal(resources); + var dscResourceInfos = this.GetDscResources(pwsh, name, moduleSpecification); if (dscResourceInfos.Count == 0) { @@ -114,17 +87,15 @@ namespace Microsoft.Management.Configuration.Processor.DscModule /// <inheritdoc/> public ValueSet InvokeGetResource( - Runspace runspace, + PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification) { - using PowerShell pwsh = PowerShell.Create(runspace); - var getResult = pwsh.AddCommand(this.InvokeDscResourceCmd) .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification)) .AddParameter(Parameters.Method, DscMethods.Get) - .InvokeAndStopOnError() + .Invoke() .FirstOrDefault(); if (getResult is null) @@ -151,19 +122,17 @@ namespace Microsoft.Management.Configuration.Processor.DscModule /// <inheritdoc/> public bool InvokeTestResource( - Runspace runspace, + PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification) { - using PowerShell pwsh = PowerShell.Create(runspace); - // Returned type is InvokeDscResourceTestResult which is a PowerShell classed defined // in PSDesiredStateConfiguration.psm1. dynamic? testResult = pwsh.AddCommand(this.InvokeDscResourceCmd) .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification)) .AddParameter(Parameters.Method, DscMethods.Test) - .InvokeAndStopOnError() + .Invoke() .FirstOrDefault(); if (testResult is null || @@ -177,19 +146,17 @@ namespace Microsoft.Management.Configuration.Processor.DscModule /// <inheritdoc/> public bool InvokeSetResource( - Runspace runspace, + PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification) { - using PowerShell pwsh = PowerShell.Create(runspace); - // Returned type is InvokeDscResourceSetResult which is a PowerShell classed defined // in PSDesiredStateConfiguration.psm1. dynamic? setResult = pwsh.AddCommand(this.InvokeDscResourceCmd) .AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification)) .AddParameter(Parameters.Method, DscMethods.Set) - .InvokeAndStopOnError() + .Invoke() .FirstOrDefault(); if (setResult is null || @@ -226,6 +193,40 @@ namespace Microsoft.Management.Configuration.Processor.DscModule return parameters; } + private IReadOnlyList<DscResourceInfoInternal> GetDscResources( + PowerShell pwsh, + string? name, + ModuleSpecification? moduleSpecification) + { + pwsh.AddCommand(this.GetDscResourceCmd); + + if (name is not null) + { + pwsh.AddParameter(Parameters.Name, name); + } + + if (moduleSpecification is not null) + { + pwsh.AddParameter(Parameters.Module, moduleSpecification); + } + + try + { + var resources = pwsh.Invoke(); + return this.ConvertToDscResourceInfoInternal(resources); + } + catch (RuntimeException e) + { + // Detect easily this. + if (e.ErrorRecord.FullyQualifiedErrorId == "ExceptionWhenSetting,GetResourceFromKeyword") + { + throw new GetDscResourceModuleConflict(name, moduleSpecification, e); + } + + throw; + } + } + private List<DscResourceInfoInternal> ConvertToDscResourceInfoInternal(Collection<PSObject> psObjects) { var result = new List<DscResourceInfoInternal>(); diff --git a/src/Microsoft.Management.Configuration.Processor/DscModules/IDscModule.cs b/src/Microsoft.Management.Configuration.Processor/DscModules/IDscModule.cs @@ -7,7 +7,7 @@ namespace Microsoft.Management.Configuration.Processor.DscModule { using System.Collections.Generic; - using System.Management.Automation.Runspaces; + using System.Management.Automation; using Microsoft.Management.Configuration.Processor.DscResourcesInfo; using Microsoft.PowerShell.Commands; using Windows.Foundation.Collections; @@ -35,55 +35,55 @@ namespace Microsoft.Management.Configuration.Processor.DscModule /// <summary> /// Gets all DSC resource. /// </summary> - /// <param name="runspace">PowerShell Runspace.</param> + /// <param name="pwsh">PowerShell.</param> /// <returns>A list with the DSC resource.</returns> - IReadOnlyList<DscResourceInfoInternal> GetAllDscResources(Runspace runspace); + IReadOnlyList<DscResourceInfoInternal> GetAllDscResources(PowerShell pwsh); /// <summary> /// Gets all resources in a module. /// </summary> - /// <param name="runspace">PowerShell Runspace.</param> + /// <param name="pwsh">PowerShell.</param> /// <param name="moduleSpecification">Module specification.</param> /// <returns>List of resources of that module and version.</returns> - IReadOnlyList<DscResourceInfoInternal> GetDscResourcesInModule(Runspace runspace, ModuleSpecification moduleSpecification); + IReadOnlyList<DscResourceInfoInternal> GetDscResourcesInModule(PowerShell pwsh, ModuleSpecification moduleSpecification); /// <summary> /// Gets a DSC Resource. /// </summary> - /// <param name="runspace">PowerShell Runspace.</param> + /// <param name="pwsh">PowerShell.</param> /// <param name="name">Name.</param> /// <param name="moduleSpecification">Module specification.</param> /// <returns>DSC Resource from that module and version.</returns> - DscResourceInfoInternal? GetDscResource(Runspace runspace, string name, ModuleSpecification? moduleSpecification); + DscResourceInfoInternal? GetDscResource(PowerShell pwsh, string name, ModuleSpecification? moduleSpecification); /// <summary> /// Calls Invoke-DscResource -Method Get from this module. /// </summary> - /// <param name="runspace">PowerShell Runspace.</param> + /// <param name="pwsh">PowerShell.</param> /// <param name="settings">Settings.</param> /// <param name="name">Name.</param> /// <param name="moduleSpecification">Module specification.</param> /// <returns>Properties of resource.</returns> - ValueSet InvokeGetResource(Runspace runspace, ValueSet settings, string name, ModuleSpecification? moduleSpecification); + ValueSet InvokeGetResource(PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification); /// <summary> /// Calls Invoke-DscResource -Method Test from this module. /// </summary> - /// <param name="runspace">PowerShell Runspace.</param> + /// <param name="pwsh">PowerShell.</param> /// <param name="settings">Settings.</param> /// <param name="name">Name.</param> /// <param name="moduleSpecification">Module specification.</param> /// <returns>Is in desired state.</returns> - bool InvokeTestResource(Runspace runspace, ValueSet settings, string name, ModuleSpecification? moduleSpecification); + bool InvokeTestResource(PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification); /// <summary> /// Calls Invoke-DscResource -Method Set from this module. /// </summary> - /// <param name="runspace">PowerShell Runspace.</param> + /// <param name="pwsh">PowerShell.</param> /// <param name="settings">Settings.</param> /// <param name="name">Name.</param> /// <param name="moduleSpecification">Module specification.</param> /// <returns>If a reboot is required.</returns> - bool InvokeSetResource(Runspace runspace, ValueSet settings, string name, ModuleSpecification? moduleSpecification); + bool InvokeSetResource(PowerShell pwsh, ValueSet settings, string name, ModuleSpecification? moduleSpecification); } } diff --git a/src/Microsoft.Management.Configuration.Processor/Exceptions/ErrorCodes.cs b/src/Microsoft.Management.Configuration.Processor/Exceptions/ErrorCodes.cs @@ -40,5 +40,10 @@ namespace Microsoft.Management.Configuration.Processor.Exceptions /// Internal error calling Invoke-DscResource Set. /// </summary> internal const int WinGetConfigUnitInvokeSet = unchecked((int)0x8A15C106); + + /// <summary> + /// Internal error calling Get-DscResource. More than one module found with the same version. + /// </summary> + internal const int WinGetConfigUnitModuleConflict = unchecked((int)0x8A15C107); } } diff --git a/src/Microsoft.Management.Configuration.Processor/Exceptions/GetDscResourceModuleConflict.cs b/src/Microsoft.Management.Configuration.Processor/Exceptions/GetDscResourceModuleConflict.cs @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------------- +// <copyright file="GetDscResourceModuleConflict.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.Processor.Exceptions +{ + using System; + using System.Management.Automation; + using Microsoft.PowerShell.Commands; + + /// <summary> + /// A call to Get-DscResource failed because at least two modules with the same version where found in the module path. + /// If you are getting this verify the module path. + /// </summary> + internal class GetDscResourceModuleConflict : Exception + { + /// <summary> + /// Initializes a new instance of the <see cref="GetDscResourceModuleConflict"/> class. + /// </summary> + /// <param name="resourceName">Resource name.</param> + /// <param name="module">Optional module.</param> + /// <param name="inner">The original runtime exception thrown.</param> + public GetDscResourceModuleConflict(string? resourceName, ModuleSpecification? module, RuntimeException inner) + : base($"Multiple modules with same version in module path: {resourceName?.ToString() ?? "<no resource>"} [{module?.ToString() ?? "<no module>"}]", inner) + { + this.HResult = ErrorCodes.WinGetConfigUnitModuleConflict; + this.ResourceName = resourceName; + this.Module = module; + } + + /// <summary> + /// Gets the resource name. + /// </summary> + public string? ResourceName { get; } + + /// <summary> + /// Gets the module, if any. + /// </summary> + public ModuleSpecification? Module { get; } + } +} diff --git a/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/HostedEnvironment.cs b/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/HostedEnvironment.cs @@ -15,8 +15,6 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces using Microsoft.Management.Configuration.Processor.Constants; using Microsoft.Management.Configuration.Processor.DscModule; using Microsoft.Management.Configuration.Processor.DscResourcesInfo; - using Microsoft.Management.Configuration.Processor.Exceptions; - using Microsoft.Management.Configuration.Processor.Extensions; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.Processor.ProcessorEnvironments; using Microsoft.PowerShell.Commands; @@ -51,7 +49,12 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces /// <summary> /// Gets the DscModule. /// </summary> - protected IDscModule DscModule { get; } + internal IDscModule DscModule { get; } + + /// <summary> + /// Gets or initializes the set processor factory. + /// </summary> + internal ConfigurationSetProcessorFactory? SetProcessorFactory { get; init; } /// <inheritdoc/> public void ValidateRunspace() @@ -84,28 +87,58 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces } /// <inheritdoc/> - public IReadOnlyList<DscResourceInfoInternal> GetAllDscResources() => - this.DscModule.GetAllDscResources(this.Runspace); + public IReadOnlyList<DscResourceInfoInternal> GetAllDscResources() + { + using PowerShell pwsh = PowerShell.Create(this.Runspace); + var results = this.DscModule.GetAllDscResources(pwsh); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); + return results; + } /// <inheritdoc/> - public IReadOnlyList<DscResourceInfoInternal> GetDscResourcesInModule(ModuleSpecification moduleSpecification) => - this.DscModule.GetDscResourcesInModule(this.Runspace, moduleSpecification); + public IReadOnlyList<DscResourceInfoInternal> GetDscResourcesInModule(ModuleSpecification moduleSpecification) + { + using PowerShell pwsh = PowerShell.Create(this.Runspace); + var results = this.DscModule.GetDscResourcesInModule(pwsh, moduleSpecification); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); + return results; + } /// <inheritdoc/> - public DscResourceInfoInternal? GetDscResource(ConfigurationUnitInternal unitInternal) => - this.DscModule.GetDscResource(this.Runspace, unitInternal.Unit.UnitName, unitInternal.Module); + public DscResourceInfoInternal? GetDscResource(ConfigurationUnitInternal unitInternal) + { + using PowerShell pwsh = PowerShell.Create(this.Runspace); + var result = this.DscModule.GetDscResource(pwsh, unitInternal.Unit.UnitName, unitInternal.Module); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); + return result; + } /// <inheritdoc/> - public ValueSet InvokeGetResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) => - this.DscModule.InvokeGetResource(this.Runspace, settings, name, moduleSpecification); + public ValueSet InvokeGetResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) + { + using PowerShell pwsh = PowerShell.Create(this.Runspace); + var result = this.DscModule.InvokeGetResource(pwsh, settings, name, moduleSpecification); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); + return result; + } /// <inheritdoc/> - public bool InvokeTestResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) => - this.DscModule.InvokeTestResource(this.Runspace, settings, name, moduleSpecification); + public bool InvokeTestResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) + { + using PowerShell pwsh = PowerShell.Create(this.Runspace); + var result = this.DscModule.InvokeTestResource(pwsh, settings, name, moduleSpecification); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); + return result; + } /// <inheritdoc/> - public bool InvokeSetResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) => - this.DscModule.InvokeSetResource(this.Runspace, settings, name, moduleSpecification); + public bool InvokeSetResource(ValueSet settings, string name, ModuleSpecification? moduleSpecification) + { + using PowerShell pwsh = PowerShell.Create(this.Runspace); + var result = this.DscModule.InvokeSetResource(pwsh, settings, name, moduleSpecification); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); + return result; + } /// <inheritdoc/> public PSModuleInfo? GetImportedModule(ModuleSpecification moduleSpecification) @@ -117,6 +150,7 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces .Invoke<PSModuleInfo>() .FirstOrDefault(); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return moduleInfo; } @@ -131,6 +165,7 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces .Invoke<PSModuleInfo>() .FirstOrDefault(); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return moduleInfo; } @@ -145,6 +180,7 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces .Invoke<PSModuleInfo>() .FirstOrDefault(); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return moduleInfo; } @@ -156,6 +192,8 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces _ = pwsh.AddCommand(Commands.ImportModule) .AddParameter(Parameters.FullyQualifiedName, moduleSpecification) .Invoke(); + + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } /// <inheritdoc/> @@ -188,6 +226,7 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces .Invoke() .FirstOrDefault(); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return result; } @@ -236,6 +275,7 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces .Invoke() .FirstOrDefault(); + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return result; } @@ -247,7 +287,9 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces _ = pwsh.AddCommand(Commands.SaveModule) .AddParameter(Parameters.Path, location) .AddParameter(Parameters.InputObject, inputObject) - .InvokeAndStopOnError(); + .Invoke(); + + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } /// <inheritdoc/> @@ -265,7 +307,9 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces _ = pwsh.AddCommand(Commands.InstallModule) .AddParameter(Parameters.InputObject, inputObject) .AddParameter(Parameters.Force) - .InvokeAndStopOnError(); + .Invoke(); + + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } /// <inheritdoc/> @@ -298,7 +342,9 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces _ = pwsh.AddCommand(Commands.InstallModule) .AddParameters(parameters) .AddParameter(Parameters.Force) - .InvokeAndStopOnError(); + .Invoke(); + + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } } @@ -310,7 +356,7 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces var signatures = pwsh.AddCommand(Commands.GetChildItem) .AddParameter(Parameters.Path, paths) .AddCommand(Commands.GetAuthenticodeSignature) - .InvokeAndStopOnError<Signature>(); + .Invoke<Signature>(); var thumbprint = new HashSet<string>(); var certificates = new List<Certificate>(); @@ -326,6 +372,7 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces } } + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); return certificates; } @@ -408,5 +455,10 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces return false; } + + private void OnDiagnostics(DiagnosticLevel level, PowerShell pwsh) + { + this.SetProcessorFactory?.OnDiagnostics(level, pwsh); + } } } diff --git a/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/ProcessorEnvironmentFactory.cs b/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/ProcessorEnvironmentFactory.cs @@ -33,13 +33,9 @@ namespace Microsoft.Management.Configuration.Processor.ProcessorEnvironments /// <summary> /// Create process environment. /// </summary> + /// <param name="setProcessorFactory">Optional processor factory.</param> /// <returns>IProcessorEnvironment.</returns> - public IProcessorEnvironment CreateEnvironment() - { - return this.CreateProcessorEnvironment(); - } - - private IProcessorEnvironment CreateProcessorEnvironment() + public IProcessorEnvironment CreateEnvironment(ConfigurationSetProcessorFactory? setProcessorFactory) { IDscModule dscModule = new DscModuleV2(); @@ -67,7 +63,10 @@ namespace Microsoft.Management.Configuration.Processor.ProcessorEnvironments var runspace = RunspaceFactory.CreateRunspace(initialSessionState); runspace.Open(); - return new HostedEnvironment(runspace, this.type, dscModule); + return new HostedEnvironment(runspace, this.type, dscModule) + { + SetProcessorFactory = setProcessorFactory, + }; } throw new ArgumentException(this.type.ToString()); diff --git a/src/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs b/src/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs @@ -7,6 +7,8 @@ namespace Microsoft.Management.Configuration.Processor { using System; + using System.Management.Automation; + using System.Text; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor.ProcessorEnvironments; using Microsoft.Management.Configuration.Processor.Set; @@ -52,7 +54,7 @@ namespace Microsoft.Management.Configuration.Processor this.OnDiagnostics(DiagnosticLevel.Verbose, $"Creating set processor for `{set.Name}`..."); var envFactory = new ProcessorEnvironmentFactory(this.type); - var processorEnvironment = envFactory.CreateEnvironment(); + var processorEnvironment = envFactory.CreateEnvironment(this); processorEnvironment.ValidateRunspace(); if (this.properties is not null) @@ -85,11 +87,57 @@ namespace Microsoft.Management.Configuration.Processor EventHandler<DiagnosticInformation>? diagnostics = this.Diagnostics; if (diagnostics != null && level >= this.MinimumLevel) { - DiagnosticInformation information = new DiagnosticInformation(); - information.Level = level; - information.Message = message; - diagnostics.Invoke(this, information); + this.InvokeDiagnostics(diagnostics, level, message); } } + + /// <summary> + /// Sends diagnostic if appropriate for PowerShell streams. + /// </summary> + /// <param name="level">The level of this diagnostic message.</param> + /// <param name="pwsh">The PowerShell object.</param> + internal void OnDiagnostics(DiagnosticLevel level, PowerShell pwsh) + { + EventHandler<DiagnosticInformation>? diagnostics = this.Diagnostics; + if (diagnostics != null && level >= this.MinimumLevel && pwsh.HadErrors) + { + var builder = new StringBuilder(); + + // There are the last commands ran by that PowerShell obj, not all in our session. + builder.Append("PowerShellCommands: "); + foreach (var c in pwsh.Commands.Commands) + { + builder.Append($"['{c.CommandText}'"); + if (c.Parameters.Count > 0) + { + builder.Append(" Parameters: "); + foreach (var p in c.Parameters) + { + builder.Append($"{p.Name} = '{p.Value}' "); + } + builder.Append("]"); + } + + builder.AppendLine(); + } + + foreach (var error in pwsh.Streams.Error) + { + builder.AppendLine($"[WriteError] {error}"); + } + + this.InvokeDiagnostics(diagnostics, level, builder.ToString()); + } + } + + private void InvokeDiagnostics(EventHandler<DiagnosticInformation> diagnostics, DiagnosticLevel level, string message) + { + DiagnosticInformation information = new () + { + Level = level, + Message = message, + }; + diagnostics.Invoke(this, information); + } } } \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestFixture.cs b/src/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestFixture.cs @@ -57,7 +57,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Fixtures /// <returns>PowerShellRunspace.</returns> internal IProcessorEnvironment PrepareTestProcessorEnvironment(bool validate = false) { - var processorEnv = new ProcessorEnvironmentFactory(ConfigurationProcessorType.Hosted).CreateEnvironment(); + var processorEnv = new ProcessorEnvironmentFactory(ConfigurationProcessorType.Hosted).CreateEnvironment(null); processorEnv.PrependPSModulePath(this.TestModulesPath); if (validate) diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs @@ -25,7 +25,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// <summary> /// Diagnostics event; useful for logging and/or verbose output. /// </summary> +#pragma warning disable CS0067 // The event is never used public event EventHandler<DiagnosticInformation>? Diagnostics; +#pragma warning restore CS0067 // The event is never used /// <summary> /// Gets or sets the minimum diagnostic level to send. diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/DscModuleV2SimpleFileResourceTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/DscModuleV2SimpleFileResourceTests.cs @@ -7,6 +7,7 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests { using System.IO; + using System.Management.Automation; using Microsoft.Management.Configuration.Processor.DscModule; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; @@ -58,10 +59,12 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests }; var dscModule = new DscModuleV2(); + + using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); Assert.Equal( expectedResult, dscModule.InvokeTestResource( - processorEnv.Runspace, + pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -86,8 +89,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests }; var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); Assert.True(dscModule.InvokeTestResource( - processorEnv.Runspace, + pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -112,8 +116,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests }; var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); Assert.False(dscModule.InvokeTestResource( - processorEnv.Runspace, + pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -139,8 +144,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests }; var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); Assert.True(dscModule.InvokeTestResource( - processorEnv.Runspace, + pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -181,8 +187,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests File.Exists(tmpFile.FullFileName)); var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); dscModule.InvokeSetResource( - processorEnv.Runspace, + pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -221,8 +228,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests }; var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); dscModule.InvokeSetResource( - processorEnv.Runspace, + pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -250,8 +258,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests }; var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(processorEnv.Runspace); var properties = dscModule.InvokeGetResource( - processorEnv.Runspace, + pwsh, settings, TestModule.SimpleFileResourceName, PowerShellHelpers.CreateModuleSpecification( diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/DscModuleV2Tests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/DscModuleV2Tests.cs @@ -6,10 +6,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests { - using System; using System.IO; using System.Management.Automation; using Microsoft.Management.Configuration.Processor.DscModule; + using Microsoft.Management.Configuration.Processor.Exceptions; using Microsoft.Management.Configuration.Processor.Helpers; using Microsoft.Management.Configuration.UnitTests.Fixtures; using Microsoft.Management.Configuration.UnitTests.Helpers; @@ -48,7 +48,8 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - var resources = dscModule.GetAllDscResources(testEnvironment.Runspace); + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + var resources = dscModule.GetAllDscResources(pwsh); Assert.True(resources.Count > 0); Assert.Contains(resources, r => r.Name == TestModule.SimpleFileResourceName); @@ -67,8 +68,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var resources = dscModule.GetDscResourcesInModule( - testEnvironment.Runspace, + pwsh, PowerShellHelpers.CreateModuleSpecification(module)); Assert.Equal(expectedResources, resources.Count); } @@ -102,27 +104,35 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests //// testEnvironment.Runspace, //// PowerShellHelpers.CreateModuleSpecification(TestModule.SimpleTestResourceModuleName)); ////Assert.Equal(8, allResources.Count); - - var ogResources = dscModule.GetDscResourcesInModule( - testEnvironment.Runspace, + { + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + var ogResources = dscModule.GetDscResourcesInModule( + pwsh, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName, version: TestModule.SimpleTestResourceVersion)); - Assert.Equal(4, ogResources.Count); + Assert.Equal(4, ogResources.Count); + } - var newVersionResources = dscModule.GetDscResourcesInModule( - testEnvironment.Runspace, - PowerShellHelpers.CreateModuleSpecification( - TestModule.SimpleTestResourceModuleName, - version: newVersion)); - Assert.Equal(4, ogResources.Count); + { + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + var newVersionResources = dscModule.GetDscResourcesInModule( + pwsh, + PowerShellHelpers.CreateModuleSpecification( + TestModule.SimpleTestResourceModuleName, + version: newVersion)); + Assert.Equal(4, newVersionResources.Count); + } - var badVersionResources = dscModule.GetDscResourcesInModule( - testEnvironment.Runspace, - PowerShellHelpers.CreateModuleSpecification( - TestModule.SimpleTestResourceModuleName, - version: "1.2.3.4")); - Assert.Equal(0, badVersionResources.Count); + { + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + var badVersionResources = dscModule.GetDscResourcesInModule( + pwsh, + PowerShellHelpers.CreateModuleSpecification( + TestModule.SimpleTestResourceModuleName, + version: "1.2.3.4")); + Assert.Equal(0, badVersionResources.Count); + } } /// <summary> @@ -134,8 +144,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var resource = dscModule.GetDscResource( - testEnvironment.Runspace, + pwsh, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification(TestModule.SimpleTestResourceModuleName)); @@ -151,12 +162,13 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - Assert.Throws<WriteErrorException>( - () => dscModule.GetDscResource( - testEnvironment.Runspace, - "FakeResourceName", - PowerShellHelpers.CreateModuleSpecification( - TestModule.SimpleTestResourceModuleName))); + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + var resource = dscModule.GetDscResource( + pwsh, + "FakeResourceName", + PowerShellHelpers.CreateModuleSpecification( + TestModule.SimpleTestResourceModuleName)); + Assert.Null(resource); } /// <summary> @@ -174,10 +186,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests testEnvironment.AppendPSModulePath(tmpDir.FullDirectoryPath); var dscModule = new DscModuleV2(); - - Assert.Throws<RuntimeException>( + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + Assert.Throws<GetDscResourceModuleConflict>( () => dscModule.GetDscResource( - testEnvironment.Runspace, + pwsh, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( TestModule.SimpleTestResourceModuleName))); @@ -208,24 +220,30 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var dscModule = new DscModuleV2(); // specific version. - var resource = dscModule.GetDscResource( - testEnvironment.Runspace, - TestModule.SimpleTestResourceName, - PowerShellHelpers.CreateModuleSpecification( - TestModule.SimpleTestResourceModuleName, - TestModule.SimpleTestResourceVersion)); - Assert.NotNull(resource); + { + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + var resource = dscModule.GetDscResource( + pwsh, + TestModule.SimpleTestResourceName, + PowerShellHelpers.CreateModuleSpecification( + TestModule.SimpleTestResourceModuleName, + TestModule.SimpleTestResourceVersion)); + Assert.NotNull(resource); + } - var dsc = dscModule.GetDscResource( - testEnvironment.Runspace, - TestModule.SimpleTestResourceName, - PowerShellHelpers.CreateModuleSpecification( - TestModule.SimpleTestResourceModuleName, - version: newVersion)); + { + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + var dsc = dscModule.GetDscResource( + pwsh, + TestModule.SimpleTestResourceName, + PowerShellHelpers.CreateModuleSpecification( + TestModule.SimpleTestResourceModuleName, + version: newVersion)); - Assert.NotNull(dsc); - Assert.NotNull(dsc.Version); - Assert.Equal(newVersion, dsc.Version.ToString()); + Assert.NotNull(dsc); + Assert.NotNull(dsc.Version); + Assert.Equal(newVersion, dsc.Version.ToString()); + } } /// <summary> @@ -237,9 +255,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var getResult = dscModule.InvokeGetResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -260,9 +278,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var dscModule = new DscModuleV2(); + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws<RuntimeException>(() => dscModule.InvokeGetResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), TestModule.SimpleTestResourceThrowsName, PowerShellHelpers.CreateModuleSpecification( @@ -280,10 +299,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); Assert.Throws<WriteErrorException>(() => dscModule.InvokeGetResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), TestModule.SimpleTestResourceErrorName, PowerShellHelpers.CreateModuleSpecification( @@ -299,10 +318,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws<RuntimeException>( () => dscModule.InvokeGetResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), "FakeResourceName", PowerShellHelpers.CreateModuleSpecification( @@ -330,8 +349,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests { "secretCode", value }, }; + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var testResult = dscModule.InvokeTestResource( - testEnvironment.Runspace, + pwsh, settings, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -349,10 +369,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws<RuntimeException>(() => dscModule.InvokeTestResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), TestModule.SimpleTestResourceThrowsName, PowerShellHelpers.CreateModuleSpecification( @@ -370,10 +390,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); Assert.Throws<WriteErrorException>(() => dscModule.InvokeTestResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), TestModule.SimpleTestResourceErrorName, PowerShellHelpers.CreateModuleSpecification( @@ -389,10 +409,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws<RuntimeException>(() => _ = dscModule.InvokeTestResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), "FakeResourceName", PowerShellHelpers.CreateModuleSpecification( @@ -418,8 +438,9 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests { "secretCode", "4815162342" }, }; + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var testResult = dscModule.InvokeSetResource( - testEnvironment.Runspace, + pwsh, settings, TestModule.SimpleTestResourceName, PowerShellHelpers.CreateModuleSpecification( @@ -438,10 +459,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws<RuntimeException>(() => dscModule.InvokeSetResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), TestModule.SimpleTestResourceThrowsName, PowerShellHelpers.CreateModuleSpecification( @@ -459,10 +480,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); Assert.Throws<WriteErrorException>(() => dscModule.InvokeSetResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), TestModule.SimpleTestResourceErrorName, PowerShellHelpers.CreateModuleSpecification( @@ -478,10 +499,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests var testEnvironment = this.fixture.PrepareTestProcessorEnvironment(); var dscModule = new DscModuleV2(); - + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); var exception = Assert.Throws<RuntimeException>(() => dscModule.InvokeSetResource( - testEnvironment.Runspace, + pwsh, new ValueSet(), "FakeResourceName", PowerShellHelpers.CreateModuleSpecification( @@ -513,22 +534,27 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests testEnvironment.AppendPSModulePath(tmpDir.FullDirectoryPath); var dscModule = new DscModuleV2(); + { + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + dscModule.InvokeSetResource( + pwsh, + new ValueSet(), + TestModule.SimpleTestResourceName, + PowerShellHelpers.CreateModuleSpecification( + TestModule.SimpleTestResourceModuleName, + TestModule.SimpleTestResourceVersion)); + } - dscModule.InvokeSetResource( - testEnvironment.Runspace, - new ValueSet(), - TestModule.SimpleTestResourceName, - PowerShellHelpers.CreateModuleSpecification( - TestModule.SimpleTestResourceModuleName, - TestModule.SimpleTestResourceVersion)); - - dscModule.InvokeSetResource( - testEnvironment.Runspace, - new ValueSet(), - TestModule.SimpleTestResourceName, - PowerShellHelpers.CreateModuleSpecification( - TestModule.SimpleTestResourceModuleName, - version: newVersion)); + { + using PowerShell pwsh = PowerShell.Create(testEnvironment.Runspace); + dscModule.InvokeSetResource( + pwsh, + new ValueSet(), + TestModule.SimpleTestResourceName, + PowerShellHelpers.CreateModuleSpecification( + TestModule.SimpleTestResourceModuleName, + version: newVersion)); + } } } }