【问题标题】:Evaluate current PowerShell session version from C# class during script module import在脚本模块导入期间从 C# 类评估当前 PowerShell 会话版本
【发布时间】:2020-12-29 03:18:48
【问题描述】:

首先,关于我正在尝试做的事情的一些背景知识。我正在编写一个 PowerShell 脚本模块,该模块需要在导入模块时设置一些变量并运行一些代码。最终需要发生的事情取决于导入模块的 PowerShell 版本。例如,这需要支持 5.1 及更高版本,但 Invoke-RestMethod 直到 PowerShell 6 才支持
-SkipCertificateCheck 参数,这意味着我需要检查版本,创建一个基本上总是返回 truedummy ICertificatePolicy验证时,并将其存储在某个全局位置,如果用户指示他们希望在调用某些 cmdlet 时跳过安全检查,我可以引用它并设置该策略。到目前为止,我有以下代码:

Add-Type @"
  using System.Management.Automation;
  using System.Net;
  using System.Security.Cryptography.X509Certificates;

  namespace MyModuleNamespace {
    public struct State {
      public static ICertificatePolicy SystemCertificatePolicy;
      public static ICertificatePolicy SkipSSLCheckPolicy;
    }

    public class TrustAllCertsPolicy : ICertificatePolicy {
      public bool CheckValidationResult(
        ServicePoint srvPoint, X509Certificate certificate,
        WebRequest request, int certificateProblem) {
        return true;
      }
    }
  }

  namespace MyModuleNamespace.SubNamespace {
    public class ModuleAssemblyInitializer : IModuleAssemblyInitializer {
      public void OnImport() {
        // pseudocode of what I'm struggling to define in C#
        if( CurrentPSSession.PSVersion.Major -lt 6 ) {
          MyModuleNamespace.State.SystemCertificatePolicy = ServicePointManager.CertificatePolicy;
          MyModuleNamespace.State.SkipSSLCheckPolicy = new MyModuleNamespace.TrustAllCertsPolicy();
        }
        // end pseudocode
      }
    }
  }
"@

但是...我正在努力评估当前 PowerShell 会话中的信息,例如评估 $PSVersionTable.PSVersion。我知道如何检查安装了哪些版本的 PowerShell,但我需要能够评估添加了此类的当前运行的 PowerShell 版本。此外,我不希望 entire 模块用 C# 编写,即使这在技术上是一种选择。我团队中的其他人需要维护该模块,虽然他们很了解 PowerShell,但 C# 超出了他们的技能范围。

如果在导入模块时有一种 PowerShell 原生的方式来处理运行代码的情况,我会全力以赴。这是我的第一个 PowerShell 模块,如果我用错误的解决方案来解决这个问题,我不会感到惊讶。

【问题讨论】:

  • 在我的脑海中,在OnImport() 中调用System.Reflection.Assembly.GetCallingAssembly() 应该会返回导入System.Management.Automation.dll 程序集
  • 版本信息不是只返回 .NET 程序集版本的信息,而不是 PowerShell 的版本信息吗?
  • System.Management.Automation.dll PowerShell
  • 是的,但如果我检查版本仍然与运行的 .NET PowerShell 版本匹配,除非我遗漏了什么。例如,如果我从我的 PS 会话中运行 [System.Reflection.Assembly]::GetCallingAssembly(),它仍然只显示 v4.0.30319,而没有关于我正在运行的 PowerShell 版本。至少无论是通过powershell.exe 还是pwsh.exe 运行,我都会在Windows 上获得相同的版本。
  • 在 Linux 机器上使用 PowerShell 7 进行的快速测试也显示了相同的程序集版本。所以这似乎行不通。它看起来确实像程序集位置在 Windows 上的 PowerShell/7 特定路径下,但 Linux 在路径中没有特定版本,至少使用 PowerShell 的 snap 包。在我的情况下,这用于在 Windows PS 和 PS Core 之间进行检测,但如果我需要在 PS 6 和 7 之间进行区分,它就不会有效。不过,我确实找到了适合我的解决方案,并将很快将其作为答案发布。

标签: c# powershell powershell-core


【解决方案1】:

总的来说,我强烈建议不要这样做。盲目地跳过证书信任验证是个坏主意。


正如我(糟糕地)试图在 cmets 中解释的那样,如果您解析调用程序集(即System.Management.Automation.dll),您也许可以使用它在使用反射的初始化程序中发现版本信息:

using System;
using System.Management.Automation;
using System.Reflection;

namespace CrossVersionModule
{
    public class ModuleAssemblyInitializer : IModuleAssemblyInitializer
    {
        public void OnImport()
        {
            // obtain reference to calling assembly
            var caller = Assembly.GetCallingAssembly();

            // locate the PSVersionInfo type
            var psversionType = caller.GetType("System.Management.Automation.PSVersionInfo");
            if (null != psversionType)
            {
                // locate the PSVersion property
                PropertyInfo versionProp = psversionType.GetProperty("PSVersion");
                if (null == versionProp) {
                    // In .NET framework (ie. Windows PowerShell 5.1) we have to explicitly pass binding flags for non-public members
                    versionProp = psversionType.GetProperty("PSVersion", BindingFlags.NonPublic | BindingFlags.Static);
                }

                // Invoke the getter to get the value
                var getter = versionProp.GetGetMethod(true);
                var version = getter.Invoke(null, new object[] { });
                Console.WriteLine(version); // here's where you'd do your version check
            }
        }
    }
}

如果我针对 PowerShell Standard 编译上述内容,则分别在 5.1 和 7 中导入时会得到正确的 PowerShell 版本

【讨论】:

  • 啊,这更有意义,谢谢。我还发现了另一种使用调用/输入程序集的方法(两者都有效),我也会将其作为答案发布。
  • 另外,忽略证书错误只是 Windows PS/PS Core 之间差异的一种情况。这是在开发场景中使用的,我们不需要忽略部署环境中的 SSL 错误。
  • @BendertheGreatest 很酷,只需要为子孙后代打一个免责声明 :) 一般来说,我倾向于将所有版本检查推迟到运行时(通过内部的 PSCmdlet.SessionState.PSVariable 获取 $PSVersionTable 变量例如一个 cmdlet,或者添加一个带有脚本的 ScriptsToProcess 条目,该脚本将版本复制到模块范围以供将来使用)
  • SessionState.PSVariable...感谢@MathiasR.Jessen!
【解决方案2】:

除了 Mathias 的 helpful answer 之外,我还发现了一些能够确定 .NET 版本的方法。

一种是扩展字符串中的表达式,并将以下代码添加到我的struct State 以将来自$PSVersionTable.PSVersion 的值插入到 C# 代码中,然后可以在模块代码的其他地方读取:

public struct State {
  public static ICertificatePolicy SystemCertificatePolicy = ServicePointManager.CertificatePolicy;
  public static ICertificatePolicy SkipSSLCheckPolicy;
  
  // Lines below were added
  public readonly static System.Version PSVersion;

  static State() {
    PSVersion = new Version(
      $($PSVersionTable.PSVersion.Major),
      $($PSVersionTable.PSVersion.Minor),
      $($PSVersionTable.PSVersion.Build),
      $($PSVersionTable.PSVersion.Revision)
    );
  }
}

另一种可行的方法是检查调用程序集的产品属性的值,它可以返回调用程序集上AssemblyProductAttribute 的一些内容之一:

  • 如果直接从 PowerShell 调用
    • Microsoft® .NET Framework 如果使用 Windows PowerShell
    • Microsoft® .NET Core 如果使用 PowerShell Core
  • 如果从使用Add-Type 在 PowerShell 脚本中编译的 C# 调用
    • Microsoft (R) Windows (R) Operating System 如果在 .NET Framework 上
    • PowerShell 如果使用 .NET Core
  • 不确定在针对 .NET Core SDK 编译时,正确编译的 C# 项目会返回什么

在我的情况下,我可以检查属性是否等于 PowerShell 以查看这是否是 .NET Core:

// New using directives
using System;
using System.Reflection;

// Check if .NET Core
AssemblyProductAttribute attribute = Attribute.GetCustomAttribute(Assembly.GetCallingAssembly(), typeof(AssemblyProductAttribute)) as AssemblyProductAttribute;
if(attribute.Product == "PowerShell")) {
  // do .NET Core things
}

似乎第三种方法在运行时有一些问题,我已经从这个答案中删除了它,直到我弄清楚发生了什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多