如何处理PowerShell 6+中不再存在的System程序集问题?
解决方案:适配Impersonation模块到PowerShell 7+
针对你遇到的Joel Bennett的Impersonation模块在PowerShell 7+中因System.Collections.Generic.Stack[System.Security.Principal.WindowsImpersonationContext]类型缺失无法运行的问题,除了调用Windows PowerShell 5.1会话外,还有以下几种更优方案:
1. 重写模块核心逻辑适配.NET Core/.NET 5+
PowerShell 7基于.NET Core/.NET 5+,WindowsImpersonationContext依然存在,但模块原代码依赖的.NET Framework程序集加载逻辑在PS7中不兼容。你可以修改模块的核心实现:
- 显式加载所需程序集:在模块开头添加代码确保加载正确的程序集
Add-Type -AssemblyName System.Security.Principal.Windows - 替换栈类型声明:将原代码中的泛型栈声明替换为兼容PS7的写法
# 修改后(PS7+兼容) $impersonationStack = [System.Collections.Generic.Stack[System.Security.Principal.WindowsImpersonationContext]]::new() - 关键说明:.NET Core中
WindowsImpersonationContext的生命周期管理逻辑与.NET Framework一致,但需通过显式加载程序集避免隐式加载失败的问题。
2. 使用已适配PS7的第三方Impersonation模块
PowerShell Gallery中有多个已兼容PS7的凭据切换模块,可直接替代旧模块:
- 安装
PSImpersonation模块:Install-Module -Name PSImpersonation -Scope CurrentUser -Force - 该模块提供
Invoke-Impersonated等命令,直接支持在PS7中切换凭据访问远程共享,无需依赖旧模块的.NET Framework逻辑。
3. 手动实现Win32 API调用(替代.NET API)
如果不想依赖.NET API的差异,可以直接调用Windows原生API实现 impersonation,核心是通过P/Invoke调用LogonUser、ImpersonateLoggedOnUser和RevertToSelf等函数:
Add-Type @" using System; using System.Runtime.InteropServices; using System.Security.Principal; public class ImpersonationHelper { [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool LogonUser( string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); [DllImport("advapi32.dll", SetLastError = true)] public static extern bool RevertToSelf(); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr hObject); public static WindowsImpersonationContext Impersonate(string username, string domain, string password) { IntPtr token = IntPtr.Zero; try { if (!LogonUser(username, domain, password, 9, 0, out token)) { throw new System.ComponentModel.Win32Exception(); } if (!ImpersonateLoggedOnUser(token)) { throw new System.ComponentModel.Win32Exception(); } return WindowsIdentity.Impersonate(token); } finally { if (token != IntPtr.Zero) { CloseHandle(token); } } } } "@ # 使用示例 $context = [ImpersonationHelper]::Impersonate("RemoteUser", "Domain", "Password") try { # 执行需要 impersonation 的操作,比如访问远程共享 Get-ChildItem "\\RemoteMachine\Share" } finally { $context.Undo() $context.Dispose() }
这种方式完全绕过.NET版本差异,直接与Windows系统交互,兼容性更强。
4. 简化Windows PowerShell会话调用
如果坚持使用旧模块,可简化原会话调用代码,无需手动管理会话生命周期:
Invoke-Command -UseWindowsPowerShell { # 加载旧模块并执行命令 Import-Module Impersonation My-LegacyCmdlet -Credential $Using:cred -Action { Get-ChildItem "\\RemoteMachine\Share" } }
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

