Ruminations of idle rants and ramblings of a code monkey

TFS Work Item Batch Updater

Code Sample | TFS | TFS 2010
TFS is a great tool – don’t get me wrong – but there are some things conspicuously missing from TFS out-of-the-box and the TFS Power Tools. One of these is a way to update work items published to a large number of projects. Certainly, you can use the witadmin command-line tool to import work items or, if the command-line frightens you, you can use the Work Item Import tool in the TFS Power Tools. While both of these work fine, they only import a single work item into a single project. And here’s where TFS fails the TFS administrator that is responsible for an implementation with many projects – you could spend all day importing a single work item across 100 projects. And if you have to import multiple work items? Well, there goes your vacation. So … let’s break this down. You have a highly repetitive task and differs by only a couple of variables that you want to do over and over and over again. Sounds like a good application of our old friend, the looping construct, right? In fact, it certainly is. Let’s start with what we need to do to import a single work item into a single project: private void ImportWorkItem(string workItemFilePath, Microsoft.TeamFoundation.WorkItemTracking.Client.Project project) { //Need to get the raw XML for the work item. var fileInfo = new System.IO.FileInfo(workItemFilePath); string workItemXml = String.Empty; using (var reader = fileInfo.OpenText()) { workItemXml = reader.ReadToEnd(); reader.Close(); } //Import ... project.WorkItemTypes.Import(workItemXml); } Really … it is that simple. The fun part comes when you want to take this simple concept and wrap it up in a nice pretty bow that allows a TFS Admin to update multiple work items across multiple projects. It’s not hard … except for the fact that I am UI-challenged and anything that is functional and looks somewhat usable is not my strong suit. But … I did manage to get it done. Here it is: Running the tool is pretty straightforward. Start the EXE. It will then prompt you to select a Team Project Collection, which gets you to this screen. Use “Browse…” to select the work item files that you want to import. These should be in the same format as you would use in the project template. Then you select the projects that you want them imported into. Hit OK and away it goes. It will take a few minutes, so go get yourself some coffee. Or Mountain Dew. Then relax and surf the web a bit … after all, you did tell the boss that this was a tedious, time-consuming process right? You can download this tool on MSDN Code Samples.

Exporting Work Items for a Project Template

There comes a time in every TFS implementation where you need to create your own set of default project work items in your template. You can do this straight in the XML by editing workitems.xml in your process template’s Work Item Tracking folder. I said you could, not that you’d actually want to! You can also use the Process Editor to add them one at a time like you do with the Team Foundation client. Uggh. Wouldn’t it be better just to use Excel to put them into a scratch, temporary TF Project? I certainly think so; I can enter work items in Excel a lot faster than I can in the TF Client (or in the Process Editor). I did some looking about thinking that someone must have solved this already and posted some code, open source, whatever. There was one result that looked promising but … the URL didn’t work (so I’m not posting it here). <Heavy Sigh> Oh well, time to pop open Visual Studio and start coding … It turned out that it was pretty easy, in fact. In the sample below, you take a raw work item query (in WIQL) and get the list of work items that you want to export with the columns that you also want to export. The order doesn’t matter. From there, you execute the query against the Work Item Store to get the list of work items. Loop over them and write to XML in the format that the project template uses. It is, for the most part, really that simple. There are only 2 things that you really have to watch out for. First, the project name should be replaced with a marker for the target project name  ($$PROJECTNAME$$). Second, you need to make sure that the field is applicable to the work item type. This is something that you need to be especially aware of if you have multiple work item types in your default work item set. With all of that … I am proud to present to you … the actual code (exception handling removed for clarity … put yours in!) public void Export( string serverName, string projectName, string filePath, string WIQL) { TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer( serverName, new UICredentialsProvider() ); tfs.Authenticate(); WorkItemStore store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore)); WorkItemCollection workItems = store.Query(WIQL); using (System.IO.Stream output = System.IO.File.Open(filePath, System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite)) { using (XmlWriter writer = XmlWriter.Create(output)) { writer.WriteStartDocument(true); writer.WriteStartElement("WORKITEMS"); WriteWorkItems(projectName, workItems, writer); writer.Flush(); writer.Close(); } } } private static void WriteWorkItems(string projectName, WorkItemCollection workItems, XmlWriter writer) { for (int i = 0; i < workItems.Count; i++) { var workItem = workItems[i]; writer.WriteStartElement("WI"); writer.WriteAttributeString("type", workItem.Type.Name); foreach (FieldDefinition field in workItems.DisplayFields) { if (workItem.Fields.Contains(field.Name)) { writer.WriteStartElement("FIELD"); writer.WriteAttributeString("refname", field.ReferenceName); writer.WriteAttributeString("value", workItem.Fields[field.Name].Value.ToString().Replace( projectName, "$$PROJECTNAME$$")); writer.WriteEndElement(); } } writer.WriteEndElement(); } } In the immortal words of Porky Pig … That’s all folks!

Desktop.ini, Process Templates and TF30169

I’ve been working on customizing a process template here. Nothing special there. I’m sure you know the process – get the template, tweak, adjust, tweak – upload to the (test) server – create a project and make sure that all looks OK. Well, while I was doing this today, I came across an mysterious error when creating the new project. Keep in mind that the template uploaded just fine … this was when selecting the template for a new project. Here’s the error: Event Description: TF30169: The New Team Project Wizard was unable to download the process template My Process Template. Exception Type: Microsoft.VisualStudio.Zip.ZipException Exception Message: A file name specified within the zip file is invalid. It contains a desktop.ini file, which could be used to run authorized code. A desktop.ini file? What on earth would that be doing in there? I suspect that this is a typo; I think they meant which could be used to run unauthorized code. Don’t get me wrong – this is a Very Good Thing. It shows that someone was thinking about the types of files in the templates and making sure that nothing harmful accidently got in there. What happened, as you may suspect, is that there was a desktop.ini file in the folder for the process template. How it got there or why is a mystery but there it was. And, apparently, when the process template is uploaded, everything in that folder on down is also uploaded. You can see the contents of the process template as it is returned from the server in your local temp folder. If the project creation succeeds, it appears that this file is deleted so you will have to be (relatively) quick about it if you want to see it. The template will have a name like 345.tmp; a number + a .tmp extension. It’s a zip file (as the error message so nicely points out) so you can change the extension to .zip and open it up. When I did this – lo and behold – there certainly was a desktop.ini file in there. Deleting it from the template directory and re-uploading the file did, of course, solve the problem. One lesson that I want to pass on from this – when you upload a new process template using the Process Template Manager, it uploads the entire contents of the process template folder. So make sure that you don’t have any extraneous stuff sitting in there (like desktop.ini, for example!).

TF209001: NullReferenceException in TFBuild Logger with Custom Task

.NET Stuff | TFS
This has been driving me absolutely nuts for … oh … a month or so (off and on). Here’s the scenario: I’ve got a custom task that access the Team Foundation Server to do some things before the build really gets kicking; the task is called in the BeforeEndToEndIteration target, which is one of the ones that are specified as extensible. So … nothing wrong there. The custom task itself changed around the workspace settings for the build so that we could have 1 build for each branch of the code. Switching between the branches is done based on a property that is passed when kicking off the build (/p: switch). This allows one build definition for all of the branches, rather than having a separate build for each branch or manually changing the workspace mappings before kicking off the build. The task itself worked perfectly; the builds pulled and labeled the correct branch. Running the TFBuild standalone, debugging with MSBuild Sidekick as I previously detailed showed no problems and everything was fine. But, when running the build under full Team Build caused errors and the build itself was marked “Partially Succeeded”. Here’s what the build results looked like: Searching for TF209001 gave me nada. Trying to figure out what was going on, I tracked the error to the Team Build logging component (in Microsoft.TeamFoundation.Build.Server.Logger.dll) and the error message was a generic one from a try{}catch{} block in the LogProjectStartedEvent. However, I couldn’t tell much else and talking with some of my friends at MSFT didn’t yield any information either. So it bugged me. I knew that there was nothing wrong with my task … it ran just fine on its own. Even when the error happened, it did what it was supposed to do successfully and the build was actually fine. When the task was removed, everything with the logger was fine and there were no errors. But even when my task didn’t actually remap any of the workspace mappings, the logger ran into this error. It could only be some sort of mysterious interaction between the task and the TF Build logger. Frustrated, I put this on the side burner for a while and just came back to it yesterday, determined to get to the bottom of this mystery and get everything working. Looking through everything in Reflector – something every .NET developer should have in their toolbox – didn’t show anything that really jumped out at me. The only thing that I could possibly guess was that, somehow, in the call to EnsureWorkspace(), the workspace wasn’t getting created. Checking the MSBuild properties WorkspaceName and WorkspaceOwner showed, however, that these were fine and being passed along correctly. Baffled, I finally decided to fire up cordbg and get to the bottom of this. I knew that it was a (caught) NullReferenceException so I set the debugger to break on all NullReferenceExceptions. (cordbg) ca e System.NullReferenceException Next … fire up Task Manager so that I can grab the PID of the MSBuild process that Team Build used to run the build. Then kick off the build and attach to MSBuild. Finally, the debugger stopped on the exception: First chance exception generated: (0x15e37fc) <System.NullReferenceException>   _className=<null>   _exceptionMethod=<null>   _exceptionMethodString=<null>   _message=(0x15e38e4) "Object reference not set to an instance of an object."   _data=<null>   _innerException=<null>   _helpURL=<null>   _stackTrace=(0x15e3960) <System.SByte[]>   _stackTraceString=<null>   _remoteStackTraceString=<null>   _remoteStackIndex=0   _dynamicMethods=<null>   _HResult=-2147467261   _source=<null>   _xptrs=4572092   _xcode=-1073741819 Exception is called:FIRST_CHANCE Now we’re getting somewhere. Let’s see where we are. The where command will give us a stack trace. (cordbg) w Thread 0x2168 Current State:GCUnsafe spot 0)* Microsoft.TeamFoundation.Client.TeamFoundationServer::GetService +0055[native] +0014[IL] in <Unknown File Name>:<Unknown Line Number> 1)  Microsoft.TeamFoundation.Build.Server.Logger.BuildLoggerBase::get_VersionControlServer +0055[native] +0000[IL] in <Unknown File Name>:<Unknown Lin e Number> 2)  Microsoft.TeamFoundation.Build.Server.Logger.BuildLogger::EnsureWorkspace +0317[native] +0000[IL] in <Unknown File Name>:<Unknown Line Number> 3)  Microsoft.TeamFoundation.Build.Server.Logger.BuildLogger::LogProjectStartedEvent +0093[native] +0047[IL] in <Unknown File Name>:<Unknown Line Numb er> 4)  Microsoft.Build.BuildEngine.EventSource::RaiseProjectStartedEvent +0061[native] +0008[IL] in <Unknown File Name>:<Unknown Line Number> 5)  Microsoft.Build.BuildEngine.EventSource::RaiseStronglyTypedEvent +0390[native] +0131[IL] in <Unknown File Name>:<Unknown Line Number> 6)  Microsoft.Build.BuildEngine.EngineLoggingServicesInProc::ProcessPostedLoggingEvents +0213[native] +0180[IL] in <Unknown File Name>:<Unknown Line N umber> 7)  Microsoft.Build.BuildEngine.Project::Load +1608[native] +0845[IL] in <Unknown File Name>:<Unknown Line Number> 8)  Microsoft.Build.BuildEngine.SolutionWrapperProject::ScanProjectDependencies +0319[native] +0114[IL] in <Unknown File Name>:<Unknown Line Number> 9)  Microsoft.Build.BuildEngine.SolutionWrapperProject::CreateSolutionProject +0208[native] +0098[IL] in <Unknown File Name>:<Unknown Line Number> We see the entire stack from MSBuild all the way into the logger. It turns out that the exception is happening in the GetService() method of Microsoft.TeamFoundation.Client.TeamFoundationServer. The stack trace doesn’t show use the actual source lines because we don’t have a pdb for the assembly, but it does show the IL line number, which will get us to what’s going on in ILDASM. Here’s what we find there: IL_000f:  ldfld      class [System]System.ComponentModel.Design.ServiceContainer Microsoft.TeamFoundation.Client.TeamFoundationServer::m_serviceContainer   IL_0014:  ldarg.1 In Reflector, this maps to: object service = this.m_serviceContainer.GetService(serviceType); If GetService() returns null, it’s no problem; that’s handled. But if m_serviceContainer is null … that isn’t handled and will throw an exception as soon as it tries to call GetService(). Now we’ve identified where the exception is coming from. But we still don’t know why. Investigating further, m_serviceContainer is created in the constructor of the TeamFoundationServer class, which is working just fine or we wouldn’t have even gotten this far. So how does m_serviceContainer get set to null? It turns out that it’s set to null in TeamFoundationServer::Dispose(). And only there. Hmmm … how is the TeamFoundationServer object getting disposed? It turns out that I was disposing the TeamFoundationServer class. How did this affect the logger? Simple … both components were calling TeamFoundationServerFactory.GetServer() to return an instance of the TeamFoundationServer class. When I called this method, I wrapped the TeamFoundationServer class in a using{} block (it implements IDisposable) like a good little .NET doobie. A look into GetServer(), however, shows that it creates a cache (using a Dictionary) with a single instance of the TeamFoundationServer for each unique TFS URI. It’s  static and handed to all clients that request the same URI; this single instance lives the entire lifetime of the process and is shared around. (Considering how long it can take to initialize the session context, this isn’t a bad thing.) However, because of this, when I called Dispose(), it was on the same instance that the distributed logger was using! (BTW … this is not documented anywhere that I could find!) The solution – somewhat counter intuitively – is to not dispose the disposable TeamFoundationServer object when you are done with it. (Don’t make a habit of it though, OK?) Once I took out the using{} block, all was well and the build ran without any hitch at all. This is only applicable to instances that are retrieved from TeamFoundationServerProxy.GetServer(); if you create the instance using new, then you should make sure that you dispose it when you are done. This smells all kinds of wrong to me. I don’t know what the official stance would be on this, but it smells suspiciously like a bug to me. The first thing is that the TeamFoundationServer class isn’t checking to see if it is disposed before it goes about its happy, merry way, contrary to what I thought was the recommended design pattern for implementing IDisposable; this includes checking to see if the class is disposed before doing any work in a method … and throwing an ObjectDisposedException when a method is called after being disposed. If you don’t do this, then be smart enough to realize when you’ve been previous disposed (somewhere) and reinitalize when necessary (ewww … no, that’s just wrong. Don’t do that.) So there it is, the end to the mystery.

Debugging TFS Build

If nothing else, this is one of the coolest things in TFS 2010. But I am not, unfortunately, afforded the luxury at this time of working with TFS 2010 in my current gig. Though I won’t complain too loudly as it is 2008 and not 2005. I’m currently working on the build process. As simple as that may sound (and some builds are simple), I do have some pretty high goals. Let me explain. The Team Project is a collection of solutions and projects that are certainly related and tied together in a single software project, but there are 10-15 of them. We have, in Source Control, branches for each release environment and thus, will need to have builds for each environment as well. My ideal scenario would be to pass the environment into TFS Build as a command line argument and have it pull the project from the correct branch, label the project from the correct branch and then create the drop directory with the correct name from the correct branch. And then zip it as well, but that getting that to have the correct branch/environment name is pretty easy as it’s a task that I’m adding in, not changing the behavior of Team Build. While there is a build debugger in TFS 2010, such a beastie does not come out-of-the-box with TFS. There is, however, MSBuild Sidekick out there that lets you edit and debug MSBuild projects. Since TFS Build projects are MSBuild projects, I figured that this would be a good place to start. So I downloaded the trial with high hopes. I started with simply opening the MSBuild project that TF Build creates when you kick off a build. You’ll find this in the BuildType folder that’s right under the working directory for the Build Agent. It defaults to the Temp folder, but I like to put it in it’s own spot. On the Build Server that I’m working on, it’s F:\Build so you will find your TFSBuild.proj file in F:\Build\BuildType. Simple enough, right? No. When you fire up the debugger, it starts with CanonicalizePaths, then BeforeCompile and then CallCompile. And there it fails. Digging in a bit, you’ll see a second file in the folder with TFSBuild.proj – TFSBuild.rsp. Hmmm … interesting. Upon opening that up in Notepad, you’ll find a list of stuff that looks remarkably like a series of command-line switches. Guess what? That’s exactly what they are. These are all of the command-line arguments that are passed into MSBuild when the build is kicked off. There are a slew of properties, some loggers and a couple of other things – like the initial target for the build. That’s a little important. Next step – add these into MSBuild Sidekick. You do this in the Properties screen for the build project. First, under project properties, you need to add in ALL of the properties that TFBuild is passing in. Then you have to specify the initial target for MSBuild. They’ll look like this: When you run it now, it hits CanonicalizePaths and then jumps right to the end and happily informs you that “Build succeeded”. Oh boy. What’s up with this? As it turns out, the first target in the chain for TF Build is “CheckSettingsForEndToEndIteration”. There’s a condition on this - $(IsDesktopBuild)'!='true'. So, let’s now add a property to the command line in our build options: IsDesktopBuild with a value of False. And … here we go again. We get past “CheckSettingsForEndToEndIteration” and go into the “InitializeBuildProperties” target, which calls the GetBuildProperties task. Which then promptly fails with the message: C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets(267,5): error MSB4018: The "GetBuildProperties" task failed unexpectedly. C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets(267,5): error MSB4018: Microsoft.TeamFoundation.Build.Client.BuildNotFoundForUriException: TF214007: No build was found with the URI vstfs:///Build/Build/34. Either the URI does not exist, or TFSTEST\Administrator does not have permission to access it. C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets(267,5): error MSB4018:    at Microsoft.TeamFoundation.Build.Client.BuildServer.GetBuild(Uri buildUri, String[] informationTypes, QueryOptions queryOptions) C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets(267,5): error MSB4018:    at Microsoft.TeamFoundation.Build.Tasks.Task.get_BuildDetail() C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets(267,5): error MSB4018:    at Microsoft.TeamFoundation.Build.Tasks.GetBuildProperties.get_BuildAgentName() C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets(271,15): error MSB4028: The "GetBuildProperties" task's outputs could not be retrieved from the "BuildAgentName" parameter. TF214007: No build was found with the URI vstfs:///Build/Build/34. Either the URI does not exist, or TFSTEST\Administrator does not have permission to access it. And that, my friends, is where I am now. BUT … at least MSBuild Sidekick allowed be to look at all of the properties as they are when it starts up. And it’s also shown me what the entire process is that TF Build actually goes through. I haven’t done much editing with MSBuild Sidekick (yet) but the debugging … even if I cannot get TFBuild to cooperate fully … is priceless. UPDATE: I’ve gotten it working from beginning to end, soup to nuts. :-) Here’s what I did: First, I logged in as the user that the Build Server runs under (don’t lecture me about security; it’s a test environment). I did this because I remember seeing an error message when I tried to change the Build Server’s account about access to some URL or another (I don’t remember the details … I just switched back to the previous account). That still didn’t work. Therefore, I knew that it wasn’t security permissions; that account would need those permissions of Team Foundation Build was going to work at all. So now I’m looking at “No Build Found”. Time to fire up Sql Server Management Studio to see what’s in the TfsBuild database. When I opened up the tbl_Build table (what’s with the “tbl” prefix??? That’s sooo pre-Y2K!), I noticed that there was no build number 34 (look at the URI … it’s in there). BUT … there was a 33 and a 35. Changing the BuildUri property in the BuildOptions (above) did the trick and I was happily stepping through the entire TFS Build process.

TFS & Reporting Services Installation Fun

Maybe it’s me … maybe SSRS just doesn’t like me. But that has always seemed to be the most difficult piece of the whole Team Foundation Server integrated solution. But, now that I have that out of the way … I had challenges installing TFS into the production environment. Yes, I read the latest installation guide. I print out a checklist and go over it twice. I am well aware (from many bouts of swearing) that it is very important to make sure that you have everything in a row when installing TFS. Granted, it’s not as bad with 2008 as it was with 2005 (which was an improvement over the betas!!!), but it’s still something that you need to do. Everything is checked. All the prereqs are installed. Did have one warning with the system scan though – it said that the processor on the app tier server wasn’t up to the recommended clock speed. Which was true. But there wasn’t just one processor … it has 4 Intel Xeon hyperthreaded processors in there! So I did ignore that. Heading off to the races, we’re getting into the final stages of the TFS install … it’s installing Reporting Services. And it’s failing. Is it a security thing? Go check permissions for the account, make sure all is kosher there (it was). Still no go. What gives? As it turned out, the backend Sql Server already had a default instance of Reporting Services registered to it (i.e. databases, etc) and I had not used an instance name for the TFS installation of reporting services on the app tier. Of course you have to have different instance names on the same box, but you also have to have different instance names from any Reporting Services instance that is registered/using your Data Tier server! I looked in the docs again and again … didn’t see it there. So, word to the wise: when installing Reporting Services for TFS on your App Tier server, check to make sure that there aren’t any instances of Reporting Services associated with your Data Tier server! If so, specify an instance name!

Installing TFS Build on XP/Win2K3 x64

Here I was happily installing a test environment for TFS. One of the requirements for this environment is x64 so, of course, I’m installing as much as possible on x64. Of course, that does not include the TFS Application Tier … it’s 32-bit only and, while there are hacks out there to get it to install on x64, I’m avoiding that as a) it’s not supported and b) they require manually hacking the MSI file, something that I think is a Very Bad Idea. Part of this was installing TFS Build Services in Win2K3 x64; Win2K8 isn’t blessed in this environment, only Win2K3. In looking at the TFS Installation Guide, it does clearly say that TFS Build will run on x64 on WOW64 mode. And it does. But … you may run into a little issue with installing TFS Build on x64. It’s the same issue described in this forum post. In shorts, here’s what happens: You’re cruising along in the TFS Build install and all is hunky-dory. You then get this error: Microsoft Visual Studio 2008 Team Foundation Server Build Setup Error 32000.The Commandline '"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe" -ga "[Domain]\[Account]"' returned non-zero value: 1. Running the command manually gives you this: [c:\windows\\framework\v2.0.50727]aspnet_regiis.exe -ga "[Domain]\[Account]" Start granting SFINTERNAL\tfsbuild access to the IIS metabase and other directories used by ASP.NET. An error has occurred: 0x800703f0 An attempt was made to reference a token that does not exist. Keep in mind that this is IIS 6.0 on x64. By default, it’s running in 64-bit mode. And it won’t allow 32-bit applications. But … you are running aspnet_regiis from the x86 (32-but) installation folder. Hmmm … a clue perhaps? As it turns out, that’s the key to the solution. Here’s what you need to do (and it does not include a manual install): flip IIS 6.0 into 32-bit mode. Unlike IIS 7.0, IIS 6 does not support running both 32-bit and 64-bit applications at the same time. To do this, running the following from the command line: cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnWin64 1 When the installation is finished, you can flip IIS back to 64-bit mode – this seems to work just fine (so far, at least) by change the 1 to a 0.

TFS Workgroup to Active Directory Migration Comments

Moving your TFS installation from a workgroup to an Active Directory domain? Good for you! Honestly, you shoulda started with the domain anyway but we’ll not go there right now. Maybe you didn’t have the domain in place in the beginning. I’ve been playing with the process. Well, I wouldn’t say playing as I’m not into inflicting pain on myself. First step, of course, is to RTFM. There’s a decent article on MSDNon how to do that. When you read the article, it seems pretty straightforward. Not necessarily simple – it’ll make you wish you’d started out in a domain – but straightforward. Follow the steps and all should be good, right? Well, it’s all good on the TFS side. When you get to the end and you’re thinking to yourself “Phew … almost done with this” you come across this little tidbit at the end of the article: No tools are available to automatically change SharePoint Products and Technologies and Reporting Services users and groups and their role memberships from local accounts (used in workgroups) to domain accounts. Although the local accounts will still work as local accounts, you might want to take advantage of the flexibility and management of Active Directory groups. Both SharePoint Products and Technologies and SQL Server Reporting Services will show the users and groups and their role memberships for each site or report folder. You can populate SharePoint Products and Technologies and Reporting Services to use new or existing Active Directory groups depending on your new deployment. It’s so small that, in the context of the article, you just might miss it. And remember – you can’t edit permissions for SharePoint or Reporting Services from TFS Explorer; that’s why they have the TFS Admin tool. And that’s exactly what you have to do … go project by project and set the permissions for SharePoint and Reporting Services. If you have a lot of projects, this gets to be mind-numbing. This is a VERY good reason to use groups for your management!!! Even if they are local groups, you can put the domain members in there and be done with with … no TFS Admin tool required (except for setting up the initial permissions). But wait, there’s more. The local machine administrators group doesn’t have access to any of the SharePoint sites for Team System!! So you have to log in to TFS with a local account that has admin rights on the SharePoint sites. And is a server admin on TFS as well. Then you can go about fixing up the permissions. One more thing – I was adding the local machine Administrators group to every SharePoint (Full Control) and Reporting Services (Content Manager) site. You cannot add BUILTIN\Administrators as the group into the TFS Admin Tool (it gives you an error) … you have to add the group as “.\Administrators”. But SharePoint doesn’t like that syntax for the local administrator’s group and fails when you try to apply permissions. Once you refresh the view. you’ll see the Administrators groups listed as “BUILTIN\Administrators” (then why couldn’t I just add it like that??). You can then set Full Control permissions for the SharePoint site and apply your changes. I really hope that they improve the permission/security management story in TFS 2010. I’ve not seen anything about that, but it sure would be nice. Edit: Forgot to mention this … you’ll also need to change the account for the SharePoint Timer Service to the domain account for the TFS Service. If you don’t, OWSTIMER.EXE will very happily eat your CPU cycles. Once I changed the account to the domain TFS Service account, all was good.