c# 中的 powershell 交换命令 - 运行空间限制
powershell exchange commands in c# - runspace limits
最近我一直在 .net 项目中创建一些模块,它通过远程 powershell 发送命令到交换服务器。
我在使用 runespace 时遇到很多问题 - 有时它的状态被破坏,有时我超过了允许的最大连接数(3)
我不知道该怎么做。
下面的代码不是很好,但它比以前的代码工作得更好 - 所以现在请不要看质量
第一个 class 负责返回 runespace(和 powershell 连接)- 我已将此 class 注册为单例(它是 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();
}
}
第二个class是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);
这个 class 是由特定的 classes 派生的,这些es已经用参数
覆盖了命令(如 Get-Group、Get-User 等)
它可以工作,但 powershell remote 经常会出现一些错误:
- 超出了 runespace 的限制(我想我只创建了一个 - 如果它坏了我会处理它并创建一个新的)
-exeeding time litims - 我必须在最新命令调用后等待 X 秒...
-坏 xml - 这是最奇怪的事情 - 远程 powershell 回答我说我发送了错误的 xml 数据 - 它很少发生并且是完全随机的
我知道代码有点混乱,但是当我尝试我在互联网上找到的最简单的方法时,确实有更多与时间限制和运行空间限制相关的错误
命令会经常执行 - 可能有 5 个用户可以同时执行某个命令。
使用远程 powershell 是在 exchange 上对启用了安全邮件的组进行操作的唯一可能方法,所以我不能使用图表 api...
我通过组合锁和单例解决了这个问题;)
最近我一直在 .net 项目中创建一些模块,它通过远程 powershell 发送命令到交换服务器。 我在使用 runespace 时遇到很多问题 - 有时它的状态被破坏,有时我超过了允许的最大连接数(3)
我不知道该怎么做。 下面的代码不是很好,但它比以前的代码工作得更好 - 所以现在请不要看质量
第一个 class 负责返回 runespace(和 powershell 连接)- 我已将此 class 注册为单例(它是 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();
}
}
第二个class是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);
这个 class 是由特定的 classes 派生的,这些es已经用参数
覆盖了命令(如 Get-Group、Get-User 等)它可以工作,但 powershell remote 经常会出现一些错误: - 超出了 runespace 的限制(我想我只创建了一个 - 如果它坏了我会处理它并创建一个新的) -exeeding time litims - 我必须在最新命令调用后等待 X 秒... -坏 xml - 这是最奇怪的事情 - 远程 powershell 回答我说我发送了错误的 xml 数据 - 它很少发生并且是完全随机的
我知道代码有点混乱,但是当我尝试我在互联网上找到的最简单的方法时,确实有更多与时间限制和运行空间限制相关的错误
命令会经常执行 - 可能有 5 个用户可以同时执行某个命令。
使用远程 powershell 是在 exchange 上对启用了安全邮件的组进行操作的唯一可能方法,所以我不能使用图表 api...
我通过组合锁和单例解决了这个问题;)