2016-03-26 17 views
8

Sto provando a connettermi a PowerShell remoto dall'app C# .NET WinForms. Il mio obiettivo è creare la mia versione di Microsoft PowerShell ISE. Quindi ho bisogno di un modo per eseguire gli script PowerShell dalla mia app su macchine remote. Ho creato un paio di metodi e l'ho provato sulla macchina locale dalla mia app. Se io non uso WSManConnectionInfo e utilizzare usando (spazio di esecuzione remoteRunspace = RunspaceFactory.CreateRunspace()) posso eseguire script a livello locale come se fosse vero PowerShell (piccolo script, l'utilizzo di variabili, dati di output utilizzando ft, fl, faccio un sacco di altre cose che uso solitamente con powershell.Il problema inizia quando aggiungo WSManConnectionInfo e lo indirizza al mio Exchange Server invece di utilizzare la connessione locale. Sembra che sia in grado di eseguire elementi di base come "get-mailbox "ma non appena provo a collegare le cose, usa alcune funzionalità di scripting come $ variabili che si rompono dicendo che non è supportatoConnessione a Microsoft Exchange PowerShell in C#

Allo stesso modo devo disabilitare powershell.AddCommand ("out-string"); quando non lo si usa localmente.

Un'eccezione non gestita di tipo "System.Management.Automation.RemoteException" si è verificata in System.Management.Automation.dll.

Ulteriori informazioni: Il termine "Out-String" non è riconosciuto come nome di un cmdlet, funzione, file di script o programma eseguibile. Controlla l'ortografia del nome o se è stato incluso un percorso, verifica che il percorso sia corretto e riprova.

Lo stesso errore non viene visualizzato se non forzare la connessione remota, ma semplicemente farlo localmente. Sembra che lo SchemaUri stia rendendo molto rigido l'esecuzione solo dei comandi di base. Ho visto altri esempi in cui le persone in cui utilizzando le informazioni molto diretto come noi:

powershell.AddCommand("Get-Users"); 
powershell.AddParameter("ResultSize", count); 

Ma con questo approccio avrei dovuto definire un sacco di opzioni possibili e non vogliono passare attraverso definiscono i parametri e altre cose. Mi piacerebbe semplicemente caricare "script" ed eseguirlo proprio come nella finestra di PowerShell. Ecco un esempio di ciò che uso ora.

public static WSManConnectionInfo PowerShellConnectionInformation(string serverUrl, PSCredential psCredentials) 
    { 
     var connectionInfo = new WSManConnectionInfo(new Uri(serverUrl), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", psCredentials); 
     //var connectionInfo = new WSManConnectionInfo(new Uri(serverUrl), "http://schemas.microsoft.com/powershell", psCredentials); 
     connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic; 
     connectionInfo.SkipCACheck = true; 
     connectionInfo.SkipCNCheck = true; 
     connectionInfo.SkipRevocationCheck = true; 
     connectionInfo.MaximumConnectionRedirectionCount = 5; 
     connectionInfo.OperationTimeout = 150000; 
     return connectionInfo; 
    } 
    public static PSCredential SecurePassword(string login, string password) 
    { 
     SecureString ssLoginPassword = new SecureString(); 
     foreach (char x in password) { ssLoginPassword.AppendChar(x); } 
     return new PSCredential(login, ssLoginPassword); 
    } 
    public static string RunScriptPs(WSManConnectionInfo connectionInfo, string scriptText) 
    { 
     StringBuilder stringBuilder = new StringBuilder(); 
     // Create a remote runspace using the connection information. 
     //using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace()) 
     using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace(connectionInfo)) 
     { 
      // Establish the connection by calling the Open() method to open the runspace. 
      // The OpenTimeout value set previously will be applied while establishing 
      // the connection. Establishing a remote connection involves sending and 
      // receiving some data, so the OperationTimeout will also play a role in this process. 
      remoteRunspace.Open(); 
      // Create a PowerShell object to run commands in the remote runspace. 
      using (PowerShell powershell = PowerShell.Create()) 
      { 
       powershell.Runspace = remoteRunspace; 
       powershell.AddScript(scriptText); 
       //powershell.AddCommand("out-string"); 
       powershell.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output); 
       Collection<PSObject> results = powershell.Invoke(); 

       foreach (PSObject result in results) { 
         stringBuilder.AppendLine(result.ToString()); 
       } 

      } 
      // Close the connection. Call the Close() method to close the remote 
      // runspace. The Dispose() method (called by using primitive) will call 
      // the Close() method if it is not already called. 
      remoteRunspace.Close(); 
     } 

     // convert the script result into a single string 
     return stringBuilder.ToString(); 
    } 

Qualche consiglio sul perché questo sta accadendo e sul modo di aggirarlo come comportarsi allo stesso modo? Ho visto un sacco di blog come this ma definire ogni comando semplice non ha senso per me. Ho anche visto un'opzione per creare una connessione locale e quindi eseguire remote connection within that ma questa deve essere l'ultima risorsa poiché si basa su molti altri fattori.

+0

cosa fa il tuo 'serverUrl' assomigliare? –

+0

https: //mail1.domain.local/PowerShell di solito – MadBoy

risposta

7

Controllare https://blogs.msdn.microsoft.com/akashb/2010/03/25/how-to-migrating-exchange-2007-powershell-managed-code-to-work-with-exchange-2010/:

L'esperienza di gestione dato da Exchange 2010 attraverso PowerShell è stato spostato tutta la strada da locale a remoto. [...] Solo i cmdlet di scambio funzioneranno in questo scenario remoto, non sarà possibile eseguire la maggior parte dei cmdlet di PowerShell. [...] Sì, questo significa che non sarà possibile eseguire cmdlet come gli script Where-Object e .PS1 nel Remote Runspace.

È una limitazione? Io non la penso così Possiamo facilmente ottenere aggirarlo creando una nuova sessione e importandola da.


Così avrete bisogno di fare qualcosa di simile :

PSCredential creds = new PSCredential(userName, securePassword); 
System.Uri uri = new Uri("http://Exchange-Server/powershell?serializationLevel=Full"); 

Runspace runspace = RunspaceFactory.CreateRunspace(); 

PowerShell powershell = PowerShell.Create(); 
PSCommand command = new PSCommand(); 
command.AddCommand("New-PSSession"); 
command.AddParameter("ConfigurationName", "Microsoft.Exchange"); 
command.AddParameter("ConnectionUri", uri); 
command.AddParameter("Credential", creds); 
command.AddParameter("Authentication", "Default"); 
powershell.Commands = command; 
runspace.Open(); powershell.Runspace = runspace; 
Collection<PSSession> result = powershell.Invoke<PSSession>(); 

powershell = PowerShell.Create(); 
command = new PSCommand(); 
command.AddCommand("Set-Variable"); 
command.AddParameter("Name", "ra"); 
command.AddParameter("Value", result[0]); 
powershell.Commands = command; 
powershell.Runspace = runspace; 
powershell.Invoke(); 

powershell = PowerShell.Create(); 
command = new PSCommand(); 
command.AddScript("Import-PSSession -Session $ra"); 
powershell.Commands = command; 
powershell.Runspace = runspace; 
powershell.Invoke(); 

# now you can use remote PS like it's local one 
+0

Sembra legittimo. Lo proveremo domani :-) Grazie. Spero che questo sia il problema che sto avendo. – MadBoy

+0

Anche io spero che questo risolva il problema. Buona fortuna con quello. Per favore fatemi sapere –

+0

@MadBoy Ciao, come va? Forse dovrei copiare il codice qui in SO per i futuri investigatori, cosa ne pensi? –

0

Sembra che durante l'esecuzione di problemi di autenticazione a doppio hop, ho avuto gli stessi problemi quando si tenta di questo.

Dopo aver risolto i problemi, ho finito per installare localmente gli add-on di Power Powershell e li ho usati per collegarmi in remoto al server Exchange.

esempio ruvida:

RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create(); 
    PSSnapInInfo info = runspaceConfiguration.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapInException);          

    Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration); 
    runspace.Open(); 

    using (PowerShell powershell = PowerShell.Create()) 
    { 
     powershell.Runspace = runspace; 

     string script = string.Format(@"Get-Mailbox -Server {0} -Identity {1}", serverName, identity); 
     powershell.AddScript(script); 

     powershell.Invoke(); 

     // Do something with the output 

    } 
+0

Non è scoraggiato da Microsoft per utilizzare Exchange Addin in PowerShell (quindi mi aspetterei che lo stesso sia vero per C#?). E questo renderebbe anche necessario alla mia app avere gli strumenti di Exchange installati localmente, il che non è l'ideale. – MadBoy

+0

Non è l'ideale ma una delle poche soluzioni che funzionerà. Ho fatto questo alcuni anni fa e ha funzionato, ma se dovessi farlo oggi prenderei in considerazione la costruzione di un piccolo servizio REST o simile che è possibile eseguire sull'host di Exchange, ancora una volta non ideale e ha le sue limitazioni, ma vale la pena considerare. NOTA: ho pensato che tu avessi esaminato l'uso del sistema EWS e l'hai escluso, altrimenti dargli un'occhiata. – ServerMonkey

+0

Non voglio installare nulla su Exchange Server. Voglio collegarmi come faccio normalmente con ISE. Non penso che EWS abbia tutto ciò che devo fare. Voglio funzionalità standard di PowerShell. – MadBoy

2

A partire da Exchange Server 2010 è necessario utilizzare sessione PowerShell remota invece di aggiungere direttamente scambio PowerShell snap-in (a causa utilizzare RBAC anziché ACL nello scambio 2010). Quindi è necessario creare una nuova sessione di PowerShell (utilizzando New-PSSession) e quindi importarla (utilizzando Import-PSSession). È possibile utilizzare il codice come questo per eseguire i comandi PowerShell si remoto:

void ExecutePowerShellUsingRemotimg() 
{ 
    RunspaceConfiguration runspaceConfig = RunspaceConfiguration.Create(); 
    PSSnapInException snapInException = null; 

    Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfig); 
    runspace.Open(); 

    Pipeline pipeline = runspace.CreatePipeline(); 


    string serverFqdn = "FQDN of you server"; 
    pipeline.Commands.AddScript(string.Format("$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://{0}/PowerShell/ -Authentication Kerberos", serverFqdn)); 
    pipeline.Commands.AddScript("Import-PSSession $Session"); 
    pipeline.Commands.AddScript("your PowerShell script text"); 
    pipeline.Commands.Add("Out-String"); 
    Collection<PSObject> results = pipeline.Invoke(); 
    runspace.Close(); 

    StringBuilder sb = new StringBuilder(); 

    if (pipeline.Error != null && pipeline.Error.Count > 0) 
    { 
     // Read errors 
     succeeded = false; 
     Collection<object> errors = pipeline.Error.ReadToEnd(); 
     foreach (object error in errors) 
      sb.Append(error.ToString()); 
    } 
    else 
    { 
     // Read output 
     foreach (PSObject obj in results) 
      sb.Append(obj.ToString()); 
    } 

    runspace.Dispose(); 
    pipeline.Dispose(); 
} 
+0

Grazie. Ho assegnato il premio per correggere la prima risposta. Il tuo codice è valido e molto valido e sarà molto utile. – MadBoy