The Visual Studio Alerts Explorer that comes with TFS Power Tools provides a great GUI for configuring email alerts on all kinds of events generated by TFS. However, it is not problem-free.
A little while back I came across a bug, in Alerts Explorer or the TFS events system, which had a very irritating effect – spam! It took a good while to diagnose but a fairly simple workaround was found. Here’s the scenario…
Imagine you want to receive an email whenever a work item is assigned to you – a very common requirement. The obvious way to set it up is like this:

This alert definition should send an email to Joe Bloggs whenever a work item is assigned to him, except for when he assigned it to himself. And it will do exactly that, most of the time. Unfortunately it will also send an email when a link is added to a work item already assigned to Joe Bloggs, with no other changes made at the same time. Imagine that another user is linking a handful of work items, one at a time, to another work item that is with Joe Bloggs. Poor old Joe will receive one email for every link added!
To diagnose this issue it’s necessary to understand the TFS eventing system. It basically works as follows:
- Every time an event happens (the “WorkItemChangedEvent” in this case) an XML document is generated by the TFS Eventing system.
- TFS sends the XML to all subscribers to that event, which in this case includes the Alerts system.
- The Alerts system applies the filter defined for each alert to the event XML to identify which alerts are relevant.
- For each alert match, an XSLT transformation is applied to convert the XML to HTML, and an email is fired off.
So far, so good. So why does this alert also fire off emails when the only thing changed is a work item link? There are two parts to this puzzle. The first is the Filter Expression for the alert. Alerts Explorer handily exposes the underlying filter generated by the GUI on a second tab:

This is actually a partial XPath expression, which is applied against the event XML. If there’s a match then the alert is sent. This filter amounts to a query that (to paraphrase) says CoreFields/AssignedTo/OldValue is not “Joe Bloggs” and CoreFields/AssignedTo/NewValue is “Joe Bloggs” (the ChangedBy field isn’t part of the problem, so I’ll ignore it).
The second part of the puzzle is the event XML itself. Unfortunately this is much more difficult to come by than the Filter Expression. There are various methods by which you could get at the XML, but I found a simple solution was to replace the XSLT transformation which would normally generate a nicely formatted HTML email message with one that simply spits out the source XML as HTML, so you can see it in the resulting email. Of course you shouldn’t do that in a production system, as your users are unlikely to appreciate emails containing raw XML!
Quick instructions on how to replace the alert XSLT:
- On the TFS application tier go to this folder (or equivalent) – C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\TFSJobAgent\Transforms
- Rename WorkItemChangedEvent.xsl to WorkItemChangedEvent.xsl.bak
- Download XMLToHTMLVerbatim.xsl (Copyright 2002 Oliver Becker) copy it into the folder and rename to WorkItemChangedEvent.xsl
- Edit and save work items and inspect the resulting emails (there may be a two minute delay as alerts are queued)
With that in place you can see that the XML generated when only adding links to a work item looks like this:
<WorkItemChangedEvent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
...
<Subscriber>UK\JoeBloggs</Subscriber>
...
<ChangeType>Change</ChangeType>
<CoreFields>
<IntegerFields>
<Field>
<Name>ID</Name>
<ReferenceName>System.Id</ReferenceName>
<OldValue>0</OldValue>
<NewValue>192</NewValue>
</Field>
<Field>
...
</IntegerFields>
<StringFields>
<Field>
<Name>Work Item Type</Name>
<ReferenceName>System.WorkItemType</ReferenceName>
<OldValue />
<NewValue>Bug</NewValue>
</Field>
...
<Field>
<Name>Assigned To</Name>
<ReferenceName>System.AssignedTo</ReferenceName>
<OldValue />
<NewValue>Joe Bloggs</NewValue>
</Field>
<Field>
<Name>Changed By</Name>
<ReferenceName>System.ChangedBy</ReferenceName>
<OldValue />
<NewValue>Fred Smith</NewValue>
</Field>
...
</StringFields>
</CoreFields>
<AddedRelations>
<AddedRelation>
<LinkName>Affects</LinkName>
<WorkItemId>358</WorkItemId>
</AddedRelation>
</AddedRelations>
</WorkItemChangedEvent>
Even though the assignment has not changed – the work item has been with Joe Bloggs the whole time – the OldValue for AssignedTo is empty. The NewValue correctly says Joe Bloggs, so the filter is met and an alert sent! The eventing system detects the change but the XML has inaccurate values, so it appears that a different change occurred (I have reported this on Microsoft Connect).
The workaround (which is thankfully much simpler than the investigation) is to modify the alert definition to also reference the XML’s ChangedFields. These fields are not populated in the example above, but are present in every other type of work item change. It seems a little odd that Alerts Explorer does not reference these fields when you use the Changes To or Changes From operators, because it does when you use the Changes operator. So all you need to do is add a Changes operator to the alert definition like so:

The filter expression now includes AssignedTo within ChangedFields as shown below, ensuring you don’t get the alert unless that field really has changed.

I suspect the fix for this is a change to the TFS eventing system to properly populate the OldValues, or a change to Alerts Explorer to reference ChangedFields when using the Changes To/From operator (or both).
Although it’s painful to debug this issue, it gives an interesting insight into the system and hopefully this post will help others figure out why TFS is spamming them!