Im currently writing a Contact Manager for our Exchange Online Tenant in C# using the Powershell-Commands and Runspaces from "System.Management.Automation" and "System.Management.Automation.Runspaces" respectively. Its working fine for adding Contacts to the GAL. But im stuck at editing Contacts.
I need to get the Contact Details with the Powershell Commands. The code i can execute looks like this:
var command = new PSCommand();
command.AddCommand("Get-Contact");
command.AddParameter("Identity", "someContact");
However: This gives me just the Contact-Name, of course. I would need to extend that Command. The equivalent native Powershell-Command i need to execute looks like this:
Get-Contact -Identity "someContact" | Format-List
When i try to somehow add that "Format-List" to the Command Scheme from above - for example like this:
var command = new PSCommand();
command.AddCommand("Get-Contact");
command.AddParameter("Identity", "someContact");
command.AddCommand("Format-List");
I get an Exception, telling me that "Format-List" is not the Name of a Cmdlet or anything ... I also tried adding it with AddParameter or even AddArgument - none of these work, i always end up with Errors.
Using Google i found threads here on Stackoverflow, where People passed a Script with the "AddScript()"-Command. But when i do something like this:
AddScript("Get-Contact -Identity 'someContact' | Format-List");
It tells me, that the Syntax is not recognized because that Remote-Powershell is running in No-Language Mode. I have no clue, how to change that Language Mode neither if it is even possible.
Below is the Full Code i use to execute Remote-Powershell-Commands on our Exchange Online Tenant:
// 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();
}
I hope you find this excerpt useful for nailing down the Problem. Sorry for the German Comments in there. I need to get track of what i do, you know?! :-)
So ... can you tell me how to pass "Format-List" to that Remote Powershell without using a Script?
Many thanks for your help in advance! Steffen
PSObject
returned by ps.Invoke()
contains all the properties of object. You can get values of all properties.
Just try this
ICollection<PSObject> psOutput = ps.Invoke();
foreach (PSObject outputItem in psOutput)
{
var name = outputItem.Members["Name"].Value.ToString();
var distinguishedName = outputItem.Members["DistinguishedName"].Value.ToString();
var displayName = outputItem.Members["DisplayName"].Value.ToString();
var lName = outputItem.Members["LastName"].Value.ToString();
}
Just enter the name of property you want to get and it will return you the value of that property.