带RunspacePool的c# PowerShell -如何导入像Get-Mailbox这样的Exchange cmd

本文关键字:Get-Mailbox cmd Exchange 导入 PowerShell RunspacePool 何导入 | 更新日期: 2023-09-27 18:17:52

我目前正在构建一个Windows-Service,它将在一些请求进来时执行PowerShell命令。会有很多请求进来,所以我采用了以下代码:使用Powershell RunspacePool多线程从c#到远程服务器

我不使用WSManConnectionInfo,但只是做New-PSSession。请参阅本问题末尾的我的代码。

这似乎对所有常规命令都很有效,但使用exchange cmdlet并在之前执行Import-PSSession的命令除外。我不想每次脚本运行时都导入cmdlet,但我没有从c#中正确导入。在调用之后,PowerShell ErrorStream还注意到术语"Get-Mailbox"是未知的。我不明白为什么导入不起作用,我需要为它做些什么。

我曾经工作过一次,但只有一个运行空间。在移动到RunspacePool后,我无法让它再次工作。

任何帮助都将是非常感激的。

代码:

static void Main(string[] args)
{ 
    string connectionUri = "https://.../Powershell";
    string userName = @"...'...";
    string password = "...";
    System.Uri uri = new Uri(connectionUri);
    System.Security.SecureString securePassword = String2SecureString(password);
    System.Management.Automation.PSCredential creds = new System.Management.Automation.PSCredential(userName, securePassword);
    //InitialSessionState iss = InitialSessionState.CreateDefault();
    //iss.ImportPSModule(new[] { "Get-Mailbox", "Get-MailboxStatistics" });
    RunspacePool runspacePool = RunspaceFactory.CreateRunspacePool();
    runspacePool.ThreadOptions = PSThreadOptions.UseNewThread;
    PowerShell powershell = PowerShell.Create();
    PSCommand command = new PSCommand();
    command.AddCommand(string.Format("New-PSSession"));
    command.AddParameter("ConfigurationName", "Microsoft.Exchange");
    command.AddParameter("ConnectionUri", uri);
    command.AddParameter("Credential", creds);
    command.AddParameter("Authentication", "Basic");
    command.AddParameter("AllowRedirection");
    // IS THIS NEEDED?
    PSSessionOption sessionOption = new PSSessionOption();
    sessionOption.SkipCACheck = true;
    sessionOption.SkipCNCheck = true;
    sessionOption.SkipRevocationCheck = true;
    command.AddParameter("SessionOption", sessionOption);
    powershell.Commands = command;
    runspacePool.Open();
    powershell.RunspacePool = runspacePool;
    Collection<PSSession> result = powershell.Invoke<PSSession>();
    foreach (ErrorRecord current in powershell.Streams.Error)
    {
        Console.WriteLine("Exception: " + current.Exception.ToString());
        Console.WriteLine("Inner Exception: " + current.Exception.InnerException);
    }
    // Returns the session
    if (result.Count != 1)
        throw new Exception("Unexpected number of Remote Runspace connections returned.");
    // THATS THE PART NOT WORKING
    // First import the cmdlets in the current runspace (using Import-PSSession)
    powershell = PowerShell.Create();
    command = new PSCommand();
    command.AddScript("Import-PSSession $Session -CommandName Get-Mailbox, Get-MailboxStatistics -AllowClobber -WarningAction SilentlyContinue -ErrorAction Stop -DisableNameChecking | Out-Null");
    command.AddParameter("Session", result[0]);
    // This is also strange... without the RunspaceInvoke I always get a SecurityException... 
    RunspaceInvoke scriptInvoker = new RunspaceInvoke();
    scriptInvoker.Invoke("Set-ExecutionPolicy -Scope Process Unrestricted");
    var tasks = new List<Task>();
    for (var i = 0; i < 3; i++)
    {
        var taskID = i;
        var ps = PowerShell.Create();
        ps.RunspacePool = runspacePool;
        PSCommand cmd = new PSCommand();
        cmd.AddCommand(@".'PSScript1.ps1");
        //cmd.AddScript("Get-Mailbox -ResultSize 5");
        cmd.AddParameter("Session", result[0]);
        ps.Commands = cmd;
        var task = Task<PSDataCollection<PSObject>>.Factory.FromAsync(
                                             ps.BeginInvoke(), r => ps.EndInvoke(r));
        System.Diagnostics.Debug.WriteLine(
                          string.Format("Task {0} created", task.Id));
        task.ContinueWith(t => System.Diagnostics.Debug.WriteLine(
                          string.Format("Task {0} completed", t.Id)),
                          TaskContinuationOptions.OnlyOnRanToCompletion);
        task.ContinueWith(t => System.Diagnostics.Debug.WriteLine(
                          string.Format("Task {0} faulted ({1} {2})", t.Id,
                          t.Exception.InnerExceptions.Count,
                          t.Exception.InnerException.Message)),
                          TaskContinuationOptions.OnlyOnFaulted);
        tasks.Add(task);
    }
    Task.WaitAll(tasks.ToArray());               
}
private static SecureString String2SecureString(string password)
{
    SecureString remotePassword = new SecureString();
    for (int i = 0; i < password.Length; i++)
        remotePassword.AppendChar(password[i]);
    return remotePassword;
}

脚本简写:

Param($Session)
Import-PSSession $Session -CommandName Get-Mailbox, Get-MailboxStatistics -ErrorAction SilentlyContinue | Out-Null
Get-Mailbox -ResultSize 5 | SElectObject Name, Alias, ...

脚本是这样工作的,但是当我试图注释掉Import-PSSession部分时,我得到了未知的术语get - mailbox错误。

提前感谢并致以最良好的问候Gope

带RunspacePool的c# PowerShell -如何导入像Get-Mailbox这样的Exchange cmd

您需要取消ISS内容的注释。像这样做:

$iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$iss.ImportPSModule($module)

将$module替换为Exchange模块的名称,或者直接用模块名称填充变量。

当你创建RunspacePool时,执行如下操作:

$runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool($minRunspaces, $maxRunspaces, $iss, $Host)

这应该使模块可用于从运行空间池创建的所有运行空间。

我找到了一种使它工作的方法。我将AddScript更改为AddCommand,并使用AddParameter。

// First import the cmdlets in the current runspace (using Import-PSSession)
powershell = PowerShell.Create();
powerShell.RunspacePool = runspacePool;
command = new PSCommand();
command.AddScript("Import-PSSession");
command.AddParameter("Session", result[0]);
command.AddParameter("CommandName", new[]{"Get-Mailbox", "Get-MailboxStatistics"});
command.AddParameter("ErrorAction ", "SilentlyContinue ");

powerShell.Invoke();

那起作用了…