RSS

Track TFS group membership changes

08 Mar

TFS provides quite a few automated notifications out of the box. For example, you can right click on the team project from team explorer and navigate to Project Alerts to view and set the available alerts.

Typically we see following options to set alerts


Should we need additional notifications, we can always extend TFS & utilize the event service. TFS offers the option to register, subscribe for event notifications and process the captured events. For detailed documentation on event subscription, please navigate
here.

And also there is an event subscription tool available in
Codeplex.

However, if we look at the available events, there is no solution to track TFS group membership changes. This is one of the critical needs during auditing. So, below is an attempt to address this partially.

The approach that I’ve outlined below would capture the event, process the Sequence Id to read more details on that specific sequence id and log an entry in your event viewer.

First let’s create an event handler

1. Create a web service project



Code behind the Web Service

using System;

using System.Xml;

using System.Net;

using System.Linq;

using System.IO;

using System.Diagnostics;

using System.Security.Principal;

using System.Collections.Generic;

using System.Web;

using System.Web.Services;

using System.Web.Services.Protocols;

using Microsoft.TeamFoundation;

using Microsoft.TeamFoundation.Server;

using Microsoft.TeamFoundation.Client;

Add above references.

Change the Namespace and class name as needed. Here I’ve changed the Namespace to “My_TFSEventCapture” and class to “DataChangedEvent”

namespace TFSDataChangedEventCatch

{


///
<summary>


/// Summary description for Service1


///
</summary>

[WebService(Namespace = "My_TFSEventCapture")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[System.ComponentModel.ToolboxItem(false)]


// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.


// [System.Web.Script.Services.ScriptService]


public
class
DataChangedEvent : System.Web.Services.WebService

{

[SoapDocumentMethod(Action =
"http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify",
RequestNamespace="http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]

 

Following Web method should contain logic to process the event notification. Typically eventXml contains notification data. In our case the eventXml will have a Sequence ID and Data type.

Example:
    
<?xml
version=1.0
encoding=utf-16?>

<DataChangedEvent
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd=http://www.w3.org/2001/XMLSchema>


<
DataType>IDENTITY</DataType>

<SeqId>194</SeqId>

</DataChangedEvent>

Need to validate the Data type and ensure its IDENTITY then process the Sequence Id.

[WebMethod]


public
void Notify(string eventXml, string tfsIdentityXml)

{


//Write your custom code here


//Use eventXml to extract event related fields and their values


XmlDocument myXmlDocument = new
XmlDocument();

myXmlDocument.LoadXml(eventXml);


XmlNode node;

node = myXmlDocument.DocumentElement;


int SeqId = int.Parse(node.SelectSingleNode(“SeqId”).InnerText) – 1;

Now, pass the SequenceId & get the identity changes from IGroupSecurityService2.


foreach (XmlNode node2 in node.ChildNodes)

{


if (node2.Name == “DataType” && SeqId != 0 && node2.InnerText == “IDENTITY”)

{

TfsTeamProjectCollection tpc = new
TfsTeamProjectCollection(new
Uri(“http://TFSDemo:8080/tfs2010″), new
NetworkCredential(“user”, “password”, “domain”));

//try catch here

tpc.EnsureAuthenticated();


IGroupSecurityService2 gss3 = tpc.GetService<IGroupSecurityService2>();


var ChangedIdentities = gss3.GetChangedIdentities(SeqId);

WriteTrace(“IdentityChanges” + “\\n” + ChangedIdentities);

ProcessChangedIdentities(ChangedIdentities);

}

}

}

GetChangedIdentities returns a string like
<?xml version=”1.0″ encoding=”utf-16″?><IdentityChanges MaxSequence=”194″ fMore=”0″><Identities><Identity SID=”S-1-5-21-919044463-614821983-2752388159-1014″ AccountName=”Visitor” DisplayName=”Visitor” DistinguishedName=”WinNT://TFSDemo/Visitor” Domain=”Demo” MailAddress=”" SpecialType=”Generic” Type=”WindowsUser” Deleted=”False” /><Identity SID=”S-1-9-1551374245-4271671090-1142510150-2272580956-3725988686-1-1830461201-1958035529-2898280848-1812300448″ AccountName=”Contributors” DisplayName=”[SampleAgile]\Contributors” DistinguishedName=”" Domain=”vstfs:///Classification/TeamProject/9eb7de28-b3d3-4afe-9ea4-6da938d0c46c” MailAddress=”" SpecialType=”Generic” Type=”ApplicationGroup” Deleted=”False”><Members><Member SID=”S-1-5-21-919044463-614821983-2752388159-1014″ /><Member SID=”S-1-5-21-919044463-614821983-2752388159-1000″ /></Members></Identity></Identities></IdentityChanges>

The result string should be analyzed to determine the type of operation. Key nodes to grasp from the resultant string

1. SID

2. Account name

3. Type (WindowsUser, ApplicationGroup etc)

4. Deleted (True, False)

Typically, the resultant string contains 2 Identities.

For delete operation Deleted will reflect True, False otherwise.

Following logic should construct a message like “User ADDED to SomeGroup” or “User DELETED from SomeGroup”


private
void ProcessChangedIdentities(string ChangedIdentities)

{


string message = null;


XmlDocument myXmlDocument = new
XmlDocument();

myXmlDocument.LoadXml(ChangedIdentities);


XmlNodeList xnList = myXmlDocument.SelectNodes(“/IdentityChanges/Identities/Identity”);

WriteTrace(“Identity change Count “ + xnList.Count);


if (xnList.Count == 2)

{


if ((string)xnList[0].Attributes["Type"].InnerText == “WindowsUser” && (string)xnList[0].Attributes["Deleted"].InnerText == “True”)

{

message = (string)xnList[0].Attributes["DisplayName"].InnerText + ” DELETED from “ + (string)xnList[1].Attributes["DisplayName"].InnerText;

}


else

{

message = (string)xnList[0].Attributes["DisplayName"].InnerText + ” ADDED to “ + (string)xnList[1].Attributes["DisplayName"].InnerText;

}

}


else

{


//something wrong – exception

}

WriteToEventLog(message);

}

Below method is written to write the message to the event viewer into Application node.


private
void WriteToEventLog(string message)

{


//you need to adjust registry settings


//http://social.msdn.microsoft.com/forums/en-US/windowsgeneraldevelopmentissues/thread/00a043ae-9ea1-4a55-8b7c-d088a4b08f09/

WriteTrace(“Attempting to write eventlog”);

WriteTrace(message);


string sSource = “TFS Group membership change”;


string sLog = “Application”;


if (!EventLog.SourceExists(sSource))


EventLog.CreateEventSource(sSource, sLog);


EventLog.WriteEntry(sSource, message, EventLogEntryType.Information);

}

}

}

2. Deploy the Web Service

3. Subscribe to DataChangedEvent

    Use BisSubscribe.exe

    Key inputs to be given

    1. Event type – DataChangedEvent

    2. Address – webservice URL

    3. Server – TFS middle tier URL

    4. Collection – your collection URI

With this, we should be able to record any group membership changes in eventviewer and write another utility to filter, show and export…


Advertisement
 

About tfsrocks

An individual with entrepreneurial drive and can do attitude. About 8 years experienced IT professional with consulting skills in seeding, anchoring and winning proposals. Strong track record in development and articulation of consulting propositions based on industry challenges. Promising Software Configuration and Release Management professional, Working with one of the premier Indian IT companies. Microsoft, Rational certified Software Configuration and Release Management practitioner.
Leave a comment

Posted by on March 8, 2011 in TFS, Uncategorized

 

Tags:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.