From 482c5033e1e1bcd4ffc7101b0996e950f7735743 Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Thu, 23 Mar 2023 18:09:50 -0700 Subject: [PATCH 1/2] Import module exception --- .../Public/AppInstallerErrors.h | 1 + .../Exceptions/ErrorCodes.cs | 5 +++ .../Exceptions/ImportModuleException.cs | 33 +++++++++++++++++++ .../HostedEnvironment.cs | 18 ++++++++++ .../IProcessorEnvironment.cs | 8 ++++- .../ConfigurationSetProcessorFactory.cs | 1 + .../Set/ConfigurationSetProcessor.cs | 18 ++++++++++ 7 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.Management.Configuration.Processor/Exceptions/ImportModuleException.cs diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h index b161598c9c..1f85fc05a7 100644 --- 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 index 5a8505ba9e..42d23cb6d7 100644 --- a/src/Microsoft.Management.Configuration.Processor/Exceptions/ErrorCodes.cs +++ b/src/Microsoft.Management.Configuration.Processor/Exceptions/ErrorCodes.cs @@ -45,5 +45,10 @@ internal static class ErrorCodes /// Internal error calling Get-DscResource. More than one module found with the same version. /// internal const int WinGetConfigUnitModuleConflict = unchecked((int)0x8A15C107); + + /// + /// The module where the DSC resource is implemented cannot be imported. + /// + 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 new file mode 100644 index 0000000000..4fba2d1821 --- /dev/null +++ b/src/Microsoft.Management.Configuration.Processor/Exceptions/ImportModuleException.cs @@ -0,0 +1,33 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.Processor.Exceptions +{ + using System; + + /// + /// Import-Module threw an exception. + /// + internal class ImportModuleException : Exception + { + /// + /// Initializes a new instance of the class. + /// + /// Module name. + /// Inner exception. + public ImportModuleException(string? moduleName, Exception inner) + : base($"Could not import module: {moduleName?.ToString() ?? ""}", inner) + { + this.HResult = ErrorCodes.WinGetConfigUnitImportModule; + this.ModuleName = moduleName; + } + + /// + /// Gets the module name. + /// + public string? ModuleName { get; } + } +} diff --git a/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/HostedEnvironment.cs b/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/HostedEnvironment.cs index 15b53891ed..4ce66c0789 100644 --- 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; @@ -196,6 +197,23 @@ public void ImportModule(ModuleSpecification moduleSpecification) this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh); } + /// + 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); + } + /// public PSObject? GetInstalledModule(ModuleSpecification moduleSpecification) { diff --git a/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/IProcessorEnvironment.cs b/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/IProcessorEnvironment.cs index 8a4299b204..324dff2241 100644 --- a/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/IProcessorEnvironment.cs +++ b/src/Microsoft.Management.Configuration.Processor/ProcessorEnvironments/IProcessorEnvironment.cs @@ -98,11 +98,17 @@ internal interface IProcessorEnvironment PSModuleInfo? GetAvailableModule(string path); /// - /// Call Import-Module with the fully qualified name. + /// Calls Import-Module with the fully qualified name. /// /// Module specification. void ImportModule(ModuleSpecification moduleSpecification); + /// + /// Calls Import-Module with a file path. + /// + /// Module file path. + void ImportModule(string path); + /// /// Calls Get-InstalledModule. /// diff --git a/src/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs b/src/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs index bf904b60a4..374c6317ea 100644 --- a/src/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs +++ b/src/Microsoft.Management.Configuration.Processor/Public/ConfigurationSetProcessorFactory.cs @@ -115,6 +115,7 @@ internal void OnDiagnostics(DiagnosticLevel level, PowerShell pwsh) { 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 index e75ab3c0ec..f7cf4bb98b 100644 --- a/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs +++ b/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs @@ -202,6 +202,24 @@ private DscResourceInfoInternal PrepareUnitForProcessing(ConfigurationUnitIntern } } + // 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 criptic 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; } From bceefb28a72bc1ca03d92861276c213f2dc08bfc Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Samaniego Date: Fri, 24 Mar 2023 09:20:09 -0700 Subject: [PATCH 2/2] one day ill learn english --- .../Set/ConfigurationSetProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs b/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs index f7cf4bb98b..5866a520b7 100644 --- a/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs +++ b/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessor.cs @@ -207,7 +207,7 @@ private DscResourceInfoInternal PrepareUnitForProcessing(ConfigurationUnitIntern // 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 criptic Invoke with 0 arguments. + // instead of the cryptic Invoke with 0 arguments. if (dscResourceInfo.Path is not null) { try