【发布时间】:2021-06-07 16:01:03
【问题描述】:
我目前正在用 C# 为我们的 Exchange Online 租户编写一个联系人管理器,分别使用“System.Management.Automation”和“System.Management.Automation.Runspaces”中的 Powershell 命令和运行空间。将联系人添加到 GAL 时效果很好。但我一直在编辑联系人。
我需要使用 Powershell 命令获取联系方式。我可以执行的代码如下:
var command = new PSCommand();
command.AddCommand("Get-Contact");
command.AddParameter("Identity", "someContact");
但是:这当然只给了我联系人姓名。我需要扩展该命令。我需要执行的等效本机 Powershell-Command 如下所示:
Get-Contact -Identity "someContact" | Format-List
当我尝试从上面以某种方式将该“格式列表”添加到命令方案中时 - 例如像这样:
var command = new PSCommand();
command.AddCommand("Get-Contact");
command.AddParameter("Identity", "someContact");
command.AddCommand("Format-List");
我得到一个异常,告诉我“格式列表”不是 Cmdlet 的名称或任何东西......我还尝试使用 AddParameter 甚至 AddArgument 添加它 - 这些都不起作用,我总是以错误结束.
使用 Google,我在 Stackoverflow 上找到了线程,人们通过“AddScript()”命令传递了一个脚本。但是当我做这样的事情时:
AddScript("Get-Contact -Identity 'someContact' | Format-List");
它告诉我,由于 Remote-Powershell 正在无语言模式下运行,因此无法识别语法。我不知道,如果可能的话,如何更改该语言模式。
以下是我用来在我们的 Exchange Online 租户上执行 Remote-Powershell-Commands 的完整代码:
// sPass Variable in SecureString umwandeln (Passwort muss ein SecureString
// sein, sonst wird es von WSManConnectionInfo nicht akzeptiert!)
SecureString ssPass = new NetworkCredential("", sPass).SecurePassword;
// Exchange Online Credentials vorbereiten
PSCredential credential = new PSCredential(sUserAndMail, ssPass);
// Connection zu Exchange Online mit der URL vorbereiten und Authentication Mode auf Basic setzen
WSManConnectionInfo wsEOConnInfo = new WSManConnectionInfo(new Uri(sURI), sSchema, credential);
wsEOConnInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
wsEOConnInfo.IdleTimeout = 60000;
// Runspace erstellen, in dem die Powershell-Befehle ausgeführt werden
using (Runspace runspace = RunspaceFactory.CreateRunspace(wsEOConnInfo))
{
// Connection herstellen
runspace.Open();
// Prüfen, ob Connection existiert. Wenn ja, Commands ausführen
if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)
{
PowerShell ps = PowerShell.Create();
// Zunächst die ExecutionPolicy auf RemoteSigned für den aktuellen Benutzer setzen.
// Andernfalls stehen die MailContact-Befehle der Remote-Powershell nicht zur Verfügung.
ps.AddCommand("Set-ExecutionPolicy").AddParameter("ExecutionPolicy", "RemoteSigned").AddParameter("Scope", "CurrentUser");
ps.Invoke();
// Nun den Befehl auf Basis des Use-Cases zusammenstellen
switch (SearchCaseValue)
{
case 1:
{
// Hier den Befehl zum Suchen des Kontakts auf Basis des Namens
var command = new PSCommand();
command.AddCommand("Get-Contact");
command.AddParameter("Identity", "*" + tb_SearchTerm.Text + "*");
// Kommando zusammensetzen und die Ausführung in diesem Runspace festlegen
ps.Commands = command;
ps.Runspace = runspace;
// Kommando ausführen
try
{
// Den Output des Invokes einer Collection zum Zugriff auf die Inhalte zuweisen
Collection<PSObject> psOutput = ps.Invoke();
// Einen neuen StringBuilder instantiieren
var sb = new System.Text.StringBuilder();
// Wenn der Inhalt der Collection nicht leer ist, dann jede Zeile des Powershell-Ouputs
// aus der Collection in neue Zeilen des StringBuilders schreiben
foreach (PSObject outputItem in psOutput)
{
lb_SearchResults.Items.Add(outputItem.ToString());
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
// Hintergrundfarbe der Listbox ändern, da nun Ergebnisse darin angezeigt werden
// und den Button zum Reset der Ergebnisse aktivieren
lb_SearchResults.BackColor = Color.White;
panel_SearchInProgress.Visible = false;
bt_ResetSearchResults.Enabled = true;
// Runspace Schließen
runspace.Close();
break;
}
case 2:
{
// Hier den Befehl zum Suchen des Kontakts auf Basis der eMail-Adresse erstellen
var command = new PSCommand();
command.AddCommand("Get-Contact");
command.AddParameter("Filter", "((WindowsEmailAddress -like '*" + tb_SearchTerm.Text + "*'))");
// Kommando zusammensetzen und die Ausführung in diesem Runspace festlegen
ps.Commands = command;
ps.Runspace = runspace;
// Kommando ausführen
try
{
// Den Output des Invokes einer Collection zum Zugriff auf die Inhalte zuweisen
Collection<PSObject> psOutput = ps.Invoke();
// Einen neuen StringBuilder instantiieren
var sb = new System.Text.StringBuilder();
// Wenn der Inhalt der Collection nicht leer ist, dann jede Zeile des Powershell-Ouputs
// aus der Collection in neue Zeilen des StringBuilders schreiben
foreach (PSObject outputItem in psOutput)
{
lb_SearchResults.Items.Add(outputItem.ToString());
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
// Hintergrundfarbe der Listbox ändern, da nun Ergebnisse darin angezeigt werden
// und den Button zum Reset der Ergebnisse aktivieren
lb_SearchResults.BackColor = Color.White;
panel_SearchInProgress.Visible = false;
bt_ResetSearchResults.Enabled = true;
// Runspace Schließen
runspace.Close();
break;
}
}
}
// Runspace schließen, falls nicht bereits geschehen. Wichtig, da in Exchange Online
// nur maximal 3 Runspaces (Connections) gleichzeitig offen sein dürfen!
runspace.Dispose();
}
我希望您发现此摘录有助于解决问题。对不起那里的德国评论。我需要知道我在做什么,你知道吗?! :-)
那么...你能告诉我如何在不使用脚本的情况下将“格式列表”传递给远程 Powershell 吗?
非常感谢您提前提供的帮助! 史蒂芬
【问题讨论】:
-
Get-Contactcmdlet 将在 Microsoft 的服务器上运行 - 而 Microsoft 显然不希望你只能运行 任何东西 ,因此它们只允许您执行一组限制性命令,而不能运行任意代码块(因此是“NoLanguage”模式)。如果您想进一步处理/格式化它,您必须将远程运行空间的输出反馈到本地运行空间
标签: c# powershell runspace