Welcome to EMC Consulting Blogs Sign in | Join | Help

James Dawson's Blog

  • Installing TFS 2008 onto SQL Server 2008 SP1 (you can't!)

    This post definitely falls into the 'note-to-self' category - myself and a blog-less colleague wasted a good few hours this morning trying to get Team Foundation Server (TFS) installed onto a new virtual machine.

    If you're in the habit of using slipstreaming to integrate service packs into products' base installers (to save time), which you can do with both TFS 2008 and SQL Server 2008, then be aware that TFS 2008 SP1 will not let you install to a server running SQL Server 2008 SP1 - it needs to be the RTM version.  Attempting to do so (as we did) will result in the TFS pre-install checks failing saying that a suitable version of SQL Server is not available.

    Initially I was convinced that I had previously done an installation with this combination, however, after a couple hours that self-belief was in tatters!  Fortunately, we were able to cleanly uninstall the SQL Server service pack via 'Add/Remove Programs', somewhat surprisingly, with no undue side effects on other databases that had already been deployed. (It's always nice when something works the way it's supposed to!)

    If you want more information on how to create slipstreamed versions of Team Foundation Server 2008 or SQL Server 2008, then you should take look at the following links:

    UPDATE: I knew putting "can't" into the title would ensure I got proved wrong!  Thanks to @StuartPreston for pointing me in the direction of a hotfix that resolves this issue: http://support.microsoft.com/kb/969985

     

  • TFS vs. SVN vs. Git …

    So, there’s been a bit of storm blowing through the part of the InterWeb that I inhabit recently – see http://blog.benday.com/archive/2009/07/27/23233.aspx for the low-down.

    I’ve seen these types of discussions countless times, both online and in the real world – the thing that wears me down the most is the seemingly constant assertion that ‘SVN/Git/Mercurial = TFS’, as if source control is the only consideration.

    Let me lay down a couple points of order before I get flamed to oblivion:

    1. I’ve generalised this assertion, somewhat
    2. I’ve spent time using both SVN and Git and like them both
    3. I have no desire to sound like a Microsoft shill!

    Fundamentally, SVN and TFS are implementations of a centralised version control system (VCS) that are designed using polar-opposite philosophies – TFS is server-centric whilst SVN is client-centric.  The reality is that both of these approaches have advantages and disadvantages in terms of usability and performance.

    Regarding the centralised versus distributed debate, I actually like a lot of things about Git et. al. and I would guess that as the various tooling/integration stories develop we will see more corporate and enterprise adoption.

    Getting to my point though, most of the customers I talk to rarely arrive at the TFS ‘answer’ on the basis of a VCS selection process (those that have, usually get a suggestion from me to look at other open source alternatives), they get there because they are looking for something to satisfy their Application Lifecycle Management (ALM) needs – I consider TFS to be pretty cost-effective in the ALM space.  Granted, there is a sizable chunk of TFS adopters who migrate from Visual SourceSafe (and are pre-disposed to use Microsoft tools) but as a like-for-like swap Microsoft have, in general terms, made that migration relatively painless.

    However, increasingly we’re seeing customers who have arrived at the TFS ‘answer’ because they use Scrum to manage their development projects and have chosen Scrum for Team System (a TFS process template we make available for free) as their preferred project management tool - and from their perspective TFS comes with the added bonus of not having to worry about selecting and integrating a separate VCS and automated build system.

     

  • TFS, Build Servers and Un-Trusted Domains

    I’ve been meaning to write this up for a while now, and a recent Twitter exchange with @thiago_bagua prompted me to finally get around to it.  This post will describe a way of getting a TFS 2008 build server up and running in an Active Directory domain that has no trust relationship with its associated TFS server.

    CAVEAT: Whilst the approach I going to describe below worked for me, your mileage may vary and I doubt that it is an officially supported configuration.

     

    Assumptions:

    • You have a working instance of TFS
    • You have already installed the TFS Build Agent (aka Team Build) on your build server

     

    The diagram below illustrates the basic scenario, with the TFS Application-Tier (AT) and TFS Build Agent residing in separate domains with no trust relationships.

    image

     

    With the above configuration there are basically two problems we need to overcome:

    • The Build Agent that runs as a Windows service called ‘Visual Studio Team Foundation Build’, will need to authenticate with the TFS AT in order to consume the various web services (e.g. source control, work item tracking, publishing build results etc.)
    • The TFS AT will need to authenticate with the Build Agent service in order to be allowed to start a build

     

    Build Agent to TFS Authentication

    Solving the first problem involves storing a saved password in the profile of the Build Agent’s service account, in this scenario ‘DOMAINB\TFSBuild’, and ensuring that the server will use that credential automatically.

    1) Log onto the Build Agent server as the ‘DOMAINB\TFSBuild’ user (or equivalent)
    2) Open the ‘User Accounts’ Control Panel applet (also called ‘Stored User Names and Passwords’ on Windows Server 2003)
    3)

    Select ‘Manage your network passwords’

     image

    4) Add an entry for the server running the TFS Application Tier (you may want to add two, one for the server’s NetBIOS name and another for the server’s FQDN).
    image

    The username you enter here needs to be an account in the ‘DOMAINA’ domain that is a member of the ‘Build Services’ application group in the Team Project(s) the Build Agent will be servicing.
     image

    Here you can see both the NetBIOS and FQDN entries for the TFS server have been added.
    image
    5) Review the Internet Explorer security settings of the ‘Local Intranet Zone’ and ensure that the ‘Automatic logon only in Intranet zone’ is selected.
     image
    image
    6) By default the FQDN of the TFS Application Tier is unlikely to be treated as being in the Intranet Zone, so we need to add it explicitly.
    image
    image
    7) You should now be able to test these changes by opening Team Explorer and verifying that you are able to transparently connect to the TFS server (i.e. you aren’t prompted to enter credentials).

     

    TFS-to-Build Agent Authentication

    Having completed the above, we now just need to solve the second problem.  My approach was to create a local account on the Build Agent with the same username and password as the account used as the identity of the ‘Microsoft Team Foundation Server Application Pool’ on the TFS Application Tier.  This has the effect of allowing the TFS Application Tier to authenticate to the build server using it credentials.

    As an alternative to this, you could setup a saved password, this time on the TFS server, in the profile of the ‘DOMAINA\TFSService’ account for the build agent server that use a domain account in ‘DOMAINB’ or local account on the build server.

     

    At this point you should be able to register the new build agent in your Team Project and queue a build to it.

     

    Troubleshooting

    If you have hit problems whilst trying the queue the build, then this indicates that the TFS Application Tier had a problem connecting to the build agent.  This is typically caused by TFS not being able to authenticate to the build agent or some kind of firewalling preventing the network connection, usually via TCP port 9191.

    If the build starts but quickly fails, then this generally points to either or both of the following:

    1. The Build Agent service account cannot authenticate to the TFS Application Tier (i.e. a problem with its saved username/password).
    2. The saved username/password has not been added to the ‘Build Services’ application group in the relevant Team Project.

     

    Anyway, if you find yourself in this sort of edge case scenario then hopefully the steps detailed here will be helpful.  I'm now wondering whether a similar approach could be used to workaround the issue of not being able to add users, from a Trusting domain, to TFS application groups when there isn't a 2-way trust in place...?

     

  • Using Linked Files with Web Application Projects

    A customer recently contacted me saying that they had upgraded to Visual Studio 2008 and had also taken the opportunity to switch from a Web Site Project to a Web Application Project.  A while back I'd helped them to centralise some of their configuration so that they only had a single copy of all the configuration sections that were referenced by multiple projects (e.g. the web site, various unit test projects etc.).

    This involved creating separate configuration files for each section and referencing them from the main web/app config using the 'configSource' attribute, for example:

      <!-- Reference the shared config file -->

      <connectionStrings configSource="dbConfig.config" />

     

    The above referenced configuration file would something like this:

      <connectionStrings>

        <add name="DB1"

             connectionString="Data Source=.;Initial Catalog=DB1;Integrated Security=SSPI"

             providerName="System.Data.SqlClient" />

      </connectionStrings>

     

    Due to the 'configSource' attribute not supporting relative paths, it was necessary to setup pre-Build events to copy the referenced files into the project folder - especially for the Web Site project where a valid web.config is required by the ASPNET compiler.

    After the switch to the Web Application project model the client was wondering whether they needed to change how they were handling these shared configuration files.  My initial response was to suggest that they discontinue the pre-build scripts and instead use the 'linked files' feature that was available with web application projects (much like they have been for other types of projects a long time).

    For those of you unfamiliar with this useful feature, here's how you add a linked file via the 'Add Existing Item' option:

    image image

    Such files appear in Solution Explorer as normal project items, albeit with a shortcut icon.

    image

    However, after trying it, they reported that those files were missing from the web site which was causing all manner of problems.

    It turns out that when you build the project linked files are not copied into the project folder, which is obviously a problem as it means that the file is not available to the web site at runtime.  Your first thought might be to enable the 'Copy Local' option for the linked files, however, all this does is to copy the linked files to the 'bin' folder... not exactly the result we're after.

    Before carrying on, it's worth looking at how the linked items are stored in the actual web application project file, as it will later help us understand why this issue occurs.

      <ItemGroup>

        <Content Include="..\..\dbConfig.config">

          <Link>dbConfig.config</Link>

        </Content>

      </ItemGroup>

     

    From this you can see the main path for the item (the 'Include' attribute) is a relative path to the shared configuration file, whilst the 'Link' attribute tells Visual Studio where the item exists within the project - in this case, the file was added to the root of the project.

    To investigate further I took a look at the 'Microsoft.WebApplication.targets' file that contains the Web Application project build process and typically lives in the 'C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\WebApplications' folder.  There I found a target called '_CopyWebApplication' which only gets executed when the output directory has been overridden (e.g. what TeamBuild does to redirect all build outputs into a single directory):

      <Target Name="_CopyWebApplication" Condition="'$(OutDir)' != '$(OutputPath)'" >

     

    At the end of this Target there is the following call to the MSBuild Copy task:

      <!-- Copy content files recursively to _PublishedWebsites\app\ folder -->

      <Copy SourceFiles="@(Content)" DestinationFolder="$(WebProjectOutputDir)\%(Content.RelativeDir)" />

     

    So when the above Copy task executes it's going to use the relative path to the linked item as part of the destination path, rather than the path within the project (i.e. the 'Link' attribute) - so this explains why the files aren't getting copied:

    1. When building in Visual Studio, nothing special happens to copy the content files (linked or otherwise)
    2. When redirecting the output path, normal Content files are copied fine (their 'Include' attribute does not contain a relative path), but the linked content files will get copied to a folder with the same relative path to the actual output folder, as the linked item has to the project file (e.g. 'MyOutDir\..\..\dbConfig.config' instead of 'MyOutDir\dbConfig.config')

    Anyway, let's cut to the chase... here is my suggested workaround:

    1. Fix the 'Copy' task to not include linked files
    2. Create another target to handle the copying of linked files using the correct destination path

    To achieve the first, we'll need to override the built-in '_CopyWebApplication' target by pasting it into our web application's project file and tweaking the 'Copy' task mentioned above:

      <!--

      ============================================================

      _CopyWebApplication

      MODIFIED: Ignores linked files as part of normal deployment logic.

     

      This target will copy the build outputs along with the

      content files into a _PublishedWebsites folder.

     

      This Task is only necessary when $(OutDir) has been redirected

      to a folder other than ~\bin such as is the case with Team Build.

      ============================================================

      -->

      <Target Name="_CopyWebApplication" Condition="'$(OutDir)' != '$(OutputPath)'">

        <!-- Log tasks -->

        <Message Text="Copying Web Application Project Files for $(MSBuildProjectName)" />

        <!-- Create the _PublishedWebsites\app\bin folder -->

        <MakeDir Directories="$(WebProjectOutputDir)\bin" />

        <!-- Copy build outputs to _PublishedWebsites\app\bin folder -->

        <Copy SourceFiles="@(IntermediateAssembly)"

              DestinationFolder="$(WebProjectOutputDir)\bin"

              SkipUnchangedFiles="true" />

        <Copy SourceFiles="@(AddModules)"

              DestinationFolder="$(WebProjectOutputDir)\bin"

              SkipUnchangedFiles="true" />

        <Copy SourceFiles="$(IntermediateOutputPath)$(_SGenDllName)"

              DestinationFolder="$(WebProjectOutputDir)\%(Content.SubFolder)%(Content.RecursiveDir)"

              SkipUnchangedFiles="true"

              Condition="'$(_SGenDllCreated)'=='true'" />

        <Copy SourceFiles="$(IntermediateOutputPath)$(TargetName).pdb"

              DestinationFolder="$(WebProjectOutputDir)\bin"

              SkipUnchangedFiles="true"

              Condition="'$(_DebugSymbolsProduced)'=='true'" />

        <Copy SourceFiles="@(DocFileItem)"

              DestinationFolder="$(WebProjectOutputDir)\bin"

              SkipUnchangedFiles="true"

              Condition="'$(_DocumentationFileProduced)'=='true'" />

        <Copy SourceFiles="@(IntermediateSatelliteAssembliesWithTargetPath)"

              DestinationFiles="@(IntermediateSatelliteAssembliesWithTargetPath->'$(WebProjectOutputDir)\bin\%(Culture)\$(TargetName).resources.dll')"

              SkipUnchangedFiles="true" />

        <Copy SourceFiles="@(ReferenceComWrappersToCopyLocal); @(ResolvedIsolatedComModules); @(_DeploymentLooseManifestFile); @(NativeReferenceFile)"

              DestinationFolder="$(WebProjectOutputDir)\bin"

              SkipUnchangedFiles="true" />

        <!-- copy any referenced assemblies to _PublishedWebsites\app\bin folder -->

        <Copy SourceFiles="@(ReferenceCopyLocalPaths)"

              DestinationFolder="$(WebProjectOutputDir)\bin"

              SkipUnchangedFiles="true" />

        <!-- MODIFICATION HERE: Copy local content files (i.e. non-linked files) recursively to _PublishedWebsites\app\ folder -->

        <Copy Condition=" '%(Content.Link)' == '' "

              SourceFiles="%(Content.Identity)"

              DestinationFolder="$(WebProjectOutputDir)\%(Content.RelativeDir)" />

      </Target>

     

    For the second, we need to add a new target to our project file and override the default dependencies to have it executed:

      <!--

      ============================================================

      CopyLinkedContentFiles

     

      A new target to copy any linked content files into the

      web application output folder.

     

      NOTE: This is necessary even when '$(OutDir)' has not been redirected.

      ============================================================

      -->

      <Target Name="CopyLinkedContentFiles">

        <!-- Remove any old copies of the files -->

        <Delete Condition=" '%(Content.Link)' != '' AND Exists('$(WebProjectOutputDir)\%(Content.Link)') "

                Files="$(WebProjectOutputDir)\%(Content.Link)" />

        <!-- Copy linked content files recursively to the project folder -->

        <Copy Condition=" '%(Content.Link)' != '' "

              SourceFiles="%(Content.Identity)"

              DestinationFiles="$(WebProjectOutputDir)\%(Content.Link)" />

      </Target>

      <!-- Override the default target dependencies to -->

      <!-- include the new _CopyLinkedContentFiles target. -->

      <PropertyGroup>

        <PrepareForRunDependsOn>

          $(PrepareForRunDependsOn);

          _CopyWebApplication;

          CopyLinkedContentFiles;

          _BuiltWebOutputGroupOutput

        </PrepareForRunDependsOn>

      </PropertyGroup>

     

    With these changes, whenever you build the web project any linked content files will be copied into the web application's folder structure  - whether you override the default output directory or not.

    If anyone is interested I've attached a sample solution that demonstrates the above workaround (as well as an unmodified project file so you can see the issue).

    Part of me wonders whether this is a bug at all, or whether it is in fact just an unexpected feature that was never supposed to be supported... I've raised a bug on Connect, so I guess I'll find out in due course.

  • TeamBuild Plug-in for CruiseControl.NET now on CodePlex

    Back in May this year I blogged about a CruiseControl.NET plug-in that I had written to allow CCNet to trigger a TFS Build - I had a few comments at the time, but recently I've had a flurry of comments & emails (well, a comparative flurry for my blog anyway!), many with the recurring theme of requesting the source.  Having been on a project full time for most of this year, I've found very little time to revisit this - other than a couple of minor private fixes for people who emailed me.  I've recently begun to roll-off this long term project so have finally found some time for this.

    I've setup a new CodePlex project (called CcNetTeamBuildTask) to host the source code and have published a release of the plug-in.  The following is a list of the main changes  compared to the version I posted back in May.

    • Compiled against the latest version of CruiseControl.NET - v1.3.0.2918
    • Fixed a bug that manifested itself when dealing with Team Project names that contained spaces
    • Fixed a bug with publishing the MSTest code coverage results, that occurred when specifying your build server using an FQDN (if you specify your build server by IP address, then this functionality will probably still not work for you)
    • Removed the task's queuing logic as CCNet now provides this out-of-the-box
    • New configuration property 'pathToTfExe' to support scenarios where Visual Studio is not installed in the default location
    • New configuration property 'useXmlLogger' to more easily switch between text or XML based MSBuild logging
      • Currently, the XML log file can only be automatically published by the task if you customise your TeamBuild to copy the 'msbuild-results.xml' file to the Drops Location
      • The text log file (BuildLog.txt) will always be automatically published to the CCNet Dashboard if this option is false
    • A general tidy-up of the code

    With the release of TFS 2008, I'm unsure whether this plug-in will be as valuable given the new features that Microsoft has added to TFS - but feel free to drop me a line if you have any ideas or would like to join the project.

    Cheers, James.

  • Configuring DCOM Security

    This post started out as a comment on one of Jamie's recent posts, however, I realised that the explanation would be easier to follow with screenshots... so here we go.

    To paraphrase, Jamie's issue was that whilst trying to use an SSIS package he was getting a DCOM access denied error (full details here) - this had worked in previous environments, but not once it got to Production.  The eventual solution that got everything working was to make the account running the package a local administrator (as it was in other environments) and reboot the server.

    Whilst this obviously got things working, what I'd like to illustrate here is a more secure way of overcoming this type of issue - as a platform guy, developer code running with full admin rights makes me twitchy! Smile

    DCOM security is managed using the 'Component Services' MMC snap-in, a shortcut to which is 'Start -> Run -> DCOMCNFG'


     

    The next step is locate the component that needs to have its security tweaked.  Jamie's post shows how to use the CLSID from the error message to locate the registration information for the component in the Registry.


     

    The value of the ''AppID' key is the surefire way of locating the component in DCOMCNFG, however, this is only visible from the component's property page - so ideally you want to find
    the component by name.  However, the names in DCOMCNFG do not necessarily exactly match those in the registry (typically it's a substring).


     

    Armed with the AppID and some ideas about the component name, return to DCOMCNFG and start hunting:


     

    Once you've found a candiate, open its properties and verify the AppID

     

     

    If you've found it, select the 'Security' tab, select 'Customize' and 'Edit..' under 'Launch and Activation Permissions', and grant the required user the rights requested in the original error message.


     

    Now you should be good to go. (complete with that warm and fuzzy feeling that you get from knowing that you haven't unduly compromised server security)
     

  • TeamBuild Plug-in for CruiseControl.NET

    I've been meaning to write this post for a couple of months now as a follow-up to a session that Colin and I gave at the Microsoft Architect Insight Conference back in March, but just haven't found the time... at least, that's my excuse - anyway cutting to the chase!

    Since the release of Team Foundation Server (TFS) there have been several Continuous Integration add-ons produced by various folks:

    We've used most of the above, but despite however well they worked have found myself wanting some feature or other that was available in CCNet (actually it more about being badgered by developers about the missing features!).  Whilst some of the above could have been extended to support these features, I couldn't help but feel that this would have been reinventing the wheel - hence the idea to extend CCNet.

    By the time I started this Vertigo had already released the VSTS plug-in for CCNet (which is a CCNet source control plug-in), so it was just a matter of producing a CCNet task plug-in that was able to trigger a Team Build.  The guts of calling the Team Build web service I based on the original CI Sample, but I was also keen to provide a mechanism for customising the behaviour of a Team Build in a more dynamic way than just creating another build type and changing the MSBuild properties.

    Team Build provides a mechanism for passing custom MSBuild properties into it via a source controlled file called TFSBuild.rsp.  This file consists of 1 or more lines each containing a valid MSBuild commandline switch (e.g. /p:PropName=value, would set the property 'PropName' to be 'value').  With that in mind, I developed the plug-in to allow these switches to be specified in the CCNet server configuration file (on a per CCNet project basis) and have it update the '.rsp' (in source control) before calling the build web service.

    I'll hold off diving into any more detail at this point, but if there is interest out there then I can do a follow-up.

    Attached to this post should be the plug-in assembly which you need to copy into your CCNet 'server' directory (along with the VSTS plug-in referred to above), and below is a configuration excerpt that shows how to configure the 'teambuild' task in CCNet.

    <project name="TeamBuild_CI">

      ...

      <tasks>

        <teambuild>

          <tfsServerUri>http://MyTfsServer:8080</tfsServerUri>

          <teamBuildServer>MyBuildServer</teamBuildServer>

          <teamProject>MyTeamProject</teamProject>

          <teamBuildType>MyTeamBuildType</teamBuildType>

          <workspace>MyBuildServer_MyTeamProject_MyTeamBuildType</workspace>

          <dropsLocation>MyDropsArea\MyTeamProject</dropsLocation>

          <msBuildParameters>

            <MsBuildParameter>/v:d</MsBuildParameter>

            <MsBuildParameter>/logger:ThoughtWorks.CruiseControl.MsBuild.XmlLogger,ThoughtWorks.CruiseControl.MsBuild.dll;msbuild-output.xml</MsBuildParameter>

            <MsBuildParameter>/p:SkipInitializeWorkspace=true</MsBuildParameter>

            <MsBuildParameter>/p:SkipGet=true</MsBuildParameter>

            <MsBuildParameter>/p:SkipClean=true</MsBuildParameter>

            <MsBuildParameter>/p:SkipDropBuild=true</MsBuildParameter>

          </msBuildParameters>

          <useMsTest>true</useMsTest>

          <useMsTestCodeCoverage>true</useMsTestCodeCoverage>

        </teambuild>

      </tasks>

      <publishers>

        <merge>

          <files>

            <file>C:\MyTeamBuildWorkspacePath\MyTeamProject\MyTeamBuildType\BuildType\msbuild-output.xml</file>

          </files>

        </merge>

        <xmllogger>

          <logDir>.\Web\Logs</logDir>

        </xmllogger>

      </publishers>

      ...

    </project>

    Let me know if you find it useful, interesting or a waste of your time! (actually, maybe save your bandwidth in the case of the latter).

     

  • COM Error When Configuring BizTalk 2006 BAM in a Multi-Server Environment

    Whilst trying to finalise some unattended server build scripts for deploying BizTalk 2006 into a multi-server environment I hit the following error when trying to configure the 'BAM Tools':

     

    This despite the fact that the SQL Server Management Tools had in fact already been installed.

    A bit of Googling revealed other people who had seen the same issue, but with no solution.  So I cast my net a little wider and found this posting indicating an issue with DTS.CustomTask that was resolved in SQL 2000 SP2.  I had already installed SP4 (a pre-req for running BizTalk 2006 on SQL 2000), but this got me wondering whether it had installed properly.

    We were using unattended installs of SQL Server that had previously been created, and thus far, had assumed that all was well with them - it turned out that there was a problem with the unattended SP4 install when run against a partial install of SQL Server (i.e. a Management Tools only install).

    One manual re-install of SP4 later and the error vanished.... happy days.

    Finally, all that was left to do was to record a new 'setup.iss' file for SP4 against the partial SQL install and we were good to go.... not before time!

  • Team Explorer & IE Problems

    I've spent most of this evening trying to figure out why my Team Explorer was, all of a sudden, refusing to connect to the company TFS server - whilst the solution is not terribly exciting, this post serves to link the TFS symptom to the underlying problem.

    Anyway, as I was saying, my attempts to add or connect to an existing Team Project resulted in a somewhat unhelpful "Team Foundation Error" message. Several reboots, uninstall/reinstall of Team Explorer later I was no better off, and Google wasn't much help either.

    Earlier in the day I had also noticed that I was getting an error message when trying to use the 'Find' functionality in IE... this caused a rather odd-looking 'Error 49: Interface not registered' message.

    On the premise that the problems might be related I decided to have a dig around... which soon revealed a solution courtesy of the veritable treasure trove that is www.jsifaq.com.

    As the error message suggests, some of the IE components had become unregistered and the JSIFaq page (found here) listed the ones to re-register:

    regsvr32 /s urlmon.dll
    regsvr32 /s actxprxy.dll
    regsvr32 /s shdocvw.dll
    regsvr32 /s mshtml.dll
    regsvr32 /s browseui.dll
    regsvr32 /s jscript.dll
    regsvr32 /s vbscript.dll 
    regsvr32 /s oleaut32.dll

    After doing this, Team Explorer worked fine.... happy days - now to get back what I had intended to do this evening!

     

     

     

     

  • Running VSTS4DB as a Non-Admin User

    I'd heard about this forthcoming edition of Visual Studio 2005 Team System (Team Edition for Database Professionals) a while back but was reminded by Jamie's recent post to actually download the CTP and give it a whirl.

    After a painless installation, I cranked-up VSTS, created a new 'SQL Server 2000' project and clicked on the 'Import Database Schema' with much anticipation.... only to be met with a rather abrupt and anti-climatic 'object reference not set' error:

    After an unsuccessful bit of Googling, and an uninstall/reboot/install cycle I eventually discovered that the import functionality worked fine when running with local Administrator rights, but not when using my everyday unprivileged account - irrespective of any other SQL-related permissions.

    So now I'll make sure that I run the ever useful 'MakeMeAdmin' before doing this sort of thing again.

    Incidentally, running VS2005 without admin rights is officially supported (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvs05/html/vs05security.asp), and to-date has worked well for me - a definite improvement on VS.NET 2003 in this respect.

  • Update to TFS Merge Issue

    So after receiving an e-mail from someone asking whether I'd heard anything more regarding the TFS Merge issue I blogged about a while back, I checked a post on the VSTS User Education blog  that I had previously left a comment on regarding this issue.

    There hadn't been any follow-up, but upon taking a look at the referrals associated with my original post I noticed some originating from forums.microsoft.com.  These referrals took me to this thread that talks about this issue (and included a link to my post, hence the referral).

    The long and short of it, is that this appears to be a known issue the 'debit/credit logic within Merge' , which based on the last message in the thread appears to have been fixed.... something to look out for in the forthcoming RC.

  • Team Foundation Server - Merging Branches Issue?

    So last week I had my first official introduction to Visual Studio Team System (VSTS) and Team Foundation Server (TFS) when I attended a 'VSTS Readiness Workshop' that was co-led by Joel.

    By Day 2 we'd started looking at the new source control system that ships with TFS and I was keen to see how the new/improved features (database back-end, concurrent check-out, transactional check-ins aka changesets  etc.) might affect our approach to versioning and release management - in particular I was interested to see how much the branch/merge functionality had been improved.

    One of the main differences is that creating a branch does not immediately result in a duplicates of each affected object (and thus double the storage requirements) - initially the branch contains links to the files, and only when a file is actually changed in the branch does a new version get created. With this in mind, I set about simulating a typical scenario to put the merge/branch functionality through its paces - and got some 'interesting' results.

    The scenario (and resulting issue) is summarised below:

    1 ) 'Sprint 1' begins, and development continues for 4 weeks
    2 ) At the end of 'Sprint 1', a new branch is created called 'Sprint 2'
    3) Consider that 'Sprint 1' functionality is now in production In both branches the file Class1.cs contains the following code: 
        1 public class Class1
        2 {
        3     public Class1()
        4     {
        5         //
        6     }
        7 
        8     public int SomeMethod1(int x, int y)
        9     {
       10         return x + y;
       11     }
       12 }
    4) All 2nd Sprint development continues in the 'Sprint 2' branch
    5) During the first week of 'Sprint 2' additional methods are added to Class1 as part of functionality on the sprint backlog. The Class1.cs file now looks like this: 
        1 public class Class1
        2 {
        3     public Class1()
        4     {
        5         //
        6     }
        7 
        8     public int SomeMethod1(int x, int y)
        9     {
       10         return x + y;
       11     }
       12 
       13     public int SomeMethod2(int x, int y)
       14     {
       15         return (2 * x) - y;
       16     }
       17 
       18     public int SomeMethod3(int x, int y, int z)
       19     {
       20         return (x ^ y) / z;
       21     }   
       22 }
    6) Also during week 1 a production bug comes in, and is fixed in the 'Sprint 1' branch. The Class1.cs in the 'Sprint 1' branch now looks like this: 
        1 public class Class1
        2 {
        3     public Class1()
        4     {
        5         //
        6     }
        7 
        8     public int SomeMethod1(int x, int y)
        9     {
       10         return x + (y - 1);
       11     }
       12 }
    7) In week 2 the bug fix is signed-off and needs to be applied to the 'Sprint 2' branch - this is done via a merge operation.

    8) The merge is performed from 'Sprint 1' to 'Sprint 2', and happens as you'd expect, with a conflict being detected and being given the opportunity to perform a manual resolution to combine the bug fix from 'Sprint 1' with the new methods in 'Sprint 2', to give a Class1.cs in 'Sprint 2' that looks like this: 

        1 public class Class1
        2 {
        3     public Class1()
        4     {
        5         //
        6     }
        7 
        8     public int SomeMethod1(int x, int y)
        9     {
       10         return x + (y - 1);
       11     }
       12 
       13     public int SomeMethod2(int x, int y)
       14     {
       15         return (2 * x) - y;
       16     }
       17 
       18     public int SomeMethod3(int x, int y, int z)
       19     {
       20         return (x ^ y) / z;
       21     }   
       22 }
    9) In week 3 another production bug is discovered and fixed in the 'Sprint 1' branch, and is fixed as shown below: 
        1 public class Class1
        2 {
        3     public Class1()
        4     {
        5         //
        6     }
        7 
        8     public int SomeMethod1(int x, int y)
        9     {
       10         return (2 * x) + (y - 1);
       11     }
       12 }
    10) This bug fix is signed-off and then needs to be applied to the 'Sprint 2' branch
    11 ) The same merge operation as in step 9 is performed, however, this time no conflict is detected and the merge completes automatically without any prompts.

    12) The merged version of Class1.cs in 'Sprint 2' can be checked-in without intervention. Unfortunately, upon inspection of the resulting Class1.cs in 'Sprint 2' we can see the merge has not quite happened as we'd intended: 

        1 public class Class1
        2 {
        3     public Class1()
        4     {
        5         //
        6     }
        7 
        8     public int SomeMethod1(int x, int y)
        9     {
       10         return (2 * x) + (y - 1);
       11     }
       12 }

    As you can see above, Class1.cs in 'Sprint 2' has been overwritten by the version in 'Sprint 1' and the all the new methods have been lost. It seems that TFS assumes that a merge operation should result in identical files, which of course is not necessarily the case.

    I'd be interested to hear from anyone who cannot recreate the above issue - I also plan to re-test this scenario using the December CTP version of TFS that was recently released to see if it behaves differently.

  • Word Documents or Wiki?

    I've talked about why I like Wiki as a documentation medium before, however, today I had a discussion with one of my colleagues where we were considering the merits of such an approach versus the traditional word processing document - incidentally, in this case 'discussion' is not a euphemism for argument!

    My belief is that certain people take some kind of comfort in being handed a tome-like Word document as a tangible deliverable from a given piece of work - and worse still, make a judgement on whether the work was value for money based on the page count - in my experience the usefulness of a document is often inversely proportional to its length!

    The problem is, in my opinion, producing documentation using traditional word processing tools seems to encourage verbosity - you feel obliged to include such elements exposition, context, and background regardless of whether they are really needed to get your message across. On top of this, I often find that the richness of the formatting functionality contained by such packages can be a distraction from the actual content.

    All you adept Word users (I consider myself in that bracket - 3 years working in a Law firm's IT group leaves it mark), how may times have you found yourself battling with some shonky template that just refuses to number or layout your headings properly? (OK, I guess that's just me then)

    My point is that by stripping the formatting and layout features back to the necessities you no longer get caught up in trying to bend the tool your will, instead you just get on with producing the content - back in the dot com days there was a phrase banded about by start-ups as to why they could make it big - 'Content is King' - I believe that this is certainly truer for documentation than it was for them.

    Don't get me wrong, there will always be a place for the traditional word processing document as the appropriate deliverable, but I think only for specific types of work. Typically, I consider these to be reviews or analysis-oriented work where the end results are essentially static and represent a state of affairs at a given time.

    I believe most technical documentation should be treated as 'living' and updated as necessary - an out of date technical document is next to useless, and 70 page word documents just don't get habitually updated. For me, that's why Wiki-based documentation is so powerful - the latest version of the content is always available and can be collaboratively updated with minimum friction to your actual task in hand. For instance, following a set of procedures (perhaps written by someone else) and finding an error or omission - you can quickly add or amend the information and get back to following the rest of the procedures, and your learning's are immediately available for the next person.

    It's no panacea of course, a key drawback I have hit with using this approach (other than convincing clients that this type of documentation is as good as the 'real thing') is when you need to get the content into a hard-copy or any other offline form - if you have structured the content across a number pages (i.e. due to length), with perhaps a table of contents page as the entry point, you have to visit and print each page individually.... if only there were some kind of recursive print function.....

  • Using Visual Studio Solution Files with MSBuild

    As previously discussed by Simon, there are issues with pointing MSBuild at a Visual Studio solution file to build your project assemblies - the alternatives are to either build each of the projects individually (using <MSBuild/> tasks) or to call VS itself to build the solution (the SDC Build Tools project has a custom task to easily do this).

    The former approach causes some maintenance headaches by requiring the MSBuild script to be updated anytime a project is added or removed from the solution - whereas the latter option means you must have Visual Studio installed (which was supposed to be one of the benefits of using MSBuild) and can also result in some referencing issues the first time you build a solution after project changes.

    In order to try and get the best of boths worlds, I started thinking about dynamically generating an MSBuild script with all the tasks needed to build each VS project referenced by the targetted .sln file (i.e. the first of the above approaches, but with all the leg-work automated).  My preferred tool for such things (since being introduced to it by Howard) is the excellent CodeSmith .

    By running the template below prior to calling MSBuild, the tasks needed to build all the Solution's projects are written to a .targets file that can be <Import'd by the main MSBuild script - in my case this 2-step process is wrapped by a simple batch file that gets called by CruiseControl.


    <%--
    Description: Generates an MSBuild script to build each of the Visual Studio
                 projects found within the specified Visual Studio Solution file.
    %>
    <%@ CodeTemplate Language="C#" TargetLanguage="Text" Src="" Inherits="" Debug="False" Description="Generate MSBuild actions from Solution file" %>

    <%@ Property Name="SolutionFile" Type="System.String" Default="" Optional="False" Category="Strings" Description="VS.NET Solution file to process" %>

    <%@ Import Namespace="System.Diagnostics" %>
    <%@ Import Namespace="System.IO" %>
    <%@ Import Namespace="System.Text.RegularExpressions" %>

    <%
    if ( !File.Exists(this.SolutionFile) )
    {
        throw new ArgumentException("ERROR:\nUnable to find the VS.NET Solution file:\n" + this.SolutionFile);
    }
    else
    {
        this.SolutionFile = Path.GetFullPath(this.SolutionFile);
    }
    %>
    <?xml version="1.0" encoding="utf-8" ?>
    <Project DefaultTargets="Build" xmlns="
    http://schemas.microsoft.com/developer/msbuild/2003">
        <Target Name="BuildProjects">
    <%
    string contents;
    using ( StreamReader sr = new StreamReader(this.SolutionFile) )
    {
        contents = sr.ReadToEnd();
    }

    // The regular expression below extracts the project information from
    // the relevant part of the solution file - the format of which seems to
    // be the same in VS 2003 & 2005
    Regex regex = new Regex("^Project\\(.*\\s=\\s\"(?<projectName>.*)\",\\s\"(?<projectPath>.*)\",");
    MatchCollection matches = regex.Matches(contents);
    if ( matches.Count > 0 )
    {
        foreach ( Match match in matches )
        {
            string projectName = match.Groups["projectName"].Value;
            string projectPath = match.Groups["projectPath"].Value;
            Trace.WriteLine( string.Format("ProjectName: {0}\nProjectPath: {1}", projectName, projectPath) );
           
            // Make sure that the reference in the .sln file is really a project
            // and not just a Solution Folder
            if ( projectName != projectPath && projectPath.EndsWith("proj") )
            {
            %>
                <MSBuild Projects="<%=Path.Combine(Path.GetDirectoryName(this.SolutionFile),projectPath)%>" Targets="Clean;Rebuild" />
            <%
            }
        }
    }
    else
    {
        throw new ApplicationException( string.Format("ERROR:\nThe specified Solution file contains no projects:\n{0}", this.SolutionFile) );
    }
    %>
        </Target>
    </Project>

    <script runat="template">
    </script>

     

  • IIS 6 Application Pool Identities

    This afternoon has been one of those annoying situations where you burn lots of time trying to resolve a problem, only to find that:
    1. the solution is easy-peasy
    2. you wonder why you didn't try the resolution sooner
    3. you discover some new 'quirk' about the system you're using.... which kind of answers the above question!
    I've spent most of this afternoon troubleshooting a security issue with a web-based BizTalk administration tool that I was trying to deploy to a new server.  Part of the tool performs some remote performance monitoring of BizTalk application servers, which obviously requires a specific security rights.

    The site runs within its own application pool (on Windows Server 2003), which was using a domain user account as its identity - needed due to the remote monitoring requirement.  I was pretty certain that I'd got the security requirements nailed, but despite this I could not get the site to work using the domain account, yet it worked fine using a local account with exactly the same local group membership.

    Anyway after spending way too much time double-checking all sort of other settings, and confirming that the IIS worker process and the ASP.NET request were definitely running in the intended user context, I realised that in-between the various group membership changes I had made at the start of this saga, I had only been stopping and starting the application pool.

    This, despite actually stopping the physical process running the application pool (and obviously causing the ASP.NET application to restart) did not allow the newly-created worker process to get its new group memebership details.  Performing a full IISRESET, on the other hand, had the desired effect and the everything started working as expected.

    Seems like there must be some kind of caching going on somewhere...... maybe the IIS Admin/WWW service caches group memebership information at startup, and worker processes use this?  Whatever the reason, it seems that when an IIS 6 worker process gets created it doesn't logon in the same way as a usual system service.

    I imagine this can be explained by the IIS 6 architecture somehow, however, for now I just need to make up some time!

    Posted 22 June 2005 17:32 by james.dawson | 0 Comments
    Filed under:
Powered by Community Server (Personal Edition), by Telligent Systems