Zum Inhalt springen

C# PowerShell Output lesen


Tician

Empfohlene Beiträge

Huhu,

ich versuch verzweifelt in C# ein Programm zu basteln mit dem man Office 365 Benutzer verwalten kann. Über PowerShell selbst funktioniert das einwandfrei, aber in C# krieg ich einfach keine Rückmeldungen, nichts was ich auslesen könnte das mir sagt was falsch läuft.

Code sieht quasi so aus:

                PSCredential cred = new PSCredential(mail,password);

                using (PowerShell ps = PowerShell.Create())
                {
                    ps.Commands.AddScript("$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $cred -Authentication Basic -AllowRedirection");
                    ps.Commands.AddScript("Import-PSSession $Session -DisableNameChecking");
                    ps.Commands.AddScript("Remove-PSSession $Session");

                    Collection<PSObject> psOutput = ps.Invoke();

                    ps.Streams.Progress.DataAdded += Progress_DataAdded;
                    /*if (ps.Streams.Error.Count > 0)
                    {

                    }*/

                    foreach(PSObject outputItem in psOutput)
                    {
                        if (outputItem != null)
                        {
                            //richTextBox1.AppendText(outputItem.BaseObject.GetType().FullName);
                            richTextBox1.AppendText(outputItem.ToString());
                        }
                    }
                }

Was genau passiert:

- Programm läuft durch ohne Fehler - aber auch ohne irgendwelche Meldungen. Kein Output, keine Errors, nichts, die foreach-Schleife wird übersprungen weil nichts drin ist. Auch das versuchte Event (egal ob Streams.Warning, 'Progress' oder 'Information' triggert nicht.

 

Was mir fehlt:

- Der Output des PS-Scripts fehlt mir wie gesagt, aber ich tu mich verdammt schwer irgendwas nützliches im Internet zu finden

- Das Objekt der Klasse PSCredential mit dem ich in C# die Benutzerdaten abfrage muss irgendwie in den Befehl als PS-Variable rein, auch hier konnte ich noch nicht raus finden wie ich das schreiben muss damit es funktioniert.

 

Könnte mir da jemand helfen?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich oute mich einfach mal als Powershell-Noob, aber fehlt hier nicht "ps.AddParameter(...)"? (siehe z.B. https://blogs.msdn.microsoft.com/kebab/2014/04/28/executing-powershell-scripts-from-c/ ):

    using (PowerShell PowerShellInstance = PowerShell.Create())
    {
        // use "AddScript" to add the contents of a script file to the end of the execution pipeline.
        // use "AddCommand" to add individual commands/cmdlets to the end of the execution pipeline.
        PowerShellInstance.AddScript("param($param1) $d = get-date; $s = 'test string value'; " +
                "$d; $s; $param1; get-service");

        // use "AddParameter" to add a single parameter to the last command/script on the pipeline.
        PowerShellInstance.AddParameter("param1", "parameter 1 value!");
    }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich bin auch ein Powershell-Noob, aber ich hab denke ich verstanden was du meinst. Der neue Code sieht so aus:

 

PSCredential cred = new PSCredential(mail,password);

                using (PowerShell ps = PowerShell.Create())
                {
                    //ps.Commands.AddScript("$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $cred -Authentication Basic -AllowRedirection");
                    ps.AddScript("$Session = New-PSSession")
                        .AddParameter("ConfigurationName", "Microsoft.Exchange")
                        .AddParameter("ConnectionUri", "https://outlook.office365.com/powershell-liveid/")
                        .AddParameter("Credential", cred)
                        .AddParameter("Authentication", "Basic")
                        .AddParameter("AllowRedirection");


                    ps.AddCommand("Import-PSSession $Session")/*.AddParameter("$Session")*/.AddParameter("DisableNameChecking");
                    ps.AddCommand("Remove-PSSession").AddParameter("$Session");

                    ps.Streams.Progress.DataAdded += Progress_DataAdded;
                    ps.Streams.Error.DataAdded += Error_DataAdded;
                    ps.Streams.Information.DataAdded += Information_DataAdded;
                    ps.Streams.Warning.DataAdded += Warning_DataAdded;

                    Collection<PSObject> psOutput = ps.Invoke();
                    
                    /*if (ps.Streams.Error.Count > 0)
                    {

                    }*/

                    foreach(PSObject outputItem in psOutput)
                    {
                        if (outputItem != null)
                        {
                            //richTextBox1.AppendText(outputItem.BaseObject.GetType().FullName);
                            richTextBox1.AppendText(outputItem.ToString());
                        }
                    }
                }

Mein Problem:

Bei den letzten 2 Commands krieg ich entweder ein "Kennt kein cmdlet 'Import-PSSession $Session'" oder "Kennt kein Parameter '$Session"' - ich hab keine Ahnung mit was ich meine Variable da ran hängen muss, der Command an sich wäre einfach nur "Import-PSSession $Session".

Was ich auch nicht verstehe ist warum meine Ereignisse nicht ziehen, bzw ich immer noch keinen Output bekomme. Alles was passiert ist das das Programm immer in meinen großen Try/Catch geht und mir dort dann den Fehler ausgibt.

Noch Ideen? ?

 

PS: Nach einigen weiteren Beispielen ist 'AddScript' sowieso totaler Schwachsinn, aber mit 'AddCommand' funktioniert es ja auch nicht. Dachte ich... ich bin verwirrt, mal schreiben Leute direkt den Befehl rein, mal verweise sie nur auf eine Datei...

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

Mal ganz blöd gefragt - funktionieren denn die PS Commands

Zitat

ps.Commands.AddScript("$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $cred -Authentication Basic -AllowRedirection");
                    ps.Commands.AddScript("Import-PSSession $Session -DisableNameChecking");
                    ps.Commands.AddScript("Remove-PSSession $Session");

wenn du sie in der PowerShell ISE / Konsole eintippst und wenn ja - was genau bekommst du zurück?

Normalerweise brauchst du den .AddParameter nur, wenn du aus C# Argumete oder Parameter an die PowerShell übergibst.
Und da du im ersten Befehl schon versuchst $cred zu übergeben, musst du dies auch als Parameter dort anhängen.

Normalerweise solltest du dir das

New-PSSession -ConnectionUri servername -Credential $cred

mal mindestens ne Ausgabe erzeugen, mit der du was anfangen kannst.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Vielleicht kannst du auch mit dem CMDlet Invoke-Command was anfangen. Dort lassen sich auch Scriptblöcke remote ausführen und mit Variablen arbeiten

Beispiel:

Invoke-Command -Session $s -ScriptBlock { param ($computer) Get-ADComputer -Server "Servername.local" -Identity $computer -Properties * } -ArgumentList $computer

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

PowerShell direkt sieht so aus und funktioniert auch (Im Anhang). Ich erwarte nicht dass mir das so angezeigt wird, aber auch nicht... nichts. Ich glaub aber auch nicht dass sie Befehle momentan ausgeführt werden, der erste scheint durchzulaufen (ich weiß es nicht, ich rkieg ja keinen Output sondern nur einen Sprung in mein allgemeines Exception bei 'ps.Invoke()' mit einem der beiden oben beschriebenen Fehler.

 

Unbenannt.jpg

Unbenannt1.PNG

Link zu diesem Kommentar
Auf anderen Seiten teilen

Bei dem ps.Invoke() musst du in Klammern auf jeden Fall das cred übergeben. Also irgnedwie so in die Richtung:

var ps = PowerShell.Create();    //Instanz erstellen
script.AddScript("New-PSSession -ConnectionUri xxx -Credential cred");     //Script mit Cmdlets hinzufügen
var output = script.Invoke(cred);        //Befehl ausführen, ggf. eine Liste mit Eingabeparametern übergeben

So wie hier beschrieben:

https://dotnet-snippets.de/snippet/powershell-cmdlets-aus-net-aufrufen/4911

Link zu diesem Kommentar
Auf anderen Seiten teilen

OK, ich bin etwas weiter. Jetzt kriege ich Rückmeldung, kann sie aber nicht ausgeben weil mein Steuerelement (textBox) nicht im selben Thread wie mein PowerShell läuft.

 

                using (PowerShell ps = PowerShell.Create())
                {
                    ps.AddScript("$Session = New-PSSession")
                        .AddParameter("ConfigurationName", "Microsoft.Exchange")
                        .AddParameter("ConnectionUri", "https://outlook.office365.com/powershell-liveid/")
                        .AddParameter("Credential", cred)
                        .AddParameter("Authentication", "Basic")
                        .AddParameter("AllowRedirection");


                    ps.AddScript("Import-PSSession $Session").AddParameter("DisableNameChecking");
                    ps.AddScript("Remove-PSSession $Session");

                    ps.Streams.Progress.DataAdded += Progress_DataAdded;
                    ps.Streams.Error.DataAdded += Error_DataAdded; //rutscht hier rein
                    ps.Streams.Information.DataAdded += Information_DataAdded;
                    ps.Streams.Warning.DataAdded += Warning_DataAdded;

                    Collection<PSObject> psOutput = ps.Invoke();

                    foreach(PSObject outputItem in psOutput)
                    {
                        if (outputItem != null)
                        {
                            richTextBox1.AppendText(outputItem.ToString());
                        }
                    }
                }


        private void Error_DataAdded(object sender, DataAddedEventArgs e)
        {
            richTextBox1.AppendText(Convert.ToString(e)); //InvalidOperationException
        }

"Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement richTextBox1 erfolgte von einem anderen Thread, für den es erstellt wurde"
Ich verstehe warum ich das kriege, aber ich hab bisher kein solches Problem gehabt, mal schauen ob ich das irgendwie gelöst kriege... auch wenn ich keine Ahnung habe wie ich etwas zwischen Threads übergebe... oder so...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Die Anleitung ist irgendwie... unvollständig. Ich hab mir zwar ein wenig über Delegaten und auch Threads durchgelesen, aber ich fürchte es reicht nicht aus um zu verstehen was bei mir eigentlich falsch läuft und warum ich auch im Debug-Modus nicht weiter komme - und irgendwas gewaltig falsch mache und dabei die nächste Exception bekomme.

Folgendes hab ich jetzt (auf wesentliches gekürzt):

        private delegate void InvokeDelegate(string text);

                using (PowerShell ps = PowerShell.Create())
                {                    
                    ps.Streams.Error.DataAdded += Error_DataAdded;
                    ps.AddScript("...");

                    Collection<PSObject> psOutput = ps.Invoke(); //geht hier in das Ereignis
                }

        private void Error_DataAdded(object sender, DataAddedEventArgs e)
        {
            //richTextBox1.AppendText(Convert.ToString(e));
            richTextBox1.BeginInvoke(new InvokeDelegate(InvokeMethod)); //Macht 'nichts'
            //InvokeMethod(Convert.ToString(e));
            //InvokeDelegate(Convert.ToString(e));
        }
                      
        public void InvokeMethod(string text)
        {
            richTextBox1.AppendText("Bla ");
        }                     

Was passiert: Sichtbar quasi nichts, die TextBox bleibt leer, der Code läuft ne ganze Weile bis er dann in 'Program.cs' bei folgendem hängen bleibt:

Unbenannt.PNG.5136d01940afbd51ae32f073d18b9068.PNG

Die Dinge die ich nicht verstehe:

'ps.Invoke()' wird in einem anderen Thread ausgeführt, weswegen ich trotz Haltepunkt (das Delegaten-Zeug auskommentiert) direkt die Exception bekomme. Gibt es in VS eine Möglichkeit auch hier irgendwie einen Haltepunkt für andere Threads zu setzen?

Im Microsoft-Beispiel wird 'InvokeMethod()' direkt ausgeführt ohne vorher ein Objekt erstellt zu haben anhand dessen man die Methode aufrufen könnte. Ich hab alles versucht irgendwie noch meinen string-Parameter an den Delegaten ran zu packen, aber mir erschließt sich der Aufbau nicht. Ich hab hier nochmal ein Beispiel mit Parameter gefunden: https://www.codeproject.com/Articles/10311/What-s-up-with-BeginInvoke

richTextBox1.BeginInvoke(new InvokeDelegate(InvokeMethod),new object[] { e });

Ich schätze also es müsste so aussehen, aber trotzdem läuft irgendwas gewaltig schief, er versucht New Form1() mit dem Parameter aufzurufen, deswegen hier nun die Ausnahme dass Form1 keinen Parameter vom Typ string mag. Aber warum es überhaupt dorthin geht statt zu 'InvokeMethod()' ... keine Ahnung. ?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich verzweifel. Ich hab aber auch keine AHnung wie man Multi-Threading debuggt.

Auf jedenfall hab ich das hier und haufenweise Haltepunkte (weswegen ich zumindest weiß wo mein Programm sich verabschiedet:

                using (PowerShell ps = PowerShell.Create())
                {                    
                    ps.AddScript("Blabla");
                    ps.Streams.Error.DataAdded += Error_DataAdded;

                    Collection<PSObject> psOutput = ps.Invoke();                   
                }

        private void Error_DataAdded(object sender, DataAddedEventArgs e)
        {
            InvokeMethod(Convert.ToString(e));
        }

        public void InvokeMethod(string text)
        {
            if (InvokeRequired)
            {
                this.Invoke(new Action<string>(InvokeMethod), new object[] { text }); //hier passiert nichts mehr
                return;
            }
            richTextBox1.AppendText("Bla ");
        }

Meiner Meinung nach hab ich quasi genau das kopiert was auf stackoverflow stand und ich hab es auch in einem neuen Projekt ausprobiert, da funktioniert es, aber in meinem Projekt passiert ab 'this.Invoke(....)' gar nichts mehr. Ich hab es 5 Minuten laufen lassen bevor es mir zu blöd wurde. Die GUI ist zu dieser Zeit auch nicht ansprechbar (logisch, sie wartet eigentlich darauf dass mein ps.Invoke fertig ist - glaube ich). Ich seh im Debug-Modus das noch kurz der Balken für die Ereignisse 2-3 mal ausschlagen, dann ist tote Hose und mein Programm veravschiedet sich ins Nirvana... macht irgendwas und kommt nicht mehr zurück.

Was mach ich nur falsch?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Edit:

Mein Gedankengang war, dass ich keinen neuen Thread brauche, weil ps.Invoke ja schon einen neuen Thread erstellt. Jetzt hab ich trotzdem alles mal in einen neuen Thread verschoben und nun funktioniert es auch. Mein Error-Text stimmt zwar nicht, aber das sollte ich jetzt auch noch hinkriegen :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Exakt. Normalerweise benutz ich die EventArgs und die beinhalten auch meine Fehlermeldungen - was hier wohl nicht der Fall war, ich hatte nur geraten. Ich brauch irgendein Interface von meinem sender, hatte eben noch ein tolles Beispiel... und find es nicht mehr. Wie gesagt, ab hier sollte ich erstmal wieder alleine klar kommen :)

@KeeperOfCoffeeDanke! ❤️

Bearbeitet von Tician
Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...