winget-cli

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

commit c5f7df49fefc52fae9de42381a8559481b790439
parent 41e2b4c5bcb24a3243f8d055e20c2d289305110f
Author: Ruben Guerrero <rubengu@microsoft.com>
Date:   Fri, 24 Mar 2023 10:51:18 -0700

Throw exception if Import-Module fails (#3104)

Try to import the module of the DSC resource before calling Invoke-DscResource. This let us fail earlier and get more information about the error.
Diffstat:
Msrc/AppInstallerSharedLib/Public/AppInstallerErrors.h | 1+
Msrc/Microsoft.Management.Configuration.Processor/Exceptions/ErrorCodes.cs | 5+++++
Asrc/Microsoft.Management.Configuration.Processor/Exceptions/ImportModuleException.cs | 33+++++++++++++++++++++++++++++++++
Msrc/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/HostedEnvironment.cs | 18++++++++++++++++++
Msrc/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/IProcessorEnvironment.cs | 8+++++++-
Msrc/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs | 1+
Msrc/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs | 18++++++++++++++++++
7 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -173,6 +173,7 @@ #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) +#define WINGET_CONFIG_ERROR_UNIT_IMPORT_MODULE ((HRESULT)0x8A15C108) namespace AppInstaller { diff --git a/src/Microsoft.Management.Configuration.Processor/Exceptions/ErrorCodes.cs b/src/Microsoft.Management.Configuration.Processor/Exceptions/ErrorCodes.cs @@ -45,5 +45,10 @@ namespace Microsoft.Management.Configuration.Processor.Exceptions /// Internal error calling Get-DscResource. More than one module found with the same version. /// </summary> internal const int WinGetConfigUnitModuleConflict = unchecked((int)0x8A15C107); + + /// <summary> + /// The module where the DSC resource is implemented cannot be imported. + /// </summary> + internal const int WinGetConfigUnitImportModule = unchecked((int)0x8A15C108); } } diff --git a/src/Microsoft.Management.Configuration.Processor/Exceptions/ImportModuleException.cs b/src/Microsoft.Management.Configuration.Processor/Exceptions/ImportModuleException.cs @@ -0,0 +1,33 @@ +// ----------------------------------------------------------------------------- +// <copyright file="BlockedFileException.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.Processor.Exceptions +{ + using System; + + /// <summary> + /// Import-Module threw an exception. + /// </summary> + internal class ImportModuleException : Exception + { + /// <summary> + /// Initializes a new instance of the <see cref="ImportModuleException"/> class. + /// </summary> + /// <param name="moduleName">Module name.</param> + /// <param name="inner">Inner exception.</param> + public ImportModuleException(string? moduleName, Exception inner) + : base($"Could not import module: {moduleName?.ToString() ?? "<no module>"}", inner) + { + this.HResult = ErrorCodes.WinGetConfigUnitImportModule; + this.ModuleName = moduleName; + } + + /// <summary> + /// Gets the module name. + /// </summary> + public string? ModuleName { get; } + } +} diff --git a/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/HostedEnvironment.cs b/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/HostedEnvironment.cs @@ -8,6 +8,7 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces { using System; using System.Collections.Generic; + using System.IO; using System.Linq; using System.Management.Automation; using System.Management.Automation.Runspaces; @@ -197,6 +198,23 @@ namespace Microsoft.Management.Configuration.Processor.Runspaces } /// <inheritdoc/> + public void ImportModule(string path) + { + if (!File.Exists(path)) + { + throw new FileNotFoundException(path); + } + + using PowerShell pwsh = PowerShell.Create(this.Runspace); + + _ = pwsh.AddCommand(Commands.ImportModule) + .AddParameter(Parameters.Name, path) + .Invoke(); + + this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); + } + + /// <inheritdoc/> public PSObject? GetInstalledModule(ModuleSpecification moduleSpecification) { var parameters = new Dictionary<string, object>() diff --git a/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/IProcessorEnvironment.cs b/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/IProcessorEnvironment.cs @@ -98,12 +98,18 @@ namespace Microsoft.Management.Configuration.Processor.ProcessorEnvironments PSModuleInfo? GetAvailableModule(string path); /// <summary> - /// Call Import-Module with the fully qualified name. + /// Calls Import-Module with the fully qualified name. /// </summary> /// <param name="moduleSpecification">Module specification.</param> void ImportModule(ModuleSpecification moduleSpecification); /// <summary> + /// Calls Import-Module with a file path. + /// </summary> + /// <param name="path">Module file path.</param> + void ImportModule(string path); + + /// <summary> /// Calls Get-InstalledModule. /// </summary> /// <param name="moduleSpecification">Module specification.</param> diff --git a/src/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs b/src/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs @@ -119,6 +119,7 @@ namespace Microsoft.Management.Configuration.Processor { builder.Append($"{p.Name} = '{p.Value}' "); } + builder.Append("]"); } diff --git a/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs b/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs @@ -202,6 +202,24 @@ namespace Microsoft.Management.Configuration.Processor.Set } } + // PowerShell will prompt the user when a module that is downloaded from the internet is imported. + // For a hosted environment, this will throw an exception because it doesn't support user interaction. + // In the case we don't import the module here, eventually Invoke-DscResource will fail for class + // resources because they will call a method on a null obj. It is easier to just fail here. + // The exception being thrown will have the correct details (user needs to call Unblock-File) + // instead of the cryptic Invoke with 0 arguments. + if (dscResourceInfo.Path is not null) + { + try + { + this.ProcessorEnvironment.ImportModule(dscResourceInfo.Path); + } + catch (Exception e) + { + throw new ImportModuleException(dscResourceInfo.ModuleName, e); + } + } + return dscResourceInfo; }