Ich arbeite viel mit Exchange Servern. Dabei benutze ich sehr gerne die PowerShell ISE statt der Exchange Management Shell – gerade auch wegen der Remoting-Funktionalität: Denn so muss ich nicht immer erst auf meinen Exchange Server mit RDP springen.
Für den Verbindungaufbau sind nur 2 Zeilen erforderlich:
$MX = New-PSSession -ConnectionUri 'http://ws-mx1/powershell' -Authentication Kerberos -ConfigurationName microsoft.exchange Import-PSSession -Session $MX -DisableNameChecking
Danach steht dann die Verbindung und vor allem die vielen Exchange-cmdlets zur Verfügung:

Das bietet sich auch in Scriptdateien an. Aber es gibt ein Problem: Der ConnectionURI muss einen existierenden und erreichbaren Exchange Server enthalten. Und das gibt 2 mögliche Probleme:
- Der Server wird in Scriptdateien statisch hinterlegt. Ersetzt ein neuer Exchange Server den alten, der im URI benannt wird, dann läuft das Script nicht mehr. Bei einer Vielzahl von Scripten bedeutet dies viel Arbeit durch nachträgliche Anpassungen.
- Nicht selten sind mehrere Exchange Server vorhanden. Es kann aber im ConnectionURI nur einer hinterlegt werden. Bei einem Ausfall-Szenario oder einer geplanten Wartung würden gespeicherte Scripte keine Verbindung mehr zum Exchange Server aufbauen können.
So kam mir die Idee zu einer einfach aufrufbaren Funktion, welche im Hintergrund alle möglichen Exchange Server ermittelt und dann der Reihe nach durchprobiert.
Die Exchange Server tragen sich in der Configuration-Partition im Active Directory ein. Diese kann mit einfachen LDAP-Befehlen direkt aus der PowerShell heraus abgefragt werden – Ohne die Notwendigkeit des PS-Modules „ActiveDirectory“. Hier sind die gewünschten Informationen im ADSIedit sichtbar:

Und mit diesen Zeilen kann man sehr einfach nach der ObjectClass msExchPowerShellVirtualDirectory suchen:
$Partition = "LDAP://" +([adsi] "LDAP://RootDSE").Get("configurationNamingContext")
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.SearchRoot = $Partition
$Searcher.Filter = "(objectClass=msExchPowerShellVirtualDirectory)"
$Searcher.FindAll().Properties.msexchinternalhostname
Das Ergebnis ist ein Array aller PowerShell-VirtualDirectories mit den URI’s:

Leider erhalten nur Administratoren der Exchange Server die URI-Liste angezeigt. Alle anderen Benutzer ohne besondere Rechte (aber mit einem Postfach) gehen leer aus:

Eine Alternative zur Abfrage wäre daher die Ermittlung der Exchange-Servernamen und daraus abgeleitete URIs:
$Partition = "LDAP://" +([adsi] "LDAP://RootDSE").Get("configurationNamingContext")
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.SearchRoot = $Partition
$Searcher.Filter = "(objectCategory=msExchExchangeServer)"
$Searcher.FindAll().Properties.cn |
ForEach-Object {
"http://$_/powershell"
}

Der Rest der Funktionslogik war dann recht einfach.
Das fertige Script habe ich zur besseren Strukturierung in einzelne Regionen unterteilt. Damit kann man relativ einfach durch die Anweisungen navigieren:

Zuerst werden bestehende Verbindungen zu einem Exchange Server ermittelt. Sind diese vorhanden, dann wird die Funktion beendet. Alternativ kann mit einem Parameter -NewSession auch ein Beenden der bestehenden Verbindungen erfolgen, bevor eine neue Verbindung aufgebaut wird. Dann sucht die Funktion nach URIs. Dabei wird zuerst der „AdminMode“ verwendet. Nur, wenn dieser keine Ergebnisse liefert, werden die URIS aus den Servernamen generiert. Dann wird die URI-Liste durchprobiert, bis eine erfolgreiche Verbindung aufgebaut wurde oder die Liste kene weiteren Einträge enthält. Zuletzt gibt es noch einen Ausgabebereich.
Das ist nun der finale Code:
function verbinde-MX {
<#
.Notes
Scriptreihe: verbinde MX (Exchange Server 2013,2016,2019)
Datum: 2020-05-24
Version: V1.00
Programmierer: Stephan Walther
.Synopsis
Die Funktion verbindet die aktuelle PowerShell mit einem Exchange Server.
.DESCRIPTION
Mit dieser Funktion kann die aktuelle PowerShell-Session bequem mit einem verfuegbaren
Exchange Server verbunden werden. Dies funktioniert mit den Versionen zwischen 2013 und
2019. Hybrid-Szenarien und Exchange Online wurden noch nicht getestet. Die Funktion
ermittelt via LDAP die existierenden Exchange Server und probiert dabei alle durch,
bis eine erfolgreiche Verbindung zustande kommt oder keine weiteren Server mehr vorhanden
sind.
.EXAMPLE
verbinde-MX
Ohne Parameter sucht die Funktion nach einem verfuegbaren Exchange Server und stellt eine
Verbindung her. Dabei werden Meldungen ausgegeben.
.EXAMPLE
verbinde-MX -NewSession
Wurde bereits eine Verbindung aufgebaut, dann wird ein erneuter Aufruf dies erkennen und
keine neue Session erstellen. Mit dem Zusatz -NewSession wird die bestehende Verbindung
entfernt und neu aufgebaut.
.EXAMPLE
if (verbinde-MX -ReturnBool -Silent) {
# erfolgreiche Verbindung
} else {
# fehlgeschlagene Verbindung
}
Diese Variante eignet sich besonders für Bedingungen, da keine Meldungen erzeugt werden.
Stattdessen wird ein Boolean-Wert für die Bedingung zurueckgegeben.
.PARAMETER Silent
Der Parameter unterdrueckt die Meldungstexte. Im Standard werden via Write-Host
Meldungen angezeigt.
.PARAMETER ReturnBool
Der Parameter gibt als Ergebnis des Verbindungaufbaus ein true oder false zurueck.
Damit kann die Funktion direkt in if-Bedingungen verwendet werden. Im Standard
wird nur der Meldungstext ausgegeben.
.PARAMETER NewSession
Mit diesem Parameter wird eine neue Verbindung erzwungen, wenn schon eine zu
einem Exchange Server besteht. Die alte Verbindung wird beendet.
#>
[outputtype([boolean])]
param(
[parameter(Mandatory=$false)] [switch] $Silent = $false,
[parameter(Mandatory=$false)] [switch] $ReturnBool = $false,
[parameter(Mandatory=$false)] [switch] $NewSession = $false
)
#region Variablen
$weiter = $true
$WriteHost = -not $silent
#endregion
#region besteht bereits eine Verbindung?
if ($weiter) {
$MXSessions = Get-PSSession |
Where-Object {$_.ConfigurationName -eq 'microsoft.exchange' -and $_.State -eq 'opened'}
if ( $MXSessions ) {
if ( $NewSession ) {
try {
$MXSessions | Remove-PSSession
if ($WriteHost) {Write-Host "Eine bestehende Verbindung zum Server $($MXSessions.ComputerName -join ' und ') wurde beendet." -ForegroundColor Green}
}
catch {
if ($WriteHost) {Write-Host "FEHLER bei Beenden der Verbindung: $($error[0].Exception.Message)" -ForegroundColor Yellow}
$weiter = $false
}
} else {
if ($WriteHost) {Write-Host "Es besteht bereits eine Verbindung zum Server $($MXSessions.ComputerName -join ' und ')." -ForegroundColor Green}
$weiter = $false
}
}
}
#endregion
#region Exchange-Powershell-URLs finden
if ($weiter) {
try {
$URLs = @()
# Variante 1: direkte Abfrage im LDAP ==> setzt Exchange-Berechtigung voraus
$Partition = "LDAP://" +([adsi] "LDAP://RootDSE").Get("configurationNamingContext")
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.SearchRoot = $Partition
$Searcher.Filter = "(objectClass=msExchPowerShellVirtualDirectory)"
$Searcher.FindAll().Properties.msexchinternalhostname |
ForEach-Object {
if ($_.length -gt 0) {
$URLs += $_
if ($WriteHost) {Write-Host "Server gefunden: $_" -ForegroundColor Green}
}
}
# Variante 2: falls keine URLs wegen fehlenden Rechten gefunden wurden ==> MX-Server ermitteln
if ($URLs.count -eq 0) {
$Searcher.Filter = "(objectCategory=msExchExchangeServer)"
$Searcher.FindAll().Properties.cn |
ForEach-Object {
$URLs += "http://$_/powershell"
if ($WriteHost) {Write-Host "Server gefunden: http://$_/powershell" -ForegroundColor Green}
}
}
# Ergebnis:
if ($URLs.count -eq 0) {
if ($WriteHost) {Write-Host "Es wurden keine Exchange Server gefunden!" -ForegroundColor Yellow}
$weiter = $false
}
}
catch {
if ($WriteHost) {Write-Host "FEHLER bei Verbindung mit LDAP: $($error[0].Exception.Message)" -ForegroundColor Yellow}
$weiter = $false
}
}
#endregion
#region Verbindung aufbauen
if ($weiter) {
$verbunden = $false
$URLs | ForEach-Object {
if ($verbunden -eq $false){
try {
$URL = $_
$MX = New-PSSession -ConnectionUri $URL -Authentication Kerberos -ConfigurationName microsoft.exchange -ErrorAction Stop
Import-PSSession -Session $MX -DisableNameChecking | Out-Null
$verbunden = $true
if ($WriteHost) {Write-Host "verbunden mit $URL" -ForegroundColor Green}
}
catch {
if ($WriteHost) {Write-Host "FEHLER bei Verbindung mit $($URL): $($error[0].Exception.Message)" -ForegroundColor Yellow}
}
}
}
}
#endregion
#region besteht jetzt eine Verbindung? ==> Ausgabe
if ($ReturnBool) {
$MXSessions = Get-PSSession |
Where-Object {$_.ConfigurationName -eq 'microsoft.exchange' -and $_.State -eq 'opened'}
if ( $MXSessions ) {
return $true
} else {
return $false
}
}
#endregion
}Und so könnten die Szenarien der Verwendung aussehen. Der klassische Aufruf erfolgt ohne Parameter:



Alternativ lässt sich der Aufruf auch in Bedingungen verwenden:

Und bestehende Verbindungen können erneuert werden:

Die Funktion ist fertig und ich kann sie in meine Exchange Scripte integrieren. Und hier gibt das Script noch als zip-Archiv .
Stay tuned!