Installing TFS 2010 on a 64-bit system

If you are one of the lucky ones with an MSDN subscription then as of today you can download the Visual Studio 2010 bits which includes Team Foundation Server 2010. As I blogged about yesterday the 64-bit install is both unsupported and quite involved.  On the other hand things are looking up for TFS 2010. The install process was much more straight forward with the exception of a connection and data warehouse issue at the end. I have recorded the process for anyone who is looking to build a demo machine to kick the tires with. Unlike the default installation process I have chosen to install SharePoint myself. It’s just one of those things that my experience tells me to make sure it’s working before loading other stuff on top of it. Have fun with the show:

Installing TFS 2008 on a 64-bit system

If you like living on the wild side then this post is for you – it is out of the bounds as far as official support goes.  Having realized that the world is going x64 the TFS product group made a few regrettable choices with one of them being not supporting 64-bit on the application server tier. Even though they made the choice, explained why, and confessed their regrets the good news is that you still can install it with a few extra steps. Below you will find a video of the process:

Here are the notes from that video:

  • Install Windows Server 2008 R2
  • Create a Software folder on the system drive and copy in the bits of the following to their own sub-folder:
    • SQL Server 2008 Standard Edition
    • SQL Server 2008 Service Pack 1
    • Team Foundation Server 2008 Standard Edition
    • Team Foundation Server 2008 Service Pack 1
    • Visual Studio 2008 Service Pack 1
    • Windows SharePoint Services 3.0 with Service Pack 2
  • Add .NET Framework 3.5.1 to the server
    • Server Manager > Add Features > .NET Framework 3.5.1 Feature
  • Create a Service Account for SQL Server
    • Net.exe User Service-SQL * /Add
    • WmiC.exe Path Win32_UserAccount Where Name='Service-SQL' Set PasswordExpires=False
  • Install SQL Server 2008 Standard with Service Pack 1 Slipstream
    • Command Prompt > C:\Software\SQLServer2008SP1-KB968369-x64-ENU.exe
      • Install the setup files then it will exit
    • Command Prompt > C:\Software\SQLServer2008-Standard\Setup.exe /PCUSource=C:\Software\SQLServer2008-SP1
      • Select all features
      • Use the service-sql service account for all services
      • Set SQL Agent and Browser services to auto start
      • Set Mixed Mode Authentication
      • Add Administrators to SQL Server administrator
      • Add Administrators to SQL Server Analysis Services administrator
      • Use the native mode default configuration for SSRS
    • Open Management Studio to verify that Service Pack 1 was installed
    • Ensure we can browse to http://localhost/reports
  • Create a Service Account for SharePoint
    • Net.exe User Service-WSS * /ADD
    • WmiC.exe Path Win32_UserAccount Where Name='Service-WSS' Set PasswordExpires=False
  • Install Windows SharePoint Services 3.0 with Service Pack 2
    • Command Prompt > C:\Software\SharePoint-3.0SP2-x64\Setup.exe
      • Advanced > Web Front End
      • Feedback tab
    • Create central administration site at http://localhost:8079
      • Can't use 8080 - TFS wants it
    • Add http://colin-tfs to the local intranet zone
    • Create a new web application
      • Use Default Web Site
      • Use Service-WSS Application Pool Identity
      • Restart IIS Automatically
    • Create a new site collection
      • Title: Team Foundation Server
      • Site Admin: Administrator
    • Ensure we can browse to http://localhost
  • Create a Service Account for TFS
    • Net.exe User Service-TFS * /ADD
    • WmiC.exe Path Win32_UserAccount Where Name='Service-TFS' Set PasswordExpires=False
  • Install Team Foundation Server 2008 Standard with Service Pack 1 Slipstream
    • Command Prompt > MSIExec.exe /A C:\Software\TFS2008-Standard\AT\VS_Setup.msi /P C:\Software\TFS2008-SP1\TFS90SP1-KB949786.msp TARGETDIR=C:\Software\TFS2008-Standard-SP1\AT
    • Modify baseline.dat to remove 64-bit blocking
      • [gencomp114] > BlockorWarn=0
      • [gencomp114] > InstallOnAMD64=0
    • Modify hcpackage.xml to support SQL Server 2008 Service Pack 1
      • PropertyStrValue LIKE '10.%'" action="=" count="(0" />
      • action="<" version="10.2" />
    • Copy SharePoint registry key to fool TfsConfigWss.exe
      • Reg.exe Copy "HKLM\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions" "HKLM\SOFTWARE\Wow6432Node\Microsoft\Shared Tools\Web Server Extensions" /S
    • Command Prompt > C:\Software\TFS2008-Standard-SP1\Setup.exe
    • Change application pool to 32-bit applications
      • IIS Manager > Microsoft Team Foundation Server Application Pool > Advanced Settings > Enable 32-bit Applications
      • Server > Restart
    • Modify first line of tfsredirect.aspx in C:\Software\TFS2008-Standard-SP1\AT\Program Files\Common Files
      • string c_rootReportServerKey = @"Software\Wow6432Node\Microsoft\VisualStudio\9.0\TeamFoundation\ReportServer\";
    • XCopy "C:\Software\TFS2008-Standard-SP1\AT\Program Files\Common Files\*.*" "C:\Program Files\Common Files" /S
  • Install Team Explorer 2008 with Service Pack 1
    • Command Prompt > C:\Software\TFS2008-Standard\TFC\Setup.exe
    • Install Visual Studio 2008 Service Pack 1 which contains Team Explorer 2008 SP1 to take care of red X on Reports
    • Reboot
    • Visual Studio 2008 > Tools > Connect to Team Foundation Server
    • Add a new project
  • Change the warehouse service identity to use service-tfs

Creating Control Modules for Sitefinity

As you develop your second web site and look to start sharing custom controls there is no doubt you will want to find a way to do so with ease. Sitefinity employs modules as their next step in the evolution of customization. While working to package up a few controls I put together a bare bones module to demonstrate how to use a module, explore the new SimpleControl class which has a brief mention in the documentation-by-blog, and an easy way to deploy the module.

When creating a module you derive from the Telerik.WebModule class. Once you have done so there is very little you need to implement. For the sake of our sample class we will override Name, Title, Description, and Controls properties:

public class HelloWorldModule : Telerik.WebModule
{
    #region Properties
    public override IList Controls
    {
        get
        {
            return new List(new ToolboxItem[] {
                new HelloWorldToolboxItem()
            });
        }
    }

    public override string Description
    {
        get
        {
            return "Control library demonstration";
        }
    }

    public override string Name
    {
        get
        {
            return "Hello World Controls";
        }
    }

    public override string Title
    {
        get
        {
            return "Hello World Controls";
        }
    }
    #endregion
}

The Controls property returns a list of class instances derived from the Telerik.Web.ToolboxItem class.  This is used by Sitefinity to populate the page editor’s toolbox without having to explicitly define each control the web site’s web.config.  The Toolbox Item is not much more than a DisplayName and Description of the item for use in the page editor view:

public class HelloWorldToolboxItem : Telerik.Web.ToolboxItem
{
    #region Constructors
    public HelloWorldToolboxItem()
        : this(typeof(HelloWorld)) { }

    public HelloWorldToolboxItem(Type type)
        : base(typeof(HelloWorld))
    {
        base.DisplayName = "Hello World!";
        base.Description = "Simple Control sample";
    }
    #endregion
}

The sample control is derived from SimpleControl which provides localization and embedded template support. We have included an embedded user control in the project which creates a label control and exposes that through our control interface:

[ToolboxItem(typeof(HelloWorldToolboxItem)),
 ToolboxData("<{0}:HelloWorld runat=\"server\" />")]
public class HelloWorld : Telerik.Cms.Web.UI.SimpleControl
{
    #region Properties
    [Bindable(true), Category("Text"), DefaultValue("Hello world!"),
     Description("Gets or sets the Label text."),]
    public string LabelText { get; set; }

    public override string LayoutTemplateName
    {
        get
        {
            return "SampleControlLibrary.HelloWorld.ascx";
        }
    }

    protected ITextControl Label1
    {
        get
        {
            return base.Container.GetControl("Label1", true);
        }
    }
    #endregion

    #region Constructor
    public HelloWorld()
    {
        LabelText = "Hello world!";
    }
    #endregion

    #region Methods
    protected override void CreateChildControls()
    {
        base.CreateChildControls();
        Label1.Text = LabelText;
    }
    #endregion
}

To make deployment easy I used the often forgotten System.Configuration.Install.Installer class which is called by the installer utility, InstallUtil.exe. The challenge with using this class is that there is very little guidance or examples of how to use it. I referred to an article on Windows Installer installation phases, which this class is loosely based upon, to guide my implementation. Below is a snippet of the Install method.  In addition I have implemented Commit, Rollback, and Uninstall.

...
public override void Install(IDictionary stateSaver)
{
    if (!File.Exists(WebConfigPath))
    {
        throw new FileNotFoundException("Unable to locate web.config. Ensure that this assembly located in the web application's bin folder.", WebConfigPath);
    }

    base.Context.LogMessage(string.Format("Using web.config located at '{0}'", WebConfigPath));

    string backupConfigFile = String.Format("{0}.bak", WebConfigPath);
    File.Copy(WebConfigPath, backupConfigFile, true);
    stateSaver.Add(WebConfigPath, backupConfigFile);

    XDocument configDoc = XDocument.Load(WebConfigPath);

    base.Context.LogMessage(string.Format("Assembly contains {0} modules to register", this.AssemblyWebModules.Count));
    if (this.AssemblyWebModules.Count > 0)
    {
        XElement modulesEntry = configDoc.XPathSelectElement(".//telerik/framework/modules");
        List moduleList = modulesEntry.Descendants().ToList();

        foreach (Type module in this.AssemblyWebModules)
        {
            Regex moduleType = new Regex(string.Format("^{0}, {{0,}}{1}$", module.FullName, AssemblyName), RegexOptions.IgnoreCase);
            if (!moduleList.Exists(x => moduleType.IsMatch(x.Attribute("type").Value)))
            {
                base.Context.LogMessage(string.Format("Registering '{0}'", module.Name));
                XElement addElement = new XElement("add");
                addElement.SetAttributeValue("type", string.Format("{0}, {1}", module.FullName, AssemblyName));
                modulesEntry.Add(addElement);
            }
        }

        configDoc.Save(WebConfigPath);
    }

    base.Install(stateSaver);
}
...

That about wraps it up. Download the project source code and use it to build your own control libraries.