Exchange 2007/2010/2013 Transportagenten

Diese Seite ist etwas umfangreicher, da sie nicht nur das Prinzip eines Transport Agenten beschreibt, sondern auch meine eigene "Tour des Leides" bei der ersten Entwicklung. Viele Anleitungen im Internet machen es sich nämlich doch sehr einfach.

Ehe Sie selbst Transportagenten schreiben, sollten Sie prüfen, ob ihr Anliegen nicht durch die eingebauten Transportregeln erledigt werden kann
Transportregelaktionen
http://technet.microsoft.com/de-de/library/aa998315.aspx

Während bei Exchange 5.x fast gar keine Möglichkeit bestand, auf dem Server die Nachrichten bei der Übertragung zu verarbeiten, war bei Exchange 2000/20003 dies über die SMTP-Transportsinks (siehe auch SMTP-EventSink) zumindest in einer gewissen Weise möglich. Erst Exchange 2007 hingegen hat den Weg zu einer leistungsfähigen Verarbeitung geöffnet, da nun jede Mail, auch die zwischen Postfächern im gleichen Informationsspeicher, immer durch die Transport-Rolle muss. Und an dieser Stelle können nun Transport Agenten einsetzen, die diese Mails verarbeiten, verändern etc.

Die Exchange 2007 Transportregeln, welche z.B.: zum Anfügen eines Disclaimers auf E2K7 Disclaimer verwendet werden, sind auch einfach Transportagenten. Das schöne bei Exchange 2007 ist, dass die Agenten in Managed Code entwickelt werden können, und damit die Zeit der C++-Sinks für den Windows SMTP-Dienst langsam abläuft. Solche Agenten können auch im Exchange Edge Server genutzt werden.

Dieser Ort ist sehr leistungsfähig, da diese Agenten auf den kompletten Nachrichteninhalte vollständigen Zugriff haben. Es ist daher nur eine Frage der Zeit, bis Drittprodukte sind dieser Schnittstelle bemächtigen, um die Regeln, die Exchange 2007 von Hause aus bietet, zu erweitern. Denkbar sind z.B.:

  • Disclaimer
    Die Disclaimer Funktion von Exchange 2007 ist sehr rudimentär. Hier wird es sicher bald neue Produkte geben
  • Verschlüsselung
    Denkbar ist auch, dass Exchange Mails nach extern z.B. verschlüsselt. Dies würde das ein oder andere SMIME-Gateway überflüssig machen, welches bislang. Zumindest die Signatur anhand eines im Active Directory hinterlegten Schlüssels ist relativ einfach möglich.
  • Archivierung
    Bislang archivieren Produkte über das Journalpostfach alle Mails an eine Speichergruppe. Aber Mails, die nur "durch" Exchange durchlaufen, werden so nicht erfasst. Hier kann ein Transport Agent helfen.
  • Mail Management
    Natürlich kann ein eigener Agent auch ganz andere Filter auf Nachrichteninhalte umsetzen, z.B.: bestimmte Anlagen blockieren oder die Größe individuell beschränken.
  • Reporting
    Der Agent kann sehr viele Betriebsdaten der Mail in eine Reporting-Datenbank überführen. Ein Beispielcode liegt sogar bei Microsoft
    A Look at a Transport Agent: The Message Statistics Sample
    http://msdn.microsoft.com/en-us/library/bb421488(EXCHG.80).aspx
  • PDF und Packen
    Denkbar sind auch Lösungen, die Anlagen für den Benutzer transparent komprimieren oder Dokumente nach PDF konvertieren.

Sie sehen also, dass es sich für Dritthersteller aber auch für größere Firmen durchaus lohnen kann, in die Entwicklung eines eigenen Transport Agenten etwas Zeit zu investieren.

Umgebung bereitstellen

Zum Entwickeln von Transport Agenten benötigen Sie eine EntwicklungsUmgebung. Idealerweise eignet sich dazu Visual Studio 2005 und höher, wobei auch die Express-Editionen durchaus ausreichend sind. Diese müssen nicht auf dem Exchange Server selbst installiert werden, sondern können auch auf einem Entwicklungssystem installiert werden.

Allerdings habe ich mir einfach entsprechende virtuelle Umgebungen mit Exchange Server und Visual Studio installiert, damit ich unabhängig von jeder ProduktionsUmgebung testen und prüfen kann.

Agent-Typ festlegen

Exchange Transport Agenten gibt es in zwei/drei Fassungen, die unterschiedliche Module benötigen und auch unterschiedliche Einsatzbereiche haben. Beide laufen natürlich auf der Hub-Transportrolle

Typ RoutingAgent SMTPReceiveAgent DeliveryAgent
Exchange Version

2007 und 2010

2007 und 2010

2010 only

Einsatzbereich

Verarbeitung interner Messages innerhalb der Exchange Organisation

Verarbeiten per SMTP eingehender Mails. Besonders auch auf dem Edge-Server

Zustellung zu fremden Mailsystemen, die nicht SMTP verwenden sollen

Beispiele

Disclaimer, Filterung, Archivierung, Kategorisierung etc.

Spamschutz, Virenschutz, Filter, CatchUnknown2010, etc.

 

vererbte Klassen

  • RoutingAgentFactory
  • RoutingAgent

  • SmtpReceiveAgentFactory
  • SmtpReceiveAgent

 

Referenzen

 

 

Sowohl der RoutingAgent als auch der SMTPReceiveAgent werden durch die verschiedenen SMTP-Events getriggert und scheinen daher gleich zu sein. Faktisch unterscheiden Sie sich aber anhand der Eigenschaften der zur Verarbeitung bereit gestellten Mails. Grade die SMTP-ReceiveAgents können sehr früh (OnConnectEvent, OnHeloCommand, OnEhloCommand, OnRcptCommand etc.) getriggert werden und da auch schon Mails ablehnen, während der RoutingAgent in der Regel auf OnSubmittedMessage oder OnRoutedMessage gebunden wird, und dann die komplette Mail auswerten und verändern kann. Die Entscheiden ist hier im Bezug der Anforderungen als auch Performance zu treffen.

Folgende Seite nutze ich gerne als Startpunkt:

Projekt starten und Referenzen addieren

Ein Transport Agent ist primär mal ein Stück "Code", welches auf den zutreffenden Servern installiert werden muss, die als HubTransport-Rolle arbeiten. Dieser Code wird heute z.B.: mit Visual Studio 2005 in .NET 2.0 entwickelt und ergibt eine DLL, die auf den Server kopiert werden muss. Es gibt zwar keine Templates für Visual Studio aber im wesentlichen nutzen Sie einfach die Vorlage für eine Class-Library, addieren ein Class-Dokument und ergänzen die erforderlichen externen Referenzen.

  • Microsoft.Exchange.Data.Transport.dll
  • Microsoft.Exchange.Data.Common.dll

Die beiden DLLs liegen auf dem Exchange Server im Verzeichnis "C:\Program Files\Microsoft\Exchange Server\Public" und können sollten Sie vom Server dann auf ihre Entwicklungsmaschine kopieren.

Erst mit der Referenz auf diese beiden DLLs können wir einen Agenten schreiben.

Code Schreiben

Es gibt viele Beispiele im Internet, die ein Grundgerüst für entsprechende Agenten. Auf folgender Seite finden Sie sogar sehr kurze Beispielcodes.

Für die Entwickler ist das ein ziemlich einfaches Dokument aber Administratoren brauchen eigentlich nur zwei Dinge zu wissen.

  • Agenten sind DLLs mit Klassendefinitionen
  • Die Namen der Klassen werden bei der Konfiguration angegeben

Insofern ist es nicht weiter verwunderlich, dass ein "Rump" sehr überschaubar ist:

Es gibt in dem Beispiel eine Klasse "MyAgentFactory", die vom Transportdienst aufgerufen wird um eine Instanz der zweiten Klasse "MyAgent" zu erhalten. Diese Klasse bekommt hier einen "EndofDataHandler" spendiert, der am Ende des "DATA" vom Transport aufgerufen wird und dabei als "e" in dem Fall die Mail übergeben bekommt. Mit dieser Message kann man nun fast alles anstellen.

Transport Agent einrichten

Nachdem wir nun aus unserem Code eine DLL gebaut haben. müssen wie diese natürlich noch auf allen Transport Servern einrichten. Meist ist es tatsächlich damit getan, die DLL auf den Server zu kopieren und einzurichten. Nur wenn Sie selbst weitere Abhängigkeiten erfüllen müssen. bedarf es zusätzlicher Installationen. Die Einrichtung erfolgt dann durch eine Abfolge von PowerShell-Befehlen.

# Transportdienst stoppen
stop-service msexchangetransport

# Transport Agent eintragen
Install-TransportAgent `
 -Name "mail2displayname" `
 -TransportAgentFactory "msxfaq.agent.mail2displaynameFactory"`
 -AssemblyPath "C:\Program Files\Microsoft\Exchange Server\TransportRoles\agents\mail2displayname\mail2displayname.dll"

# danach unbedingt das PowerShell Fenster beenden !!!

Darauf weist Sie zwar auch die PowerShell noch mal explizit hin, wie sie in diesem Fenster sehen können.

Der Hintergrund ist, dass die PowerShell anscheinend die DLL beim "Install-TransportAgent" weiter gesperrt hält und damit der Transport Agent nicht drauf zugreifen kann. Verstehen muss ich das nicht aber beenden Sie einfach die PowerShell und starten Sie diese wieder. In der Zwischenzeit passiert noch nichts, da der Agent per Default erst mal "deaktiviert" ist.

Das merken Sie z.B.: auch, wenn Sie mit ihrer EntwicklungsUmgebung direkt in das Zielverzeichnis entwickeln.

Achtung:
Leider sperrt die PowerShell die gerade installierte DLL mit einem LOCK. Sie müssen also die PowerShell schließen und wieder öffnen, um den Agent zu deinstallieren. Das nervt besonders, wenn man per Visual Studio die DLL direkt an das Ziel kompilieren lässt. Man bekommt dann direkt einen Build-Fehler

Es ist einfacher eine geänderte DLL im Projektverzeichnis erstellen zu lassen und dann beim gestoppten MSExchangeTransport-Dienst am Ziel zu tauschen. 

Ehe Sie nun den Agenten aktivieren, müssen Sie bedenken, dass der Agent selbst als "Network System" läuft und daher z.B. keine Rechte hat, eine Eventlog-Quelle zu definieren. Wenn der Agent also ins Eventlog schreiben will, muss der Entwickler einen entsprechenden Code einbauen, der durch den Installer ausgeführt wird. Nur gibt es bei dem einfachen Beispiel ja erst mal keinen Installer. Wenn ein kommerzieller Hersteller später ein "Paket "draus macht, sieht das anders aus. Baue ich aber erst mal nur schnell eine DLL, dann muss ich den "Installationsabschnitt" manuell ausführen mit:

Das Comandlet "Install-TransportAgent" führt nicht den Abschnitt unter "[RunInstaller(true)]" aus !

REM Exchange 2007
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe 
    "C:\Program Files\Microsoft\Exchange Server\TransportRoles\agents\mail2displayname\mail2displayname.dll"

REM Exchange 2010
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe 
    "C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles\agents\mail2displayname\mail2displayname.dll"

Hier die Bildschirmausgabe:

Auf dem Bildschirm wird der Vorgang protokolliert und abschließend können Sie mit RegEdit den Eintrag auch sehen

Nachdem dies (und eventuell andere Vorarbeiten erfolgt sind, können Sie den Sink auf aktivieren:

# Transport Agent akivieren
Enable-TransportAgent mail2displayname

Start-Service msexchangetransport

Kontrollieren Sie nach dem Start auf jeden Fall das Eventlog auf Fehler, z.B. falsche DLL-Pfade etc. Wenn die DLL z.B.: nicht geladen werden kann, dann beendet sich der Transportdienst wieder !. Daher sollten sie in produktiven Umgebungen nur ausgetestete DLLs verwenden und zudem die Schritte einer Deinstallation zur Hand haben

# Transportdienst stoppen
Stop-Service msexchangetransport

# Transport Agent deaktiveren
Disable-transportagent mail2displayname

In den meisten Fällen reicht ein "Disable-Transportagent", um die Transportdienste dann wieder starten zu lassen. Die Deinstallation erfolgt dann über folgende Schritte. Eventuell. zusätzlich gemachte Änderungen (Dateien, Registrierungsschlüssel etc.) während der Installation werden damit aber nicht rückgängig gemacht.

# Transport Agent deregistrieren
uninstall-transportagent mail2displayame
#oder
Get-TransportAgent mail2displayname | uninstall-transportagent

#TransportDienst wieder starten
Start-Service msexchangetransport

Auch hier sollten Sie danach umgehend prüfen, dass die Funktion wieder hergestellt ist, z.B. mit einer Testmail, die sie per Telnet versenden können.

HELO telnet.msxfaq.local
MAIL FROM:<user@extern.tld>
RCPT TO:<f1u1@firma1.de>
DATA
Subject: mail2displayname-Testmail per Telnet
From: "ExternUser" <user@extern.tld>
To: "User1Firma1" <f1u1@firma1.de>

Dies ist der Body
.

Berechtigungen
Der Agent läuft mit den Rechten "NetworkAgent". Das ist viel weniger als "LocalSystem" o.ä. Der Agent kann also nicht einmal etwas in das Eventlog schreiben. Um Zugriff auf andere Ressourcen zu nutzen, müssen Sie daher diese z.B. mit der Angabe expliziter Anmeldedaten durchführen.

Error Handling

Vergessen Sie bitte NIE einen "Try/Catch" um ihre Blöcke zu machen, da ansonsten eine Schutzverletzung direkt den TransportDienst "rauswirft. Zwar startet Exchange den Dienst automatisch immer wieder neu und macht ein Recovery der TransportQueue, aber das ist relativ aufwändig. Sie finden die "Treffer" im Eventlog:

Log Name: Application
Source: MSExchange Common
Date: 07.04.2010 14:44:45
Event ID: 4999
Task Category: General
Level: Error
Keywords: Classic
User: N/A
Computer: WIN-TIT899W5KN4.e2007.msxfaq.de
Description:
Watson report about to be sent to dw20.exe für process id: 3204, with parameters: E12, c-RTL-AMD64, 08.02.0247.002, edgetransport, M.E.D.Transport, M.E.D.T.S.SmtpReceiveAgent.Invoke, System.NullReferenceException, d27d, 08.02.0231.000. ErrorReportingEnabled: False

Darauf kommen dann die ESE Meldungen

Besser ist es daher den kompletten Block mit einer Fehlerbehandlung abzufangen und einen Alarm zu generieren.

Debugging über die Trace-Schnittstelle

Eine sehr einfache Option ist die Trace-Schnittstelle von Windows. An den verschiedenen Stellen des Codes kann man als Entwickler einfach eine Trace-Ausgabe generieren, die dann mittels DebugView

Es bietet sich auch an, dass ihr Agent eine Möglichkeit einer Debug-Ausgabe hat. Das kann eine Datei, das Eventlog oder die Windows Debug-Schnittstelle sein. (DebugView), die unter .NET sehr einfach anzusprechen ist:

Trace.WriteLine("Dies ist eine Debugausgabe");
Trace.WriteLine("Dies ist eine Traceausgabe");

// System.diagnostics funktioniert nicht
// System.Diagnostics.Debug.WriteLine("Dies ist eine Traceausgabe");

Eine Ausgabe über die anscheinend gleichwertige Funktion System.Diagnostics funktioniert übrigens unter Windows 2008 zumindest nicht. Mit DebugView sieht das dann wie folgt aus:

Beachten Sie aber, dass Sie in DebugView die Option "Capture Global Win32" aktivieren müssen

Debugging über Pipeline Tracing

Eine andere Möglichkeit stellt die Funktion Pipelinetracing zur Verfügung, bei der der Transportdienst die Mail eines bestimmten Absender oder Empfängers als EML-Dump in ein Verzeichnis schreibt und dabei für jede Station durch die Transport-Engine eine eigene Version anlegt.

Set-TransportServer servername -PipelineTracingSenderAddress absender@domain.tld -PipelineTracingEnabled $true -PipelineTracingPath c:\pipeTrace

Damit kann ein Admin dann die EML-Datei per Notepad öffnen und anzeigen. Weitere Details finden Sie hierzu auf Pipelinetracing

Debugging über das Eventlog

Ein Pipelinetrace wird man nur bei bekannten Fehlern ansetzen und DebugView wird auch kaum jemand permanent mitlaufen lassen. Das Eventlog ist ein idealer Platz, in dem Anwendungen informelle Meldungen aber auch Warnungen und Fehler hinterlassen können. Diese erlaubt einer Firma auch die zentrale Überwachung der Prozesse, wenn der Entwickler denn nicht vergisst, die verschiedenen Events samt deren Bedeutung zu dokumentieren.

Mit jeder neuen Windows Version reduziert Microsoft immer mehr die Berechtigungen der Prozesse. der HubTransport-Prozess läuft im Gegensatz zu den anderen Exchange-Diensten sogar nur noch als "Netzwerkdienst"

Auch wenn es naheliegend ist, durch folgende Zeile einen Eintrag ins Anwendungs-Eventlog zu schreiben, so werden Sie damit nur eine Schutzverletzung provozieren.

(Wohl dem, der mit Try/Catch diese abgefangen hat).

EventLog.WriteEntry("mail2displayname", "Start Factory", EventLogEntryType.Information, 1);

Dummerweise funktioniert das ab Windows 2008 nicht mehr, da hier der "Network Service" einfach nicht mehr die Berechtigungen hat.

If your agent writes to the application log, you must grant the Network Service Full Control over HKLM\System CCS\services\Eventlog key.
Quelle: How to write Exchange 2007 transport agents
http://blogs.technet.com/b/exchange/archive/2006/12/05/431755.aspx

Damit bekommt aber der "Network Service" definitiv zu viele Rechte. Besser ist es hierbei ein Umweg über einen EventlogInstaller, welcher für die Anwendung den Zugriff auf das Eventlog für die eigenen Einträge zulässt. Darauf hat mich Henning gebracht:

Wenn du in deinem Programm einfach versuchst einen EventLog Eintrag zu erstellen und die Quelle existiert nicht, dann versucht die EventLog Klasse die Quelle on the fly zu registrieren - und dafür braucht man Admin Rechte.
Quelle: Henning Krause www.infinitec.de

Also sollte man als guter Entwickler, der sich am Konzept der minimalen Rechte orientiert, bei der Installation die Dinge erledigen, die man als Admin eben tun muss, damit das Programm später diese Rechte nicht benötigt.

  1. EventLogInstaller-Klasse
    http://msdn.microsoft.com/de-de/library/system.diagnostics.eventloginstaller.aspx
  2. .NET Framework Class Library Installer Class
    http://msdn.microsoft.com/en-us/library/system.configuration.install.installer%28VS.71%29.aspx
[RunInstaller(true)]
	public class AgentEventLogInstaller : Installer
	{
		private EventLogInstaller eventLogInstaller;
		public AgentEventLogInstaller()
		{
			eventLogInstaller = new EventLogInstaller();
			eventLogInstaller.Source = "agentname";
			eventLogInstaller.Log = "Application";
			Installers.Add(eventLogInstaller);
		}
	}

Damit allein ist es aber nicht getan. Der Code wird natürlich nicht aufgerufen, wenn die DLL einfach auf den Exchange Server kopiert und registriert wird. Über das .NET-Werkzeug "InstallUtil" kann der "RunInstaller-Teil der DLL aufgerufen werden.

C:\Windows\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe 
   "C:\Program Files\Microsoft\Exchange Server\TransportRoles\agents\mail2displayname\mail2displayname.dll"

Dann kann auch der TransportAgent zumindest in das Application Eventlog Meldungen schreiben, solange die Quelle dem "agentname" entspricht.

3rd Party Transportevent Agenten und Beispiele

Exchange 2010 Service Pack 1 (SP1) Transport Agents Software Development Kit (SDK)
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=99989695-5116-4ec6-93c3-8395de84996d

3rd Party Produkte, die auf Transport Agenten aufsetzen

Alternative: Transportregel

Es muss nicht immer ein Transportagent sein. Ein TransportAgent ist natürlich erforderlich, wenn eine Mail quasi "Realtime" verarbeitet werden soll. Exchange startet ihren Code und Sie haben recht viele Freiheiten. Aber es gibt auch Dinge, bei denen ein Transport Agent nicht so gut ist, z.B. wenn asynchrone Aktionen auszuführen sind. vor allem wenn ein Queueing fehlt. Ein paar Beispiele.

  • Anhänge auf einem UNC-Pfad speichern
  • Informationen von anderen Systemen einbinden

In beiden Fällen muss ich fragen, wie Sie den Fall behandeln, wenn der UNC-Pfad oder das andere System nicht erreichbar ist. Auch die Skalierung ist zu hinterfragen, wenn jede Mail durch ihren Code muss.

Gerade wenn es um "asynchrone" Verarbeitung geht, gibt es durchaus interessante Alternativen. So könnte eine Transportregel Mails nach bestimmten Kriterien einfach zu einem anderen Mailsystem (Entsprechender SendConnector oder ForeignConnector vorausgesetzt) weiter routen. Dort könnte dann ein Windows SMTP-Dienst die E-Mails einfach in ein DROP-Verzeichnis fallen lassen., welches dann von einem anderen Prozess "Asynchron" abgearbeitet wird. Die verarbeiteten Ergebnisse sendet dieser wieder per SMTP zurück, addiert aber einen Header, so dass die neue Mail nicht noch mal von der Regel erwischt wird.

Genauso gut könnte eine Transportregel die Mail in ein Postfach zustellen, welches wieder von einem Programm asynchron ausgelesen wird und die Verarbeitung startet.

Weitere Links