Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fixes #3127

Merged
merged 3 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ namespace Microsoft.Management.Configuration.Processor.DscModule
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
using System.Text;
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;
Expand All @@ -26,6 +28,9 @@ internal class DscModuleV2 : IDscModule
private const string InDesiredState = "InDesiredState";
private const string RebootRequired = "RebootRequired";

// Fake max version to avoid using v3 and allowing new v2 versions to be used.
private const string MaxVersion = "2.*";
msftrubengu marked this conversation as resolved.
Show resolved Hide resolved

private static readonly IEnumerable<string> ExclusionResourcesParentPath = new string[]
{
@"C:\WINDOWS\system32\WindowsPowershell\v1.0\Modules\PsDesiredStateConfiguration\DscResources",
Expand All @@ -39,7 +44,8 @@ public DscModuleV2()
{
this.ModuleSpecification = PowerShellHelpers.CreateModuleSpecification(
Modules.PSDesiredStateConfiguration,
minVersion: Modules.PSDesiredStateConfigurationMinVersion);
minVersion: Modules.PSDesiredStateConfigurationMinVersion,
maxVersion: MaxVersion);
}

/// <inheritdoc/>
Expand Down Expand Up @@ -95,9 +101,20 @@ public ValueSet InvokeGetResource(
var getResult = pwsh.AddCommand(this.InvokeDscResourceCmd)
.AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification))
.AddParameter(Parameters.Method, DscMethods.Get)
.Invoke()
.InvokeAndStopOnError()
.FirstOrDefault();

if (pwsh.HadErrors)
msftrubengu marked this conversation as resolved.
Show resolved Hide resolved
{
var psStreamBuilder = new StringBuilder();
foreach (var line in pwsh.Streams.Error)
{
psStreamBuilder.AppendLine(line.ToString());
}

throw new InvokeDscResourceGetException(name, moduleSpecification, psStreamBuilder.ToString());
}

if (getResult is null)
{
throw new InvokeDscResourceGetException(name, moduleSpecification);
Expand Down Expand Up @@ -132,9 +149,20 @@ public bool InvokeTestResource(
dynamic? testResult = pwsh.AddCommand(this.InvokeDscResourceCmd)
.AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification))
.AddParameter(Parameters.Method, DscMethods.Test)
.Invoke()
.InvokeAndStopOnError()
.FirstOrDefault();

if (pwsh.HadErrors)
{
var psStreamBuilder = new StringBuilder();
foreach (var line in pwsh.Streams.Error)
{
psStreamBuilder.AppendLine(line.ToString());
}

throw new InvokeDscResourceTestException(name, moduleSpecification, psStreamBuilder.ToString());
}

if (testResult is null ||
!TypeHelpers.PropertyWithTypeExists<bool>(testResult, InDesiredState))
{
Expand All @@ -156,9 +184,20 @@ public bool InvokeSetResource(
dynamic? setResult = pwsh.AddCommand(this.InvokeDscResourceCmd)
.AddParameters(PrepareInvokeParameters(name, settings, moduleSpecification))
.AddParameter(Parameters.Method, DscMethods.Set)
.Invoke()
.InvokeAndStopOnError()
.FirstOrDefault();

if (pwsh.HadErrors)
{
var psStreamBuilder = new StringBuilder();
foreach (var line in pwsh.Streams.Error)
{
psStreamBuilder.AppendLine(line.ToString());
}

throw new InvokeDscResourceSetException(name, moduleSpecification, psStreamBuilder.ToString());
}

if (setResult is null ||
!TypeHelpers.PropertyWithTypeExists<bool>(setResult, RebootRequired))
{
Expand All @@ -173,11 +212,7 @@ private static Dictionary<string, object> PrepareInvokeParameters(
ValueSet settings,
ModuleSpecification? moduleSpecification)
{
var properties = new Hashtable();
foreach (var setting in settings)
{
properties.Add(setting.Key, setting.Value);
}
Hashtable properties = settings.ToHashtable();

var parameters = new Dictionary<string, object>()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// -----------------------------------------------------------------------------
// <copyright file="BlockedFileException.cs" company="Microsoft Corporation">
// <copyright file="ImportModuleException.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
// -----------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,21 @@ internal class InvokeDscResourceGetException : Exception
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
public InvokeDscResourceGetException(string resourceName, ModuleSpecification? module)
: base($"Failed when calling `Get` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]")
: base(CreateMessage(resourceName, module, null))
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeGet;
this.ResourceName = resourceName;
this.Module = module;
}

/// <summary>
/// Initializes a new instance of the <see cref="InvokeDscResourceGetException"/> class.
/// </summary>
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
/// <param name="message">Message.</param>
public InvokeDscResourceGetException(string resourceName, ModuleSpecification? module, string message)
: base(CreateMessage(resourceName, module, message))
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeGet;
this.ResourceName = resourceName;
Expand All @@ -36,5 +50,16 @@ public InvokeDscResourceGetException(string resourceName, ModuleSpecification? m
/// Gets the module, if any.
/// </summary>
public ModuleSpecification? Module { get; }

private static string CreateMessage(string resourceName, ModuleSpecification? module, string? message)
{
string result = $"Failed when calling `Get` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]";
if (message != null)
{
result += $" Message: '{message}'";
}

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,21 @@ internal class InvokeDscResourceSetException : Exception
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
public InvokeDscResourceSetException(string resourceName, ModuleSpecification? module)
: base($"Failed when calling `Set` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]")
: base(CreateMessage(resourceName, module, null))
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeSet;
this.ResourceName = resourceName;
this.Module = module;
}

/// <summary>
/// Initializes a new instance of the <see cref="InvokeDscResourceSetException"/> class.
/// </summary>
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
/// <param name="message">Message.</param>
public InvokeDscResourceSetException(string resourceName, ModuleSpecification? module, string message)
: base(CreateMessage(resourceName, module, message))
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeSet;
this.ResourceName = resourceName;
Expand All @@ -36,5 +50,16 @@ public InvokeDscResourceSetException(string resourceName, ModuleSpecification? m
/// Gets the module, if any.
/// </summary>
public ModuleSpecification? Module { get; }

private static string CreateMessage(string resourceName, ModuleSpecification? module, string? message)
{
string result = $"Failed when calling `Set` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]";
if (message != null)
{
result += $" Message: '{message}'";
}

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,21 @@ internal class InvokeDscResourceTestException : Exception
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
public InvokeDscResourceTestException(string resourceName, ModuleSpecification? module)
: base($"Failed when calling `Test` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]")
: base(CreateMessage(resourceName, module, null))
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeTest;
this.ResourceName = resourceName;
this.Module = module;
}

/// <summary>
/// Initializes a new instance of the <see cref="InvokeDscResourceTestException"/> class.
/// </summary>
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
/// <param name="message">Message.</param>
public InvokeDscResourceTestException(string resourceName, ModuleSpecification? module, string message)
: base(CreateMessage(resourceName, module, message))
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeTest;
this.ResourceName = resourceName;
Expand All @@ -36,5 +50,16 @@ public InvokeDscResourceTestException(string resourceName, ModuleSpecification?
/// Gets the module, if any.
/// </summary>
public ModuleSpecification? Module { get; }

private static string CreateMessage(string resourceName, ModuleSpecification? module, string? message)
{
string result = $"Failed when calling `Test` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]";
if (message != null)
{
result += $" Message: '{message}'";
}

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ public static Collection<PSObject> InvokeAndStopOnError(this PowerShell pwsh)
ErrorActionPreference = ActionPreference.Stop,
};

var result = pwsh.Invoke(null, settings);

pwsh.ValidateErrorStream();

return result;
return pwsh.Invoke(null, settings);
}

/// <summary>
Expand All @@ -48,29 +44,7 @@ public static Collection<T> InvokeAndStopOnError<T>(this PowerShell pwsh)
ErrorActionPreference = ActionPreference.Stop,
};

var result = pwsh.Invoke<T>(null, settings);

pwsh.ValidateErrorStream();

return result;
}

/// <summary>
/// Throws <see cref="WriteErrorException"/> if there are streams in the stream error.
/// </summary>
/// <param name="pwsh">PowerShell.</param>
public static void ValidateErrorStream(this PowerShell pwsh)
{
if (pwsh.HadErrors)
{
var psStreamBuilder = new StringBuilder();
foreach (var line in pwsh.Streams.Error)
{
psStreamBuilder.AppendLine(line.ToString());
}

throw new WriteErrorException(psStreamBuilder.ToString());
}
return pwsh.Invoke<T>(null, settings);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// -----------------------------------------------------------------------------
// <copyright file="ValueSetExtensions.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
// -----------------------------------------------------------------------------

namespace Microsoft.Management.Configuration.Processor.Extensions
{
using System.Collections;
using Windows.Foundation.Collections;

/// <summary>
/// Extensions for ValueSet.
/// </summary>
internal static class ValueSetExtensions
{
/// <summary>
/// Extension method to transform a ValueSet to a Hashtable.
/// </summary>
/// <param name="valueSet">Value set.</param>
/// <returns>A hashtable.</returns>
public static Hashtable ToHashtable(this ValueSet valueSet)
{
var hashtable = new Hashtable();

foreach (var keyValuePair in valueSet)
{
if (keyValuePair.Value is ValueSet)
{
ValueSet innerValueSet = (ValueSet)keyValuePair.Value;
hashtable.Add(keyValuePair.Key, innerValueSet.ToHashtable());
}
else
{
hashtable.Add(keyValuePair.Key, keyValuePair.Value);
}
}

return hashtable;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace Microsoft.Management.Configuration.Processor.Helpers
/// </summary>
internal static class PowerShellHelpers
{
private const string MaxRange = "999999999";

/// <summary>
/// Creates a module specification object.
/// </summary>
Expand Down Expand Up @@ -56,7 +58,14 @@ minVersion is null &&

if (!string.IsNullOrEmpty(maxVersion))
{
moduleInfo.Add(Parameters.MaximumVersion, maxVersion);
// For some reason, the constructor of ModuleSpecification that takes
// a hashtable calls ModuleCmdletBase.GetMaximumVersion. This method will
// validate the max version and replace * for 999999999 only if its the last
// char in the string. But then the returned value is not assigned to the
// ModuleSpecification's MaximumVersion property. If we want to set a
// MaximumVersion with a wildcard and pass this to Install-Module it will
// fail with "Cannot convert value 'x.*' to type 'System.Version'."
moduleInfo.Add(Parameters.MaximumVersion, maxVersion.Replace("*", MaxRange));
}

if (!string.IsNullOrEmpty(guid))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ DscResourcesToExport = @(
'SimpleTestResource'
'SimpleTestResourceThrows'
'SimpleTestResourceError'
'SimpleTestResourceTypes'
)
HelpInfoURI = 'https://www.contoso.com/help'

Expand Down
Loading