Linux Azure应用服务尝试安装PS模块时出现错误“无法识别安装模块”

vq8itlhq  于 2023-08-03  发布在  Linux
关注(0)|答案(1)|浏览(102)

我有一个ASP.NET Core Web应用程序(Linux)作为Azure应用程序服务,并且有一个页面尝试安装ExchangeOnlineManagement PowerShell模块。该脚本需要连接到exchange并调用Get-EXOMailbox。脚本报告PowerShell v7.3.6。
我得到这个错误:术语“Install-Module”不能识别为cmdlet、函数、脚本文件或可执行程序的名称

var scriptContents = "if(-not (Get-Module ExchangeOnlineManagement -ListAvailable))" + Environment.NewLine +
                    "{ " + Environment.NewLine +
                        "Write-Host $PSVersionTable.PSVersion" + Environment.NewLine +
                        "Install-Module ExchangeOnlineManagement -Scope CurrentUser -Force" + Environment.NewLine +
                    "}";
            
using (PowerShell ps = PowerShell.Create())
{
    ps.AddScript(scriptContents);
    var pipelineObjects = await ps.InvokeAsync().ConfigureAwait(false);
}

字符串

q3qa4bjr

q3qa4bjr1#

上下文

  • 使用PowerShell NuGet package构建的PowerShell(核心)SDK项目-即托管其自己的PowerShell副本的应用程序或库-不 * 捆绑独立PowerShell (Core) 7+安装所做的相同非内置模块。
  • 值得注意的是,这意味着PowerShell SDK项目不附带PowerShellGet模块,而Install-Module cmdlet是该模块的一部分。
  • Windows 上,存在预定义的PSModulePath环境变量($env:PSModulePath)指向旧版 Windows PowerShell 目录,即使从PowerShell会话 * 外部 * 运行可执行文件,您仍然可以调用Install-Module,在没有-Scope参数的情况下,这将导致目标模块的 * 用户级 * 安装,Windows PowerShell会话也会看到。
  • 如果您碰巧从 PowerShell(Core)7+ 会话调用可执行文件,则目标是后者的用户级模块根目录,因此PowerShell(Core)7+会话也会看到任何已安装的模块。
  • 在类似Unix的平台上-例如您的情况(Linux)-没有 * 预定义的PSModulePath环境变量,这解释了您的症状。
  • 虽然PowerShell本身(在两个版本中)在启动时使用默认值定义PSModulePath,但在SDK项目中,它将使用 * 托管应用程序 * 的目录作为其 * 系统 * 模块的目录,即与PowerShell本身捆绑在一起。
    结果
    托管PowerShell * 的应用程序/库可以 * 通过预先存在的 * 独立 * PowerShell安装 * 找到PowerShellGet模块(托管Install-Module cmdlet):
    • 始终 * 在 Windows 上,因为Windows随附了Windows PowerShell。
  • 如果您碰巧从PowerShell(Core)7+会话启动应用程序,则将使用后者的模块。
    • 只是顺便说一句,如果在 Unix 类平台上:
  • 如果你碰巧从PowerShell(Core)7+会话启动你的应用程序(而不是从Bash),你将使用后者的模块。
  • 否则,您的应用程序将无法找到该模块。
    注意事项
  • 即使存在,Install-Module的使用也总是针对模块“借用”的独立PowerShell安装的模块目录-这可能是不希望的。
  • 从托管自己的PowerShell副本的应用程序中按需安装PowerShell模块 *

由于不能假定Install-Module cmdlet存在于非Windows平台上,因此需要 * 自定义代码 * 来按需下载和安装模块。

  • 虽然这显然很麻烦,但它的优点是允许您在所选的本地目录中安装所需模块的私有副本。
  • 一个潜在的替代方案是在开发时 * 下载PowerShellGetPackageManagement模块 * 并 * 将它们与您的应用程序/库捆绑在一起 * -但这将再次安装到与独立PowerShell(核心)安装共享的模块目录中。

以下代码自动执行Manual Package Download中记录的手动步骤,以便从PowerShell Gallery直接下载和安装模块:

  • 在下面指定应安装按需下载的模块的本地目录的完整路径。
  • 代码的限制
    • 依赖关系 *,即您的目标模块需要的其他模块也必须 * 显式 * 下载。
  • 为了简单起见,代码只是下载指定(模块)的 * 最新稳定 * 版本,尽管添加对版本控制的支持并不需要更多的工作。
  • 有关详细信息,请参阅源代码注解。
# Make the Write-Verbose statements below produce output.
# Set to $false to silence them.
$verbose = $true

# The name(s) of the module(s) to download.
# If a module has *dependencies*, i.e. requires other modules to function,
# append them to the array.
# SEE NOTE ABOUT VERSIONS BELOW.
$requiredModules = @('ExchangeOnlineManagement')

# Where to install manually downloaded modules.
# Note: Be sure to use a FULL PATH.
$modulesRootDir = "$HOME\.MyApp\Modules"

# Add the root dir. of all manually installed modules to $env:PSModulePath,
# if necessary.
if (($env:PSModulePath -split [IO.Path]::PathSeparator) -notcontains $modulesRootDir) {
  $env:PSModulePath += [IO.Path]::PathSeparator + $modulesRootDir
}

# Determine which modules need to be dowloaded, if any.
$missingModules = 
  Compare-Object -PassThru $requiredModules @(Get-Module -ListAvailable $requiredModules | ForEach-Object Name)

# Download and install any missing modules.
foreach ($moduleName in $missingModules) {

  # Silence the progress display during download and ZIP archive extraction.
  # Note: For this to be effective for Expand-Archive, the *global* variable must be set.
  $prevProgressPreference = $global:ProgressPreference
  $global:ProgressPreference = 'SilentlyContinue'

  try {
  
    # NOTE RE VERSIONING: 
    # For simplicity, this code does NOT support versioning, which means:
    #   * The *latest stable version* of each module is downloaded.
    #   * Such a version is placed directly in directory named for the module, 
    #     i.e. installation of *multiple versions*, side by side - which would
    #     require version-specific subdirs. - is *not* supported here.
    # To download a specific version, simply append /<version> to the URL below, e.g.:
    #       https://www.powershellgallery.com/api/v2/package/PSReadLine/2.2.6
  
    # Derive the download URL, the local installation dir., and path of the temp. ZIP file.
    $downloadUrl = 'https://www.powershellgallery.com/api/v2/package/{0}' -f $moduleName
    $moduleDir = Join-Path $modulesRootDir $moduleName
    $tempZipFile = Join-Path $moduleDir "$moduleName.zip"
  
    Write-Verbose -Verbose:$verbose "Downloading and installing module $moduleName to $moduleDir..."
    
    # Make sure the target directory exists.
    $null = New-Item -ErrorAction Stop -ItemType Directory -Force $moduleDir    
  
    # Download the *.nupkg file, as a *.zip file (which it technically is)
    Invoke-WebRequest -ErrorAction Stop $downloadUrl -OutFile $tempZipFile
      
    # Extract the contents of the *.zip file.
    Expand-Archive -ErrorAction Stop -Force -LiteralPath $tempZipFile -DestinationPath $moduleDir
    
    # Clean up files that aren't needed locally.
    Get-Item $moduleDir\* -Include *.zip, _rels, '`[Content_Types`].xml', *.nuspec, package |
      Remove-Item -Recurse -Force
  }
  finally {
    $global:ProgressPreference = $prevProgressPreference
  }

}

# Now you can import the modules - either explicitly, as in this example, 
# or implicitly, by module auto-loading via $env:PSModulePath
Write-Verbose -Verbose:$verbose "Importing module $($requiredModules[0])..."
Import-Module -ErrorAction Stop $requiredModules[0]

字符串

相关问题