As noted in the earlier E12 Developer’s Roadmap blog post, managed agents (or “Transport Agents”) have replaced transport event sinks and EDK Gateways in Exchange Server 2007. Exchange 2007 transport rules are very flexible, so why bother writing a transport agent? There are still some configuration options not covered by rules. Actually, transport rules are really just built-in transport agents. Both Edge and Hub Transport servers have these built-in agents. Custom agents give you almost unlimited options when mail passes through the transport pipeline.
Suppose you want to send all inbound mail for a certain domain to a single mailbox? For some small organizations that is an attractive solution. An SMTP transport agent is the answer. Be careful with custom agents, though. Agents have full access to all messages they process. Agents that have performance or security flaws can affect your server in ways you might not anticipate. Extensive lab testing is a must. Now that the disclaimer is out of the way, let’s dig into creating, installing, and troubleshooting an agent.
First of all, a transport agent requires Exchange Server 2007 configured with the Edge or Hub Server Roles. You will also need the .NET Framework 2.0 SDK, and Visual Studio 2005.
Decide whether you want to write an SMTP receive agent or a routing agent. Routing agents are best for mail internal to your Exchange organization, and SMTP receive agents are best for incoming SMTP mail. Each agent type defines a specific set of events that are available in the context in which they run. Visual Studio’s IntelliSense is a big help here.
Setting up the Environment and Writing the Agent
1. In Visual Studio 2005, create a new Windows C# project with the Class Library template.
2. Copy Microsoft.Exchange.Data.Transport.dll and Microsoft.Exchange.Data.Common.dll (from the C:\Program Files\Microsoft\Exchange Server\Public directory) on the Exchange 2007 server to a directory on the machine you are running the IDE on (VS 2005). Typically I’ll copy to the \debug directory for the project. Although the .Common assembly isn’t required to build a very simple agent, it has useful namespaces in it such as the Data.ContentTypes,Tnef , Data.TextConverters namespace, and Data.Mime to name a few.
3. In VS 2005 Solution Explorer, expand your project, right-click “References” and Add reference… browse to the two DLLs you copied locally, and add them.
With beta builds of Exchange 2007, a transport agent project depends on the build of the assemblies you use- in other words, if you build the project with a certain build of the above-mentioned DLLs, your class library will only run on an Exchange server of that build. If your Exchange 2007 Server gets upgraded, you need to copy the new version of Microsoft.Exchange.Data.Transport.dll and Microsoft.Exchange.Data.Common.dll locally, remove the reference to the old dll and add a new reference to the new DLLs, rebuild the project.
This should hold true only until Exchange 2007 releases. After that point, it’s my understanding that Exchange will allow earlier builds of agents to run on later builds of Exchange. So if and when Exchange Server 2007 SP1 comes out, from what I know at this point, we shouldn’t have to rebuild our Exchange 2007 transport agents.
4. Now for the coding. You’ve got the project with the added references ready. Now add using directives for these DLLs (see sample code below).
In your code, if you are using an SMTP Receive agent, implement the following derived classes that inherit from these classes:
If you are writing a routing agent, your classes must inherit from these two classes:
These agent factory and agent classes provide base properties and methods for accessing transport events and messages. In the agent factory derived class, override the CreateAgent method so that it returns a new instance of your agent class. Here is a sample routing agent that adds the administrator as another recipient if mail is sent to jimbob. The mail that gets to jimbob does not say that it was also sent to the administrator, unless jimbob would look at the full message headers. This agent also writes an event to the applicaton log.
Please see the blog post attachment (RoutingAgent1.cs at the bottom of this post) for the sample agent code.
A sample SMTP receive agent can be found here.
5. Use the Exchange Management Shell to install and enable the agent:
Install-TransportAgent -Name “SteveRouting” -TransportAgentFactory “SteveRoutingAgent.SampleRoutingAgentFactory” -AssemblyPath “C:\myagents\SteveRoutingAgent.dll”
6. Exit the shell to complete the installation.
7. Then run:
Enable-TransportAgent -Identity “SteveRouting”
(view installed/enabled agents with get-transportAgent)
8. Restart the MSExchangeTransport service.
If your agent writes to the application log, you must grant the Network Service Full Control over HKLM\System CCS\services\Eventlog key.
If using an SMTP receive agent, to test your agent after installation, you may need to allow telnet tests from external domains. To do so, modify the default SMTP Receive Connector at:
ADSIEDIT\Configuration\Services\Microsoft Exchange\<org name>\Administrative Groups\<your AG>\servers\<your server>\Protocols\SMTP receive connectors\default connector.
Allow anonymous user full control (you could get more granular than that).
Troubleshooting Transport Agents
The application log is the best place to start. If you agent doesn’t run correctly, it may cause the Transport service to crash. Look for errors/warnings from the MSExchangeTransport service for clues to the problem.
Pipeline tracing can help you verify that your agent is acting as you intended. Pipeline tracing copies the complete contents of e-mail messages that are sent from the e-mail account that is configured by using the PipelineTracingSenderAddress parameter on the Set-TransportServer cmdlet. Set appropriate permissions on the PipelineTracingPath folder in a production environment to avoid possible exposure of confidential information. Here’s how you enable pipeline tracing from the shell:
[MSH] C:\>Set-TransportServer servername -PipelineTracingSenderAddress firstname.lastname@example.org -PipelineTracingEnabled $true -PipelineTracingPath c:\pipelineTracing
When a message comes through the pipeline from the PipelineTracingSenderAddress, .eml files are created in the C:\pipelinetracing\MessageSnapshots\<GUID> directory from each stage in the transport pipeline. I traced my routing agent and in the last .eml file I see:
X-EndOfInjectedXHeaders: MessageSnapshot-End injected headers
Opening the message headers can also help troubleshoot problems. In this case, opening the message (with Outlook Express), File, Properties, Details tab shows the following info:
X-CreatedBy: MessageSnapshot-Begin injected headers
X-EndOfInjectedXHeaders: MessageSnapshot-End injected headers
Using Pipeline Tracing to Diagnose Transport Agent Issues
How to Enable Pipeline Tracing
Overview of Transport Agents
Transport Agent Commands
Overview of Transport Rules