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

Throw exception if Import-Module fails #3104

Merged
merged 2 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions src/AppInstallerSharedLib/Public/AppInstallerErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,10 @@ internal static class ErrorCodes
/// 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);
}
}
Original file line number Diff line number Diff line change
@@ -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; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -196,6 +197,23 @@ public void ImportModule(ModuleSpecification moduleSpecification)
this.OnDiagnostics(DiagnosticLevel.Verbose, pwsh);
}

/// <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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,17 @@ internal interface IProcessorEnvironment
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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ internal void OnDiagnostics(DiagnosticLevel level, PowerShell pwsh)
{
builder.Append($"{p.Name} = '{p.Value}' ");
}

builder.Append("]");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down