Exchange Web Services (EWS)

Microsoft Consultant Exchange & Skype for Business (m/w)
Kommen Sie zu Net at Work. Wir haben spannende Projekte bei innovativen Kunden. Unser Team arbeitet kollegial und kooperativ – ständiger Austausch und Weiterbildung sind bei uns Standard.
https://www.netatwork.de/unternehmen/karriere/

Früher war MAPI/CDO der bevorzugte Weg, um an die Inhalte eines Exchange Servers (Postfach und öffentliche Ordner) zu kommen. Allerdings hat dieser Weg in der modernen Zeit diverse Einschränkungen. Als 16/32bit Applikation bzw. COM-DLL ist der Weg in die Zukunft nicht einfach. Zudem erfolgt die Kommunikation per RPC gegen den Postfachserver. HTTPS wäre aber viel besser. Mit Exchange 2000/2003 gab es einen Zwischenschritt, da über das Protokoll WebDAV ein Zugriff auf Inhalte per HTTPS möglich wurde. Besonders elegant war dies aber auch nicht und mit Exchange 2007 wurden erstmals die Webservices bereit gestellt, welche zukünftig alle anderen APIs (WebDAV, CDO, ExOLEDB und OWA URL Commands) ersetzen sollen. Mit Exchange 2010 wurden dann eine ManagedAPI für .NET bereit gestellt, dass Entwickler sehr viel einfacher arbeiten können.

Achtung: EWS mit Exchange Online und 13. Okt 2020: BasicAuth entfällt, OAUTH erforderlich
Upcoming changes to Exchange Web Services (EWS) API for Office 365
https://blogs.technet.microsoft.com/exchange/2018/07/03/upcoming-changes-to-exchange-web-services-ews-api-for-office-365/

Starting today, Exchange Web Services (EWS) will no longer receive feature updates
https://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Upcoming-changes-to-Exchange-Web-Services-EWS-API-for-Office-365/ba-p/608055 "

However, this API is in sustaining mode, the recommended access pattern for Office 365 and Exchange online data is Microsoft Graph
Quelle: https://GitHub.com/officedev/ews-managed-api

Funktionsumfang

Was sie mit Webservices heute schon abdecken können, zeigt ein Diagramm. Sie zeigt sehr gut, welche Funktionen schon in Exchange 2007 enthalten waren und dass Exchange 2007 SP1 den Exchange 2007 Funktionsumfang quasi komplettiert hat. Natürlich wurden die Webservices mit Exchange 2010 nochmal erweitert.


Quelle: TechEd UNC324 What's New in Exchange Web Services in Microsoft Exchange Server 2010, Autor Albert Kooiman

Das Ernsthaftigkeit zum Einsatz von Exchange Web Services von Microsoft ist klar und deutlich: Sehr viele Produkte und Programme setzen mittlerweile auf EWS auf z.B. Entourage für Mac Web Service Edition, die TransporterSuite, Project Server 2010 für Aufgaben und immer mehr andere Tools von Microsoft und Drittherstellern. Die eigenständige Seite Webservice liefert eine allgemeine Beschreibung.

Über die Exchange Web Services ist daher ein einfacher, internettauglicher, plattformunabhängiger Zugriff auf Inhalte möglich. Per Webservice kann sich ihr Programm sogar informieren lassen, wenn sich in einem Ordner etwas ändert. Allerdings funktioniert das nur Asynchron und nur pro Ordner. Es ist daher kein Ersatz für die Storesinks von Exchange 2000/2003. Allerdings kann man über die Synchronization-API Änderungen sehr einfach ermitteln und darauf reagieren.

Exchange Web Services nutzen

Ehe es von Microsoft die "Managed API" zu den Exchange WebServices gab, muss man sich noch selbst die WSDL-Datei besorgen und sich Proxy-Klassen schreiben und sehr genau prüfen, welche Parameter an welcher Stelle eingesetzt werden sollen. All Links zum Einsatz ohne Managed API habe ich mittlerweile an das Ende der Seite verschoben. (Siehe Exchange 2007 Webservices ohne Managed API). Sie sind immer noch wichtig, wenn Sie mit Programmen auf Exchange zugreifen wollen, die nicht .NET unterstützen, z.B. Java, Unix etc. Ansonsten würde ich speziell für Einsteiger stickt dazu raten, die Managed API zu nutzen.

Die sind aber erst mal herunter zu laden und auf dem Client zu installieren, damit Sie diese auch nutzen können.

Microsoft Exchange Web Services Managed API (Nicht mehr gültig)
Version 2.2: http://www.microsoft.com/en-us/download/details.aspx?id=42951
Version 2.1: http://www.microsoft.com/en-us/download/details.aspx?id=42022
Version 2.0: http://www.microsoft.com/en-us/download/details.aspx?id=35371
Version 1.2 http://www.microsoft.com/download/en/details.aspx?id=28952

Aktuell wird die Entwicklung wohl auf GitHub weiter geführt
https://GitHub.com/officedev/ews-managed-api
https://www.nuget.org/packages/Microsoft.Exchange.WebServices/

Die Installation erfolgt per NUGET

Register-PackageSource -provider NuGet -name nugetRepository -location https://www.nuget.org/api/v2
Install-Package Microsoft.Exchange.WebServices -Source "NuGet.org"

Exchange 2010 Service Pack 1 (SP1) Web Services Software Development Kit (SDK)
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=d55dcd05-1dd6-4f44-956f-82f052ee7d82

Exchange API-Spotting Exchange 2010 SDK March Update
http://blogs.msdn.com/exchangedev/archive/2010/03/03/Thom-Randolph.aspx

Die Installation muss auf den System erfolgen, von dem aus der Zugriff auf die Daten erfolgen soll. Es ist nicht mehr erforderlich, eine Anwendung oder zusätzliche API auf dem Exchange Server zu installieren.

Nach Abschluss der Installation gibt es aber kein Icon im Startmenü o.ä. Sie können dann in ihrem Projekt einfach die neuen DLLs einbinden und dann basierend darauf eine neue Instanz eines Objekts zu erstellen und für Diagnosezwecke das Tracing zu aktivieren.

using Microsoft.Exchange.WebSerivces.Data;
using Microsoft.Exchange.WebSerivces.Autodiscover;

/* ewsserver initialisieren */
ExchangeService ewsservice= new ExchangeService;
/* ewsserver initialisieren wenn Ziel Exchange 2007 ist*/
ExchangeService ewsservice= new ExchangeService(Exchange2007_SP1);

/* Optional Credentials angeben */
ewsservice.Credentials = new NetworkCredential("name", "pwd", "domain");

/*  bei Bedarf ein trace zum debugging einschalten */
ewsservice.traceEnabled = true;

/*  URL definieren ODER per Autodiscover ermitteln */
service.URL = new uri("https://servername/EWS/Exchange.asmx");
service.AutodiscoverURL("mailadresse@domain.tld");

Exchange Web Services Overview
https://www.youtube.com/watch?v=wOQMJhrp6GQ

EWS-Filter: Zugriff kontrollieren ab Exchange 2010 SP1

Damit nicht jeder Client nun mit einem bekannten Benutzernamen per EWS und frei verfügbaren Tools im Store tummelt, kann ein Administrator seit Exchange 2010 SP1 auch hier mehr Einstellungen vornehmen. Dazu sind die beiden folgenden Commandlets relevant. Das SP1 für Exchange 2010 hat zwei Befehle erweitert:

Beide Commandlets können seit SP1 die folgenden Parameter:

  • EWSEnable
    Damit wird generell gesteuert, ob EWS erreichbar sein soll
  • EwsAllowMacOutlook".
    Damit kann gesteuert werden, ob Outlook 2011 für Mac per EWS auf Exchange zugreifen kann.
  • EwsAllowEntourage
    Gleiches ist für Entourage möglich.
  • EwsAllowOutlook
    Steuert, ob Outlook 2007 und höher über EWS zugreifen kann
  • "EwsApplicationAccessPolicy " und "EwsBlockList" und EwsAllowList
    Abhängig von der Einstellung der Policy (EnforceAllowList oder EnforceBlockList) können Sie über die Liste bestimmte Clients zulassen (/Allowlist) oder bestimmte Einträge aussperren (Blocklist)

Die Felder müssen nicht mit "Wahr" oder "Falsch" gefüllt sein, sondern können auch "leer" sein. Nach meiner Erfahrung überstimmten "pro Benutzer" eingestellte Parameter die organisationsweiten Einstellungen. Wenn nichts eingestellt ist, dann ist alles "erlaubt". (Das konnte ich aber z.B. mangels Mac noch nicht testen.

EWS Authentication

Zugriffe auf das eigene Postfach oder per EWS und Impersonation ist natürlich nicht anomym möglich. Wie bei HTTP üblich, muss sich der Benutzer oder ein Service entsprechend authentifizieren. Exchange ist hier flexibel und der Administrator kann auf dem Server die erlaubten Anmeldeverfahren mit "Set-EWSVirtualDirectory" einstellen.

Interessant wird die Anmeldung an Exchange Online, da Kerberos dort nicht geht und Microsoft den Umstieg von "Basic/NLTM" auf OAUTH forciert.

PowerShell und EWS

Mit dem Skript Test-EWS habe ich ein Framework geschaffen, mit dem Sie sehr schnell einen ersten Zugriff per EWS auf ihre Postfach durchspielen können.

Aus VBScript ist es nicht möglich, Managed-Code einfach aufzurufen. Aber PowerShell erlaubt dies schon sehr einfach. die folgenden Zeilen bauen eine Verbindung zum Exchange Server per EWS auf.

# Laden der Manages API-DLLs in den Prozessraum

[void][Reflection.Assembly]::LoadFile("C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll")

# ews-Klasse instanziieren
# Wenn Sie auf Exchange 2007 zugreifen, müssen Sie dies mit angeben

$ews = new-object Microsoft.Exchange.WebServices.Data.ExchangeService
#$ews = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)

# Dann müssen Sie die EWS-URL angeben oder per Autodicsover anhand der Mailadresse ermitteln lassen.

# Ermitteln mit Mailadresse
$ews.AutodiscoverURL($mailbox)

# alternativ: Aktuellen User verwenden
#$windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
#$sidbind = "LDAP://<SID=" + $windowsIdentity.User.Value.ToString() + ">"
#[adsi]$aceUser = [ADSI]$sidbind
#$ews.AutodiscoverURL($aceUser.mail.ToString())

# Alternativ URL fest vorgeben
$ews.URL = [system.URI]"https://casserver/ews/exchange.asmx"

# Optional mit anderen Benutzerdaten anmelden
$ews.Credentials = New-Object System.Net.NetworkCredential("Username","passwort","domain")

Danach steht die Klasse für die weitere Verwendung zur Verfügung.

Mail per EWS versenden

Sicher kann ich auch per SMTP eine Mail einfach versenden. Das geht per Blat oder auch über Mail per CDO Senden, aber dazu ist immer SMTP und Port 25 erforderlich. Viele Firewalls, Virenscanner etc. blocken dies und und per HTTP und EWS ist man hier zum einen flexibler und die Mail landen auch noch in "gesendete Objekte".

Import-Module -Name 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll'
$Credential = Get-Credential
$exchService = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExchangeService
$exchService.Credentials = New-Object `
                              -TypeName Microsoft.Exchange.WebServices.Data.WebCredentials `
                              -ArgumentList $Credential.UserName, $Credential.GetNetworkCredential().Password
$exchService.AutodiscoverUrl($Credential.UserName, {$true})
$eMail = New-Object 
              -TypeName Microsoft.Exchange.WebServices.Data.EmailMessage 
              -ArgumentList $exchService
$eMail.Subject = 'Test EWS'
$eMail.Body = 'Test EWS'
$eMail.ToRecipients.Add('demouser@msxfaq.de') | Out-Null
$eMail.SendAndSaveCopy()

Natürlich können Sie der Mail noch Anlagen, Formatvorlagen etc. verpassen.

Posteingang per EWS auslesen

Auch das Abrufen von Mails aus dem Posteingang und anderen Ordnern ist mit der Managed API sehr einfach geworden. Auch hier benötigen Sie den Initialisierungsteil und dann geht es sehr einfach weiter. Ungewohnt ist hier, dass man neben dem Ordner sich erst mal einen "View" definieren muss, damit man die gewünschten Daten bekommt. Das ist aber ganz hilfreich, denn wenn Sie wissen, nach was sie suchen, dann brauchen Sie nicht alle Inhalte anzufordern und auf dem Client zu filtern, sondern den Server kann schon filtern.

Write-Host "Bindung Inbox"
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($ews, `
       [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
write-host "Number or unread Messages : " + $inbox.UnreadCount
$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1)
$findResults = $ews.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$view)
""
"Last Mail From : " + $findResults.Items[0].From.Name
"Subject : " + $findResults.Items[0].Subject
"Sent : " + $findResults.Items[0].DateTimeSent

Diese Zeilen zeigt die erste Mail an.

  • Test-EWS
    Einfache EWS-Funktionstests mit PowerShell

Kalenderzugriff per EWS

Per EWS ist auch ein Zugriff auf Termine möglich. Hier ein paar Links zu weiterführenden Seiten:

Kontakte mit EWS

Neben den Mails und Terminen gibt es auch Personendokumente. Hierbei müssen Sie zwischen den Kontakten im Postfach jedes einzelnen Anwenders und dem globalen Adressbuch unterscheiden. Beide Datenquellen können per EWS abgefragt werden. Allerdings sind es unterschiedliche APIs. Ich beschränkte mich hier auf Links zu anderen Seiten:

Autodiscover und Umleitung

Ehe Outlook 2007 SP1 auch mit SRV-Records arbeiten konnte, konnte ein Admin also nur ein Zertifikat für "autodiscover.maildomain.tld" konfigurieren oder ohne SSL einen HTTP-Redirect machen. Das funktioniert mit Outlook 2007 schon sehr gut aber wer per Exchange Web Services darauf zugreifen will, muss eine "Callback"-Funktion einrichten, die die Rückfrage bezüglich der "Umleitung" behandelt.

Das geht mit PowerShell 1.0 schon gar nicht und mit 2.0 ist die Komplexität für die MSXFAQ eher hoch. Sie sollten dann einfach die URL manuell angeben.

$ewsservice.URL = "https://exchangeserver/EWS/exchange.asmx

Sie können diese URL auch per Browser ansurfen. Nach erfolgter Authentifizierung bekommen Sie folgende Seite bei Exchange 2016

Lassen Sie sich hier nicht davon irritieren, dass in dem Beispiel der "echte Servername" und bei Exchange 2016 mit kombinierter Frontend/Backend-Rolle auch der interne Port (444) erscheint. Als Programmierer sollten sie natürlich den richtigen öffentlichen Namen mit HTTPS über 443 nutzen. Die hier sichtbare URL ist wenn, dann nur intern erreichbar aber nie von extern. Die URL sollten sie dann auch entsprechend umschreiben:

https://exchangeserver/EWS/services.wsdl

Darauf antwortet der Exchange Server nach Authentifizierung mit einer XML-Datei zur API-Beschreibung, die auch ein Browser anzeigen kann

Diese URLs sind also ein erster Test, ob EWS schon einmal generell erreichbar ist. Sie eignen sich in Grenzen auch für ein Monitoring.

EWS und Impersonation

EWS-Header: X-AnchorMailbox u.a.

EWS nutzt HTTPS und auch wenn EWS einfach so funktioniert, gibt es Header, die ein Entwickler setzen sollte. Einige sind optional aber X-AnchorMailbox ist in vielen Stellen essentiell.

  • User-Agent
    Für die Fehlersuche auf dem Server ist es nützlich, wenn das eigene Skript sich gegenüber dem WebServer auch mit einem eigenen UserAgent meldet. Das geht sehr einfach aber das "UserAgent"-Property beim EWSService-Objekt und erlaubt dem Admin die direkte Suche im IISLog auf dem Server
  • X-ClientStatistics
    Über das EWS-Property SendClientLatencies der Klasse kann der Programmierer einrichten, dass EWS entsprechende Performance Kennzahlen an den Server zurück sendet. Aktuell sammelt aber nur Exchange Online diese Daten, damit Microsoft seinen Service verbessern kann.
  • X-AnchorMailbox
    X-AnchorMailbox
    Dieser Header ist alles andere als optional und sollte von Entwicklern aktiv genutzt werden. Dies gilt besonders, wenn ein Prozess mehrere Postfächer per Impersonation anspricht. Über die Adresse im X-AnchorMailbox wird der Bezug zum gleichen BackendServer festgezurrt. Ansonsten könnte für jeden Request ein anderer Server genutzt werden, was Mehraufwand bei Verbindungsaufbau, Authentifizierung etc. bedeutet und Performance kostet.

A high scale EWS-based application/service that uses impersonation needs to manage affinity with the backend Exchange server by adding an HTTP header called "X-AnchorMailbox" to requests with the ExchangeImpersonation SOAP header. The values of the X-AnchorMailbox HTTP header and the ExchangeImpesonation SOAP header on the request should match.
Quelle: https://blogs.msdn.microsoft.com/mstehle/2013/07/25/managing-affinity-for-ews-impersonation-in-exchange-2013-and-exchange-online-w15/

Weitere Informationen finden Sie auf folgenden Seiten.

EWS für OOF

OOF-Einstellungen sind auch per EWS erreichbar. Hier ein paar Einstiegsadressen:

EWS und Elementgrößen

Haben sie schon mal versucht, ein Postfach von Notes oder Exchange nach Exchange 2007/2010 zu verschieben, in dem eine Mail > 50 MB lag?. Richtig, das geht nicht, das die Migration über EWS erfolgt. Microsoft hat hier aber einen "Schutz" gegen angriffe eingebaut der ein upload von größeren Elementen verhindert. Es wäre doch Schade, wenn ein böser Prozess einen upload von 20 GB "starten" (aber eh nie abschließen) würde und Exchange dafür Platz/RAM reservieren müsste. Daher gibt es ein Limit, welches Sie aber hochsetzen können.

Das "Problem" betrifft nicht nur Migrationen sondern jede Art von Zugriff auf EWS.

Ein Attachment "vergrößert" sich beim Einsatz von EWS
20 MB: Dateigröße auf Disk
30 MB Datei nach MIME-Konvertierung (+33%)
43 MB Datei codiert innerhalb der XML/SOAP-Message (+36%)
Die individuellen Größen können abweichen.

Entsprechend können Sie mehrere Stellen "treffen", bei denen der IIS über Grenzen steuern. Letztlich sind es Änderungen an der "web.config".

<configuration>
	<system.web>
		<httpRuntime maxRequestLength="10240" />
	<system.web>
<configuration>

Der Wert wird in "Kilobyte" angegeben.

Achtung
Dies ist eine XML-Datei und Tippfehler bewirken, dass der entsprechende Applicationpool nicht mehr startet. Sie können viele Einstellungen auch im IIS-Manager machen.

Danach ist der IIS mit einem IISRESET durchzustarten, damit die Einstellungen aktiv werden.

Hier eine unvollständige Liste der Parameter, mit denen ich schon mal konfrontiert worden bin.

Parameter   IIS 2007 2010

maxRequestLength

web.config

4KB

13280KB

2097151KB

MaxReceiveMessageSize

web.config

na ?

na ?

RTM: 13600000
SP1: 35000000
Achtung: 8x vorhanden

maxAllowedContentLength

web.config

30 MB

30 MB

35 MB

executionTimeout

web.config

110 Sek

 

 

Es gibt noch weitere Grenzwerte (z.B. connectionTimeout), die aber nur bei sehr großen Anlagen über sehr langsame Leitungen zum Tragen kommen. Auch Funktionen wie "Connection Filterung" oder eine vorgelagerte Firewall können natürlich eigene Grenzen umsetzen und am Ende

EWS und Notification Services

Die Webservices öffnen noch einen netten Weg: eine Anwendung kann per Webservices eine "Pushbenachrichtigung" einstellen, d.h. der Exchange Server informiert die Anwendung darüber, wenn sich in einem Postfach z.B. etwas tut. Dazu muss die Anwendung selbst auch einen Webservice bereitstellen, welcher von Exchange dann "Rückwärts" aufgerufen wird. Als Programmierer weise ich also Exchange an, von von meiner Anwendung angebotenen Webservice bei bestimmten Situationen aufzurufen.

Achtung: Die Funktion auf Push-Notifications zu warten wurde 2007 eingeführt aber schon 2010 durch "Streaming Notifications" abgelöst. Im Jan 2019 wurde die Push-Notification für Angriffe missbraucht und von Microsoft angepasst.
Exchange Fail Jan 2019 - CVE-2018-8581

SubscribePullNotification

Sie müssen aber einen Listener entwickeln, welcher dann von Exchange aufgerufen (ebenfalls WebService) wird. Details finden Sie auch hier:

Wenn Sie alles richtig gemacht haben aber ihr Webservice dennoch nicht angesprochen wird, dann sollten Sie einen Blick ins Eventlog tun:.

Event Type:      Warning
Event Source:    MSExchange Web Services
Event Category:  Core
Event ID:        6
Description: unable to send a notification für subscription 
fsdfllkn4325kjbzuvjdj2sj1jshj2dk2kj3s1fefh13g4g4sjg34as3244=. (Send attempts: 3)
Event Type:      Error
Event Source:    MSExchange Web Services
Event Category:  Core
Event ID:        7
Description:
After 8 unsuccessful attempts to send a notification für subscription 
fsdfllkn4325kjbzuvjdj2sj1jshj2dk2kj3s1fefh13g4g4sjg34as3244=
K8=, subscription has been removed.
Event Type:      Warning
Event Source:    MSExchange Web Services
Event Category:  Core
Event ID:        5
Description: unable to send a notification für subscription 
fsdfllkn4325kjbzuvjdj2sj1jshj2dk2kj3s1fefh13g4g4sjg34as3244=
K8=. Will retry.

Die lange Zeichenkette ist in Realität eine Base64-codierte Binärdatei, die man zwar decodieren kann, aber viel mehr als den Servername und ein paar kryptische Zeichen sieht man nicht. Es ist aber zumindest ein Hinweis, wohin der Sink gehen sollte. Die Einträge sind übrigens auf dem CAS-Server hinterlegt und wenn ich den Entwicklern glauben darf gibt es keine einfache Möglichkeit eine Liste der aktiven Benachrichtigungen zu generieren.

Man kann allerdings die IIS-Logs durchsuchen, wer, wann ein "Subscribe" auf den Exchange Web Services macht, d.h. im IISLog einfach mal nach "ews/exchange.asmx?SoapAction=Subscribe" suchen.

https://forums.microsoft.com/TechNet/ShowPost.aspx?PostID=931885&SiteID=17
For push notifications, you need to write your own web service that implements the NotificationService.wsdl interface. Then you subscribe to receive notifications using the EWS Subscribe web method with a PushSubscriptionRequest sub-element. You will notice that the PushSubscriptionRequestType surfaces a status frequency (how often you want to be alerted of changes) and a URL which is the URL of YOUR web service. Then EWS will call the SendNotification web method on your service with the changes. You can unsubscribe by responding to the SendNotification web method with a subscription status of "Unsubscribe".

Streaming Notifications

Die Bereitstellung eines WebService und diesen von Exchange aufrufen zu lassen, ist auch mit Teams ausgehend WebHooks üblich aber für Exchange nicht immer passend. Daher gibt es auch eine Streaming API. Per EWS setze ich dann einen Request ab und der Server verzögert die Antwort einfach, bis eine Antwort sinnvoll ist. Die Verbindung wird weiter vom Client aufgebaut und bleibt bestehen. Das Handling kann man der EWS.DLL überlassen und auf https://administrator.de/contentid/364117  habe ich dazu einen schönen kompakten Beispielcode gefunden. Damit er nicht verloren geht, habe ich ihn hier unverändert übernommen. Leider steht kein Autor oder Copyright im Original. Poster war der Benutzer Colinaro ( https://administrator.de/user/colinardo/). Das Skript zeigt auch schön, wie man Zertifikatfehler abfängt und mit PowerShell Events (PowerShell und Callback-Funktionen) arbeitet.

# ===== START VARS =====
$global:MAILBOX = 'max.muster@domain.tld'
$global:EXSERVER = 'ex.domain.tld'
$global:SUBJECTREGEX = 'SPAM'
# ===== END VARS =====

$global:CONNECTIONTIMEOUT = 30

if ($PSVersionTable.PSVersion.Major -lt 3){write-host "ERROR: Minimum Powershell Version 3.0 is required!" -F Yellow; return}  

# EWS DLL laden
Add-Type -Path "$PSScriptRoot\Microsoft.Exchange.WebServices.dll" -EA Stop

Get-EventSubscriber | Unregister-Event -Force

# Allen Zertifikaten vertrauen
Add-Type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
# Trust all certs policy dem ServicePointManager zuweisen
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy


# EWS Objekt erstellen
$global:ews = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010)
$global:ews.Url = "https://$global:EXSERVER/ews/exchange.asmx"
# Benutze die Credentials mit dem das Skript ausgeführt wird
$global:ews.UseDefaultCredentials = $true
$global:ews.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, [string]$global:MAILBOX)

$global:subscription = $global:ews.SubscribeToStreamingNotifications([Microsoft.Exchange.WebServices.Data.FolderId[]](New-Object Microsoft.Exchange.WebServices.Data.FolderId ([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)),[Microsoft.Exchange.WebServices.Data.EventType]::NewMail)
$global:connection = New-Object Microsoft.Exchange.WebServices.Data.StreamingSubscriptionConnection($global:ews,$global:CONNECTIONTIMEOUT)

# Event das beim Eintreffen einer/mehrerer neuer Mails ausgeführt wird
Register-ObjectEvent $global:connection -EventName OnNotificationEvent -SourceIdentifier NewMailNotification -Action {
    write-host "NewMail subscription event received." -F Green
    # Alle eingetroffenen Mails verarbeiten
    $Event.SourceEventArgs.Events | %{
        # Mail-Details mit übermittelter ItemId abrufen
        $mail = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($global:ews,$_.ItemId)
        # Wenn die Mail gefunden wurde ...
        if ($mail){
            # Wenn der Betreff ein bestimmten Inhalt hat ...
            if ($mail.Subject -match $global:SUBJECTREGEX){
                write-host "New mail wich matches subject: '$($mail.Subject)'." -F Green
            }
        }
    }
}

# Event das beim Disconnect der Verbindung ausgeführt wird (Verbindung wird dann erneut hier wiederhergestellt)
Register-ObjectEvent $global:connection -EventName OnDisconnect -SourceIdentifier OnDisconnectNotification -Action {
    write-host "Subscription-Connection disconnected, restarting connection ... " -F Yellow -NoNewline
    while(!$global:connection.IsOpen){
        $global:connection.Open()
    }
    write-host "Connected." -F Green
}

# Subscription erstellen
$global:connection.AddSubscription($global:subscription)
# Subscription öffnen
$global:connection.Open()

cls
write-host "Waiting for new events ..." -F Green
while($true){
    sleep 1
}

EWS Performance Analysen über das IISLog

Sie wissen ja nun, dass die Web Services ein Teil des Webservers IIS sind. Damit landen im IISLog auch die entsprechenden Anfragen. Hier drei exemplarisch Zeile, die zur Lesbarkeit gekürzt sind.

POST /ews/Exchange.asmx - - 401 2
POST /ews/Exchange.asmx - Domain\User 500 0
POST /ews/Exchange.asmx SoapAction=FindItem;Version=0;RpcC=27;RpcL=137;LdapC=10;LdapL=58; Domain\User - 200 0

Die erste Zeile zeigt eine anonyme Anfrage, die vom Server natürlich mit einem 401.2 Fehler quittiert werden. Die zweite Zeile hingegen zeigt, dass da wohl keine gültige Abfrage aber mit gültigem Benutzer erfolgt und mit einem 500er beantwortet wird. Hier wäre ein NETMON-Mitschnitt denkbar, um den XML-Request und die Details der Antwort zu sehen.

Besonders interessant ist die Zeile einer erfolgreichen EWS-Anfragen. Hier ist nicht nur der Befehl "FindItem" sichtbar, sondern auch ein paar Werte zur internen Verarbeitung. Bislang habe ich noch keine Bestätigung aber die Zahlen dürften die Millisekunden sein, die in der entsprechenden Routine verweilt wird.

Leider gibt es keinen Parser, der diese Details genauer auswertet.

EWS Server Log

Auch der Exchange Server selbst protokolliert neben dem IIS in seinen eigenen Protokolldateien viele Informationen. Dazu zählt eben auch EWS. Die Logs liegen per Default auf

C:\Program Files\Microsoft\Exchange Server\V15\Logging\Ews

Per PowerShell können Sie so z.B. ganz schnell mal ermitteln, welche Methoden die Clients generell einsetzen:

Get-Item "C:\Program Files\Microsoft\Exchange Server\V15\Logging\Ews\ews_20190102*-*.LOG" | %{import-csv $_} | group SoapAction –NoElement

Count Name
----- ----
  208
   52 SoapAction
67686 FindItem
36973 Sbsc_ConnStatus
26177 GetUnifiedGroupsSettings
 3253 Subscribe
 2243 Sbsc_CrteConn
 6956 GetUserAvailability
15388 GetUserOofSettings
 2143 Sbsc_EndConnSuccess
 2143 GetStreamingEvents
 1597 GetFolder
 4925 GetItem
  487 CreateItem
  104 DeleteItem
 1465 FindFolder
  407 GetClientIntent
  361 GetUserConfiguration
 1020
 8190 GetMailTips
 3344 GetAppManifests
 2565 GetServiceConfiguration
 1229 GetAppMarketplaceUrl
 7892 GetUserPhoto
 1549 Sbsc_EventsAvailable
 1638 Sbsc_SndNtf
 2551 GetEvents
 1129 EndSearchSession
 1495 Sbsc_WrtRspFailed
  216 GlobalActivity
 6545 GetSearchSuggestions
 3395 ExecuteSearch
 1227 StartSearchSession
  450 SyncFolderHierarchy
 5520 SyncFolderItems
  593 ConvertId
  213 ResolveNames
   46 GetClutterState
  378 UpdateItem
  116 Sbsc_QErr
  116 Sbsc_NoSubLeft
  116 Sbsc_ErrNtf
  272 GetRoomLists
   18 CreateFolder
    6 Unsubscribe
    6 FindConversation
   43 SetUserOofSettings
    3 UpdateFolder
   44 GetAttachment
   16 MoveItem
  108 Sbsc_DisCntSub
    1 CopyItem

Natürlich können sie auch anhand der anderen Felder und Inhalte ihre eigenen Auswertungen durchführen, z.B. EWS-Anfragen nach Client-IP oder User ermitteln und so Missbrauch oder Überbeanspruchung eingrenzen.

EWS Throttling

Neu in Exchange 2010 ist die Funktion, dass ein Client nicht den Server per Web Services voll machen. Wer also zu "verfressen" mit EWS arbeitet, wird ausgebremst. Sinnvoll ist dies bei der Entwicklung des Code zu berücksichtigen und "Paged Searches" zu verwenden.

NOTE: Even after the EWS Throttling Policy has been updated, EWS imports will still be limited to 150MB per 5 minutes per mailbox; to achieve higher migration throughput speeds, please migrate more users concurrently.
https://docs.microsoft.com/de-de/exchange/client-developer/exchange-web-services/ews-throttling-in-exchange

Weitere Infos finden Sie u.a. auf:

Exchange 2007 Webservices ohne Managed API

Eine Echtzeitverarbeitung mit der Option, eine Änderung des Clients zu verhindern oder synchron andere Funktionen durchzuführen ist mit Webservices scheinbar nicht möglich.

Hier nur eine kleine Auswahl von Links. Die MSDN-Seite ist sicher die beste Quelle.

EWS.DLL als Proxyklasse

Normalerweise nutzt ein Entwickler WSDL, um von einem Webservice die unterstützt Schnittstellen zu erfahren. Dazu muss man aber zur Entwicklungszeit natürlich zugriff auf den Webservice haben. Und nicht alle EntwicklungsUmgebungen unterstützen direkt die Einbindung der WSDL-Informationen. Man kann daher die Beschreibung in einer DLL "einpacken", so dass auch andere Umgebungen eine elegante Unterstützung bei der Entwicklung erlauben.

Eleganter weise hat Glen Scales schon wieder die Arbeit getan, die Microsoft im BLOG nicht getan hat und stellt eine EWS.DLL direkt bereit

EWS.DLL zum Download
http://msgdev.mvps.org/exdevblog/ewsutil.zip.

Mit dieser EWS ist es dann auch in PowerShell sehr einfach auf Exchange 2007 Ressourcen zuzugreifen. Hier ein Beispiel, um sich per PowerShell auf den Posteingang zu verbinden.

[void][Reflection.Assembly]::LoadFile("C:\Programme\msxfaq\EWSUtil.dll")

$mbMailboxEmail = "admin@msxfaq.local"
$ewc = new-object EWSUtil.EWSConnection($mbMailboxEmail,$false,$null,$null,$null,$null)

$dTypeFld = new-object EWSUtil.EWS.DistinguishedFolderIdType
$dTypeFld.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::inbox

$mbMailbox = new-object EWSUtil.EWS.EmailAddressType
$mbMailbox.EmailAddress = $mbMailboxEmail
$dTypeFld.Mailbox = $mbMailbox

$fldarry = new-object EWSUtil.EWS.BaseFolderIdType[] 1
$fldarry[0] = $dTypeFld
$msgList = $ewc.FindUnread($fldarry, $null, $null, "")
if ($msgList.Count -ne 0){
    "Inbox:" + $msgList[0].Subject.ToString()
}

Sie können daher die Beschreibung in eine DLL "einpacken", so dass auch andere Umgebungen eine elegante Unterstützung bei der Entwicklung erlauben.

Visual Studio und Webservices

Natürlich können Visual Studio 2005 und 2008 ganz einfach Webservices einbinden. Über das Projektmenü" wird ein Assistent zum einbinden eines Webservice gestartet, der dann eine "Hilfsklasse" erstellt, die in der späteren Entwicklung einfach genutzt werden kann.

VS 2005 Webservice Assistent

Mit Visual Studio 2008 gibt es eine "Service Referenz"

VS2008 Service reference

Danach können Sie in Visual Studio die Webservices wie jede andere Klasse verwenden

EWS und Limits

Per Default erlauben die Exchange 2007 Webservices nur Anlagen bis 10 Megabyte. Dies ist ein Schutz gegen übergroße http-Requests und sollte für die Migration temporär erhöht werden.

Dazu ist auf dem CAS-Server die WEB.CONFIG im virtuellen Verzeichnis "/EWS" der Default Webseite anzupassen. Diese liegt normalerweise auf C:\Program Files\Microsoft\Exchange Server\ClientAccess\exchweb\ews). Suchen Sie in der Datei nach dem Tag: <system.web> in dem sich folgende Zeile befindet:

<httpRuntime maxRequestLength="10240" />

Tragen Sie hier die gewünschte maximale Größe ein. Denken Sie aber daran, nach der Migration den Wert wieder auf eine für ihr unternehmen passende Größe einzustellen. ACHTUNG: Dieser Wert ist nach einem Update (Servicepack, Rollup) zu prüfen und eventuell erneut zu setzen.

Zudem sollten Sie im IIS noch die Konfiguration anpassen:

C:> cd \Windows\System32\inetsrv
C:> appcmd set config "Default Web Site/ews" -section:requestFiltering -requestLimits.maxAllowedContentLength:[size in Bytes!]
C:> iisreset

Danach sollten die neuen Grenzen gelten.

EWS und Memory

Bei Verschiedenen Programmen habe ich gesehen, dass diese immer auf dem Exchange Server immer mehr Speicher fressen und dann EWS die Schuld gegen wird. Genau genommen ist es aber eine unsaubere Programmierung des Clients. Exchange "cached" Daten. Der Prozess der "Garbage Collection" kann nur solche Speicherbereiche wieder freigeben, wenn die darin vorgehaltenen Objekte als "nicht mehr erforderlich" gekennzeichnet sind. Dazu gibt es extra die "Finalize"-Methode:

Wenn Sie als Entwickler also in einen Programm z.B. die EWS-Instanz nicht wieder frei geben, sondern einfach weiter neue Instanzen öffnen, dann wird der belegte Speicher nicht frei gegeben. Zugegeben, ich bin selten in der Situation mir darüber Gedanken machen zu müssen, da meine Skripte eigentlich nicht "endlos" laufen, sondern am Ende terminieren und damit den Speicher frei geben. Das ist aber anders, wenn Sie eine permanent aktive Lösung entwickeln.

EWS Synchronisation

Je weniger Server mit Exchange 2003 und Früher arbeiten, desto eher gibt es Exchange 2007 und höher, die alle EWS anbieten und auch die Microsoft Cloud und andere Anbieter bieten EWS als einziger Weg auf die Postfächer an. MAPI und CDO sind auf dem absteigenden Asts.

Da könnte man ja auf die Idee kommen, einfach per EWS z.B. zwei Postfächer in unterschiedlichen Exchange Organisationen "Synchron" zu halten, z.B. Um eine elegante Koexistenz zu erreichen oder auch eine Stichtagmigration vorzubereiten.

Aktuell habe ich selbst noch kein "fertiges Skript" oder gar eine Lösung dazu. Sie können mir aber gerne Links und Erfahrungen mitteilen, die ich hier gerne ergänze.

Weitere Links

Microsoft Consultant Exchange & Skype for Business (m/w)
Kommen Sie zu Net at Work. Wir haben spannende Projekte bei innovativen Kunden. Unser Team arbeitet kollegial und kooperativ – ständiger Austausch und Weiterbildung sind bei uns Standard.
https://www.netatwork.de/unternehmen/karriere/

Hier nur eine kleine Auswahl von Links. Die MSDN-Seite ist sicher die beste Quelle.