问题描述
最近我一直在.net项目中创建一些模块,该模块通过远程Powershell向交换服务器发送命令。 我有很多符文空间问题-有时状态被破坏,有时我超过了允许的最大连接数(3)
我不知道该怎么做。 下面的代码并不是很好,但是比以前的代码效果更好-因此,请暂时不要关注质量
第一堂课负责返回符文空间(和powershell连接)-我已经将该课注册为单例(这是webapi项目)
public class PowershellCommandEnvironment : IPowershellCommandEnvironment,Idisposable
{
readonly (string user,string password) powerShellAuth;
private static Runspace _runspace = null;
WSManConnectionInfo _connectionInfo;
public PowershellCommandEnvironment()
{
powerShellAuth.user = CloudConfigurationManager.GetSetting("ExchangePowerShellUser");
powerShellAuth.password = CloudConfigurationManager.GetSetting("ExchangePowerShellPassword");
securestring secureStrin = new NetworkCredential("",powerShellAuth.password).SecurePassword;
var creds = new PSCredential(powerShellAuth.user,secureStrin);
_connectionInfo = new WSManConnectionInfo(new Uri("https://outlook.office365.com/powershell-liveid/"),"http://schemas.microsoft.com/powershell/Microsoft.Exchange",creds);
_connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
_connectionInfo.MaximumConnectionRedirectionCount = 2;
_runspace = RunspaceFactory.CreateRunspace(_connectionInfo);
_runspace.StateChanged += _runspace_StateChanged;
}
private void _runspace_StateChanged(object sender,RunspaceStateEventArgs e)
{
var state = _runspace.RunspaceStateInfo.State;
switch (state)
{
case RunspaceState.broken:
_runspace.Close();
_runspace.dispose();
_runspace = RunspaceFactory.CreateRunspace(_connectionInfo);
break;
case RunspaceState.opening:
Thread.Sleep(500);
break;
case RunspaceState.BeforeOpen:
_runspace.open();
break;
}
}
public Runspace GetRunspace()
{
while (_runspace.RunspaceStateInfo.State != RunspaceState.Opened)
{
OpenRunSpaceTimeExceededAttempt(0);
Thread.Sleep(100);
}
return _runspace;
}
private void OpenRunSpaceTimeExceededAttempt(int attempt)
{
if (attempt > 2)
return;
try
{
var state = _runspace?.RunspaceStateInfo.State;
if (_runspace == null || state == RunspaceState.Closed)
{
_runspace = RunspaceFactory.CreateRunspace(_connectionInfo);
_runspace.open();
}
if (state == RunspaceState.BeforeOpen)
_runspace.open();
if (!(state == RunspaceState.Opened))
{
OpenRunSpaceTimeExceededAttempt(attempt+1);
}
}
catch (Exception ex)
{
if (ex.Message.Contains("Please wait for"))
{
System.Threading.Thread.Sleep(10000);
}
OpenRunSpaceTimeExceededAttempt(attempt + 1);
}
}
public void dispose()
{
_runspace.dispose();
}
}
第二类是PowershellComand,它负责执行命令
protected abstract Dictionary<string,object> Parameters { get; set; }
protected abstract string Command { get; }
private static Runspace _runspace = null;
public PowershellCommand(IPowershellCommandEnvironment powershellCommandEnvironment)
{
_runspace = powershellCommandEnvironment.GetRunspace();
Parameters = new Dictionary<string,object>();
}
public T Execute(int attemp=0)
{
if (attemp > 2)
return null;
try
{
using (var powershell = System.Management.Automation.PowerShell.Create())
{
powershell.Runspace = _runspace;
powershell.AddCommand(Command);
foreach (var param in Parameters)
{
powershell.AddParameter(param.Key,param.Value);
}
Collection<PSObject> result = powershell.Invoke();
powershell.Runspace.dispose();
return Map(result);
}
}
catch(Exception ex)
{
string logMessage = $"Command ${Command} not suceeded.{Environment.NewLine} {ex.Message} {ex.InnerException?.Message}";
_logger.Log(LogLevel.Error,logMessage);
int sleep = 5000;
if (ex.Message.Contains("Please wait for"))
{
sleep = 10000;
_logger.Log(LogLevel.Error,"waiting 10000 seconds (powershell command time exceeded");
}
Thread.Sleep(sleep);
return Execute(attemp+1);
}
}
protected abstract T Map(IEnumerable<PSObject> psobj);
此类由特定类派生,这些特定类已使用参数覆盖Command(例如Get-Group,Get-User等)
它可以工作,但通常Powershell Remote会出现一些错误: 超出了符文空间的限制(我认为我只能创建一个-如果打破就将其丢弃并创建一个新的) 结束时间-我必须在最新命令调用后等待X秒... -错误的xml-这是最奇怪的事情-远程Powershell回答我发送了错误的xml数据-很少发生,并且是完全随机的
我知道代码有点混乱,但是当我尝试了在互联网上发现的最简单的方法时,与时间限制和运行空间限制有关的错误确实很多
命令将经常执行-可能5个用户可以同时执行某些命令。
使用远程Powershell是在交换时对启用了安全性的邮件启用的组进行操作的唯一可能方法,因此我无法使用图形API ...
解决方法
我通过组合锁和单例来解决了这个问题;)