我已经编写了一些使用 RunspaceFactory 通过 C# 执行 Powershell 的内容。
我正在加载默认的 Powershell 配置文件,如下所示:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
string scriptText = @". .\" + scriptFileName + "; " + command;
Pipeline pipeline = runspace.CreatePipeline(scriptText);
Run Code Online (Sandbox Code Playgroud)
命令=我知道有效的配置文件中的一个函数。
所有这些 Powershell 内容都包含在 Impersonator 中。
为避免疑义,$profile = C:\Users\Administrator\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
这是一个在 IIS 7.5 下运行的 Web 应用程序
如果我的 IIS 应用程序在“管理员”帐户下运行,则它可以工作。在任何其他帐户下都会引发错误:
“术语“.\Microsoft.PowerShell_profile.ps1”未被识别为 cmdlet、函数、脚本文件或可操作程序的名称。请检查名称的拼写,或者如果包含路径,请验证该路径是否为改正并重试。”
当我冒充“管理员”帐户时,我认为该位置是正确的。
一些带有调用的“get-location”的日志记录报告目录应该是什么。
出于血腥的想法,我试图强迫它:
System.Environment.CurrentDirectory = dir;
Run Code Online (Sandbox Code Playgroud)
...并且还尝试调用“设置位置”。这些工作,但如果我从运行空间调用“get-location”,它会报告与之前相同的目录(正确的目录)。
我认为模拟可能存在问题,因此我编写了一些测试,以应用程序池不应该执行的方式接触文件系统。这些工作。
我还检查了这个:
string contextUserName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
Run Code Online (Sandbox Code Playgroud)
当代码“包装”在模拟程序中以及通过应用程序池身份执行时,它都会报告正确的用户。然后我绝望了并尝试调用:
@"cmd /c dir"
Run Code Online (Sandbox Code Playgroud)
...(还有 get-Childitem)。两个命令都返回:
{}
Run Code Online (Sandbox Code Playgroud)
...在应用程序池身份下运行时(无论是否模拟),但当应用程序池作为“管理员”运行时,正确目录的完整且准确的目录列表。
我确信我在这里错过了一些愚蠢而基本的东西,如果有人能给我一些关于我在思维(和代码)中犯了错误的地方的指导,那就太好了。
这是在模拟上下文中在 ASP.NET 中使用 PowerShell 时出现的一个“众所周知”的问题:它无法按照人们认为应有的方式工作。
原因是 PowerShell 在幕后启动另一个线程来实际完成其所有工作。PowerShell 内的新线程不会继承模拟的上下文。
修复的结果并不完美。这篇 MSDN 博客文章建议将 ASP.NET 设置为始终遵循模拟策略(以便幕后的线程获取身份):
<configuration>
<runtime>
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
</runtime>
</configuration>
Run Code Online (Sandbox Code Playgroud)
另一种更丑陋的方法(尽管不那么老套)是使用WinRM。您可以将环回 PowerShell 会话(连接到本地主机)与 PowerShell 结合使用,并让 WinRM 处理模拟。这需要 System.Management.Automation 版本 3.0.0.0。
var password = "HelloWorld";
var ss = new SecureString();
foreach (var passChar in password)
{
ss.AppendChar(passChar);
}
var psCredential = new PSCredential("username", ss);
var connectionInfo = new WSManConnectionInfo(new Uri("http://localhost:5985/wsman"), "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", psCredential);
using (var runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
connectionInfo.EnableNetworkAccess = true;
using (var powershell = PowerShell.Create())
{
Run Code Online (Sandbox Code Playgroud)
这也是一个俗气的解决方案,因为它需要运行 WinRM 的 Windows 服务并配置 WinRM。WinRM 并不完全是“正常工作”的东西,但它在第一个选项不合适的情况下完成了工作。
WSManConnectionInfo 中使用的 URL 是本地主机 WinRM 端点,默认情况下它侦听版本 3 中的端口 5985,并为连接指定凭据。