Exchange PowerShell

PowerShell ist nicht nur lokal eine immense Verbesserung für Administratoren sondern enthält auch leistungsfähige Möglichkeiten zur Fernausführung. Quasi das, was früher per PSEXE oder RCMD genutzt wurde ist mit WinRM und PowerShell nun auch Remote möglich. Aber oft sind die Tücken im Detail und diese Seite soll zeigen, wie eine Verbindung von einem Client mit PowerShell zum Exchange Management System erfolgen kann und welche Fallstricke dabei zu umgehen sind.

WinRM ist quasi der Nachfolger von COM, DCOM und anderen Wege, aus der Ferne ein System zu verwalten. Auch die PowerShell nutzt "WinRM" als Transport, wenn man ihr nichts anderes vorschreibt. Details zu WinRM finden Sie auf WinRM.

eBook Update: Layman's guide to PowerShell 2.0 remoting
http://www.ravichaganti.com/blog/?p=1780
Sehr lesenswertes eBook zu PowerShell und remote

Programmatic Access via Remote PowerShell in Exchange Server 2010
http://blogs.technet.com/b/exchange/archive/2009/11/02/3408653.aspx

Hinweis: Wenn man Exchange virtuell betreibt, darf die Systemzeit nicht vom Host kommen sondern über die Windows Zeitdienste. Ansonsten kann sich die PowerShell nicht verbinden und sie bekommen den Fehler:
"WinRM cannot process the request because the input XML contains a syntax error"

.NET Abhängigkeiten

Exchange erweitert die lokal installierte Powershell um zusätzliche Module. Dabei gibt es zwangsläufig Abhängigkeiten von der der Exchange Version, der Powershell-Version und der NET-Version. Ich kann nicht alle einzelnen Versionen immer auf Kompatibilität und Supportstatement überprüfen. Sie sind auf der sicheren Seite, wenn Sie die Version einsetzen, die zum Zeitpunkt des RTM des Servers oder Service Packs aktuell war. Es ist möglich, verschiedene Versionen der Powershell nebeneinander zu installieren. Beim Aufruf muss dann nur die eventuell gewünschte ältere Version als Parameter übergeben werden. Exchange macht das sogar, allerdings kann es gerade wieder andere Einstellungen geben, die diese manuelle Auswahl außer Kraft setzen. Dies ist insbesondere dann knifflig, wen eine andere Software auf dem Server eben eine neuere Powershell voraussetzt oder ein Windows Update unbemerkt eine neue "Windows Management Framework" installiert. Normalerweise sollte auf einem Exchange Server natürlich keine andere Software installiert werden. Wenn aber das SAN-Management oder die Überwachungslösung die PowerShell nutzt, kann dieser Konflikt durchaus akut werden.

Exchange "Local PowerShell"

Achtung: Dieser Betrieb ist ein Sonderfall, der von Exchange beim Setup selbst genutzt wird.
Sie sollten über den Weg nur dann den lokalen Server verwalten, wenn die Verwaltung über die normale Remote PowerShell nicht möglich ist.

Die normale Verwaltung von Exchange erfolgt über die Exchange Remote PowerShell, bei der Exchange sich mit dem virtuellen Verzeichnis "/Powershell" des Exchange Frontend/CAS-Servers verbindet. Dieser Weg kann aber auch einmal verbaut sein, z.B. wenn der IIS nicht läuft oder das "/PowerShell"-Verzeichnis nicht da ist. Das Exchange Setup nutzt ebenfalls die lokale Powershell zum Exchange erst einmal einzurichten. Es ist also durchaus interessant dies zu wissen. Allerdings gibt es Einschränkungen:

  • Single Server Betrieb
    Ich habe mich bei der Nutzung immer nur darauf beschränkt, den lokalen Server zu verwalten. Alles andere kann gehen, muss aber nicht gehen
  • Direkte Rechte, Kein RBAC
    Die Änderungen .z.B. an AD-Objekten oder IIS-Metabase erfolgen direkt. Der aufrufende Anwender muss die Rechte haben. Es kommt nicht die RBAC-Delegierung als "ExchangeServer$" zum Einsatz.

Da diese Weg schon seit Exchange 2010 möglich ist und damals noch keine PowerShell-Module existieren, ist diese Basisfunktion noch als PSSnapIn implementiert. Neben einigen anderen Snapins finde ich selbst auf einem Exchange 2016 Server hier vier Module:

#Anzeige der registrierten PSSnapins
get-pssnapin -registered


Name : Microsoft.Exchange.Management.PowerShell.E2010
PSVersion : 1.0
Description : Admin Tasks for the Exchange Server

Name : Microsoft.Exchange.Management.PowerShell.Setup
PSVersion : 1.0
Description : Setup Tasks for the Exchange Server

Name : Microsoft.Exchange.Management.PowerShell.SnapIn
PSVersion : 1.0
Description : Admin Tasks for the Exchange Server

Name : Microsoft.Exchange.Management.Powershell.Support
PSVersion : 1.0
Description : Support Tasks for the Exchange Server

# Snapin registrieren
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010

Ich nutzen den Weg wirklich nur in Ausnahmefällen. In Office 365 ist er nicht mehr möglich.

Exchange Remote PowerShell

Universeller ist der normale Zugang per Remote Powershell (Siehe auch PS Remote). Sie müssen dazu lokal nichts installieren, außer eine PowerShell 2.0 und brauchen auch keine Updates. Dafür das Herstellen der Verbindung etwas aufwändiger und die Objekte wurden einmal durch die Pipeline serialisiert. Die Properties eines Objekts können teilweise nur "Strings" sein und keine Exchange Objekte.

# Anmeldung mit aktuellen Benutzer im gleichen Forest
$session = new-pssession `
   -ConfigurationName "Microsoft.Exchange" `
   -ConnectionUri http://<casservername>/PowerShell/ `
   -Authentication Kerberos

# oder ueber explizite Angabe z.B. fuer Office 365
$session = New-PSSession `
    -ConfigurationName Microsoft.Exchange `
`   -ConnectionUri https://ps.outlook.com/PowerShell/ `
    -Credential $Cred `
    -Authentication Basic `
    -AllowRedirection 

# Session einbinden
import-pssession -Session $session -AllowClobber | out-null

Ich bevorzuge die Remote PowerShell per HTTPS, da ich dann auf dem ausführenden PC selbst gar keine Exchange Software erforderlich ist und damit diese auch nicht aktualisiert werden muss.

Sie müssen dazu auch lokal kein Administrator sein und der lokale WinRM-Dienst muss auch nicht laufen.

In der Cloud (Office 365) wird nur mit der Remote PowerShell gearbeitet wird.

Modul mit "Endpunkt-Erkennung"

Wenn Sie ihre Skripte eh im LAN ausführen, dann können Sie mit etwas mehr Code drum herum sogar eine automatische Verbindung zum ersten Exchange Server aufbauen lassen. Es beruht auf einer LDAP-Anfrage, über die ich die PowerShell-VirtualDirectories in der Konfiguration-Partition ermitteln kann. Die sind als LDAP-Objekte z.B. hier abgelegt:

CN=Configuration,DC=msxfaq,DC=net
  CN=Services
    CN=Microsoft Exchange 
      CN=Orgname
        CN=Administrative Groups
          CN=Exchange Administrative Group (FYDIBOHF23SPDLT)
            CN=Server
              CN=SERVERNAME
              CN=Protocols
                 CN=HTTP
                    CN=PowerShell (Default Web Site)

Das Objekte hat mehrere Properties von denen mit zwei genauer interessieren:

  • msExchInternalHostName
    Enthält auf dem Frontend-Server die hinterlegte URL, z.B. "http://SERVERNAM.msxfaq.net/powershell". Auf dem Backend Server ist das Feld leer
  • msExchInternalAuthenticationMethods
    Diese Feld enthält die unterstützten Anmeldeverfahren. Diese Werte ich aber nicht weiter aus. Bislang habe ich folgende Inhalte gefunden.
$null none oder Basic 
18 (NTLM/Integrated)
19 (Basic, NTLM, Integrated)
2 Basic
1 None

Entsprechend suche ich zuerst die RootDSE um so zum Configuration Context zu kommen und dann die eigentliche Suche zu starten.

param (
   [string]$exchangeuri=""
)

$error.Clear()
if (get-pssession | where-object {$_.State -eq "Opened" -and $_.Runspace.ConnectionInfo.ConnectionUri -eq $exchangeuri}) {
   Write-Host "Using existing Exchange On-Premises Connection"
   $ExOnPremsession = $null
}
else {
    Write-Host "Start new Connection to Exchange On-Premises"
    if ([string]::IsNullOrEmpty($exchangeuri)) {
        Write-host "Retrieving Exchange URL from Active Directory"
        $RootDSE = [adsi]"LDAP://rootDSE"
        $exchangeuri = Get-ADObject `
                            -LDAPFilter "(&(objectclass=msExchPowerShellVirtualDirectory)(msExchInternalHostName=*))" `
                            -SearchBase $RootDSE.configurationNamingContext[0] `
                            -properties msExchInternalHostName `
                        | Select-Object -ExpandProperty msExchInternalHostName -First 1
    }
    
    if ([string]::IsNullOrEmpty($exchangeuri)) {
        Write-Host "No valid Exchange URI found. Check AD or parameters" -ForegroundColor red
        exit
    }
    Write-Host " Connecting to Exchange On-Premises at $($exchangeuri)"
    $ExOnPremSession = New-PSSession `
        -ConfigurationName "Microsoft.Exchange" `
        -ConnectionUri $exchangeuri 
    Write-Host "Import Exchange Remote Session Commandlets"
    import-pssession -Session $ExOnPremSession -AllowClobber | out-null
    set-adserversettings -viewentireforest $true
}
if ($error) {
   Write-Host "Exitcode:8 Unable to load Exchange Snapins"
   exit 8
}

Der Code enthält keine "Failover-Intelligenz", wenn eine Exchange Server nicht erreichbar ist. Wer mehrere Exchange Server mit HA-Anforderungen hat, sollte vielleicht über einen Loadbalancer nachdenken, damit Dienste zuverlässig einen Endpunkt finden.

Authentifizierung, Loadbalancer, Kerberos

Oft fällt es gar nicht auf, denn Administratoren starten eine Exchange PowerShell meist auf dem Exchange Server selbst. Und diese verbindet sich per Default mit dem lokalen Server. Per Default kommt dabei "Negotiate" zum Einsatz und solange die Verbindung auch direkt zum Servernamen erfolgt, funktioniert natürlich auch Kerberos. Eine explizite Anmeldung per NTLM oder sogar Basic ist per Default nicht möglich.

Allerdings betreiben größere Firmen natürlich mehrere CAS-Server und verteilen die Last per Loadbalancer unter Nutzung eines virtuellen Namens, z.B.: CAS.<firmendomain> oder "owa.<firmendomain>". Für diesen Namen gibt es natürlich per Default erst mal keine Kerberos-Anmeldung. Hier muss ein Administrator erst aktiv werden und Kerberos für CAS (Siehe E2010 CAS und Kerberos) erst aktivieren. Allerdings ist das Ergebnis hinsichtlich der PowerShell nicht optimal. Ich habe bei einem Exchange 2016 CU20 eine Testreihe gestartet und mich mit einem Client über unterschiedliche Authentifizierungsverfahren (Default, Negotiate, Kerberos), Verschlüsselung (HTTP und HTTPS) und URLs angemeldet. Nicht jede Anmeldung war erfolgreich. Hier meine Tabelle:

Die Ergebnisse sind suspekt

  • http geht nur mit Kerberos
    Wenn ich direkt auf die beiden Exchange Server EX11 und EX12 eine Verbindung genutzt habe, dann konnte ich per HTTPS mit "Default" und "Kerberos" anmelden. Eine explizite Anmeldung per "Negotiate" wurde aber unterbunden, denn die Server waren nicht in der "TrustedHost"-List. New-PSSession hat sich geweigert, vielleicht schwache NTLM-Anmeldungen auch nur zu starten.
  • HTTPS erlaubt auch "Negotiate" aber kein Kerberos
    Wenn die Verbindung verschlüsselt ist, dann kann ich mich per Negotiate anmelden aber interessanterweise nicht per Kerberos. Ursache noch unbekannt
  • Loadbalancer ohne HTTP
    Wenn ich auf den virtuellen Namen gehe, dann hatte ich in meinem HLB den Port 80 nicht geöffnet. Insofern sind die Werte hier nicht aussagekräftig
  • Loadbalancer mit HTTPS
    Aber per HTTPS konnte ich zugreifen. Her aber dann nur per "Negotiate" aber ebenfalls nicht per Kerberos

Ich muss die genauen Fälle bei Gelegenheit noch mal genauer untersuchen aber ich bin sicher, dass meine beiden Exchange 2016 Server im Labor absolut "Standard" sind aber natürlich ein ASA-Konto für Kerberos nutzen.

Vielleicht ist das der Grund, warum die Exchange PowerShell selbst erst aufwändig nach den ADSites und den Exchange Servern in den Sites sucht um sich dann zu einem Server über den FQDN zu verbinden.

Die Nutzung von Kerberos und Negotiate ist im internen LAN möglich aber aus dem Internet nicht immer einfach. Der Client hat vielleicht keine Verbindung zum KDC und NTLM geht nicht immer über Proxy-Server. Sie können in dem Zuge natürlich auch "Basic" aktivieren. Dann natürlich zwingend über HTTPS, damit das Kennwort nicht so einfach abgefischt werden kann. Schon das ist bei mir Grund genug, den Loadbalancer gar nicht auf Port 80 lauschen zu lassen, damit auf jeden Fall HTTPS zum Einsatz kommt. BasicAuth funktioniert in der Regel auch problemlos über Proxy-Server und aus dem Internet,

Kerberos

Mir das die "Nicht Funktion" von New-PSSession zu einem Exchange Server mit HTTPS und Kerberos keine Ruhe gelassen. Also habe ich einmal mittels Fiddler die HTTPS-Verbindung mit geschnitten und folgendes gesehen:

Der erste Request an den Exchange Server wird noch blind mit einem Kerberos-Ticket als "Authorization" versehen und mit einem 200 OK von Exchange quittiert. Aber der dann folgende Request mit der eigentlichen XML-Payload kommt als "Content-Type: application/soap-xml;charset=UTF" aber ohne Authorization-Header mit. Damit ist klar, dass Exchange oder genauer WinRM diese Anforderung mit einem 401 Access Denied ablehnt. Ich weiß bislang nicht, warum PSSession beim zweiten Request keine Authentifizierung mitsendet.

Die Verbindung per HTTP kann ich mir aber z.B.: im Wireshark direkt anschauen..

Hier geht der erste Request auch mit einem Kerberos-Ticket zur Gegenseite aber der zweite Request hat einen interessanten "Content-Type: application/http-kerberos-sessionencrypted". Die PSSession verschlüsselt die Payload mit dem Kerberos ticket und Exchange kann diese Payload decodieren.

Obwohl das eigentliche Übertragungsprotokoll hier unverschlüsseltes HTTP ist, werden die Daten durch die Applikation verschlüsselt und authentifiziert. Die Funktionen des IIS hinsichtlich HTTPS und Authentication kommen gar nicht zum Einsatz.

Das Verhalten finde ich natürlich schade, dass Exchange über HTTPS keine Anmeldung per Kerberos erlaubt und die Meldung sogar irreführend ist. Wenn ich eine Lösung finde, dann werde ich die Seite hier aktualisieren.

Loadbalancer, VirtualDirectory und External/InternalURL

Eine weitere Falle gibt es aber beim Einsatz eines Loadbalancer noch. Wenn Sie nun mit der Authentifizierung alles gelöst haben und sie von Clients nun endlich über den virtuellen Namen ihres CAS-Verbunds auf die Powershell zugreifen können, dann könnte es dennoch nicht möglich sein, von einem Exchange Server selbst über diesen Namen eine Remote Powershell zu starten. Das Problem hier ist wieder der Loadbalancer, der eigentlich die Probleme reduzieren sollte. Das Stichwort hier lautet Transparent bzw. Source-NAT. Wenn in interner Exchange Server quasi "von hinten" auf den Loadbalancer zugreift, dann können viele Layer4 Loadbalancer das Paket nicht wieder nach intern weiter geben. Selbst wenn das geht, dann stellt sich die Frage, mit welcher Source-IP die Anfrage dann beim anderen Exchange Server landet. Es sollte der Loadbalancer sein, damit die Antwort auch wieder über den Loadbalancer zurück geht. Allerdings verbauen Sie sich damit jede Form der Auswertung über die reale Client-IP.

Wenn die Anfrage aber von der originalen Client-IP kommt, dann wird der Ziel-Exchange Server die Antwort direkt zum benachbarten Exchange Server zurück senden. Der TCP-Handshake kommt damit gar nicht erst zustande. Auf Exchange Servern selbst sollte man daher besser direkt auf die Servernamen zugreifen.

Übrigens ist es kein Problem, die InternalURL auf dem FQDN des Server zu belassen, wenn die Server auch direkt von alle Clients, die Powershell nutzen, erreichbar sind. Sie können dennoch auch über den virtuellen Namen auf den Server zugreifen.

IIS-Verzeichnis "/PowerShell"

Durch die Installation von Exchange 2010 tut sich aber noch ein neuer Weg auf. In der Default Webseite des mit installierten IIS landen neben den virtuellen Verzeichnissen für die CAS-Rolle auch ein Verzeichnis "/PowerShell", welches von Anwendern so gar nicht angesprochen wird. Dieses Verzeichnis und komplett getrennt von den Exchange Web Services (EWS) zu sehen, welche primär einen Zugriff in die Daten von Exchange erlauben.

Wie der IIS-Manager offenbart, gehört "/PowerShell" zu Exchange und nicht zu WinRM oder der PowerShell als solches. Entsprechend funktionieren die folgenden Beschreibungen nur, wenn der Server ein Exchange 2010 Server ist. Dass Exchange dieses virtuelle Verzeichnis selbst nutzt, können Sie einfach im IISLog kontrollieren. Dort sehen Sie beim Aufruf der Exchange PowerShell entsprechende Treffer. (Logdatei gekürzt und Zeilenumbruch addiert)

#Software: Microsoft Internet Information Services 7.5
#Version: 1.0
#Date: 2011-04-07 00:00:19
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-Username c-ip cs(User-Agent) sc-status sc-substatus sc-win32-status time-taken
2011-04-07 08:09:43 ip POST /PowerShell PSVersion=2.0 80 - ip Microsoft+WinRM+Client 200 0 0 0
2011-04-07 08:09:43 ip POST /PowerShell PSVersion=2.0 80 MSXFAQ\adm-scink ip Microsoft+WinRM+Client 200 0 0 46
2011-04-07 08:09:44 ip POST /PowerShell serializationLevel=Full;clientApplication=EMC; ExchClientVer=14.1.218.15;PSVersion=2.0 80 - ip Microsoft+WinRM+Client 200 0 0 0
2011-04-07 08:09:44 ip POST /PowerShell serializationLevel=Full;clientApplication=EMC; ExchClientVer=14.1.218.15;PSVersion=2.0 80 MSXFAQ\adm ip Microsoft+WinRM+Client 200 0 0 15
2011-04-07 08:09:44 ip POST /PowerShell serializationLevel=Full;clientApplication=EMC; ExchClientVer=14.1.218.15;PSVersion=2.0 80 MSXFAQ\adm ip Microsoft+WinRM+Client 200 0 0 499

Sie können auch sehen, dass die Exchange PowerShell bzw. genauer das Exchange PowerShell PSSnapin diesen Weg beschreitet.

Auch diese erste "Verbindung" wird im IIS schon mit geloggt.

Externer Zugriff

Interessant wird die Funktion nun, wenn der Exchange Server per HTTPS von "überall" erreichbar ist und mit entsprechenden Credentials können Sie also einfach eine "PowerShell" nutzen, ohne auf dem PC irgendwelche Zusatzprogramme installiert zu haben, d.h. ein Windows XP PC mit PowerShell 2.0 reicht aus, um zumindest per Exchange Commandlets dann Exchange zu verwalten. Die GUI haben Sie dabei Natürlich nichts installiert.

Dieser Weg ist aber auch interessant, wenn andere Programme, z.B. Provisioning-Systeme wie MIIS,ILM, FIM, DirX, DirXML, SunIDM und andere aus der Ferne die Exchange Konfiguration verändern wollen, aber auf dem Server selbst keine Exchange PowerShell installiert werden soll bzw. kann.

Damit das ganze aber funktioniert, muss erst mal die Berechtigung auf dem virtuellen Verzeichnis aktiviert werden. Standardmäßig ist nämlich keine Authentifizierung aktiviert.

Das ist so aber nicht ganz richtig, da der IIS schon eine "Native Authentication" nutzt, die Sie unter den "Modulen" finden:

Insofern bedeutet, dass alle Authentifizierungsmodule auf "disabled" stehen, dass der IIS selbst keine Authentifizierung vornimmt. Aber Das Modul greift hie schon vorher ein und kann unabhängig von einer nicht weiter erforderlichen nachgeschalteten IIS-Authentifizierung arbeiten. Das funktioniert aber nur innerhalb des gleichen Forest mit Benutzern im Forest, da diese Kerberos-Funktion nicht über Trusts oder für explizite Anmeldedaten geeignet ist.

Für eine Anmeldung unter Angabe von Benutzername und Kennwort müssen die anderen Authentifizierungsverfahren aktiviert werden. Sobald Sie die "Windows Authentication" aktiviert haben, können Sie sich schon per Kerberos oder NTLM anmelden. Das kann können Sie zwar über die IIS Verwaltung machen, aber der korrekte Weg ist die Exchange PowerShell. Schließlich ist auch dies ein Exchange virtuelles Verzeichnis und sie können nie sicher sein, dass diese Einstellung nicht dort irgendwann von Exchange aus einem anderen Datenbestand repliziert wird. 

Get-PowerShellVirtualDirectory | Set-PowerShellVirtualDirectory -WindowsAuthentication $true

Letztlich können Sie im IIS die Änderung der PowerShell genauso kontrollieren.

Wenn Sie aber auch die "Basic Authentication" aktivieren wollen, dann sollten Sie unbedingt den Zugriff per SSL erzwingen, damit die Anmeldedaten ausreichend geschützt sind.

Rechte für Benutzer

In all meinen Exchange 2010 Umgebungen waren alle Benutzer auch für den Zugriff per Remote PowerShell aktiviert. Natürlich können Sie nur die Funktionen ausführen, die ihnen über ihre Rollen (Siehe RBAC) zugänglich sind. Aber falls der Zugriff nicht möglich sein sollte, dann prüfen Sie die aktuelle Einstellung einfach mal mit einem:

[PS] C:\>Get-User | ft name, *power*

Name                                                                     RemotePowerShellEnabled
----                                                                     -----------------------
Administrator                                                                               True
Guest                                                                                       True
krbtgt                                                                                      True
DiscoverySearchMailbox {D919BA05-46A6-415f-80AD-7E09334B...                                 True
mailUser                                                                                    True
mailboxUser                                                                                 True
Archiv User                                                                                 True
Bad User                                                                                    True
Sales1                                                                                      True
User1                                                                                       True
User2                                                                                       True
W2003$                                                                                      True

Sollte die Funktion wider Erwarten nicht aktiv sein, dann ist sie schnell gesetzt.

Set-User Username -RemotePowerShellEnabled $True

Weitere Links