Continuous Learning and Sharing of Team Foundation Server and Application Lifecycle Management RSS 2.0
# Thursday, July 30, 2009

Note:  I slightly changed the title from TFS Deployments to Deployments with TFS in case there was any confusion about whether this is about deploying TFS or doing deployments with TFS.

In Part 1: The Deployment Process Should Enforce Good Configuration Management Practices, I gave some background on my experiences and how the configuration management process has evolved and some rules and benefits to the automated deployment MSIs.

In this Part, I will walk through the steps to create an automated deployment MSI in Visual Studio 2008 satisfying the rules from Part 1.    In this example I will build the MSI for a windows service that will auto assign the username and password.

The first few steps are standard steps for building the MSI.  Then the steps get more interesting when it starts getting into to the automated deployment settings.

Step 1:  Add the Setup Project to existing solution

To add the setup project to the solution, choose “Add New Project” > Other Project Types > Setup Project.



Step 2:  Add Project Output to MSI

Add the required files for the deployment by adding the project output of the primary project.  To the project output, right click on the setup project in the solution explorer  > View > Project Output.  A dialog box similar to the one below will appear.  Verify that the windows service project is the selected project in the combo box.  Choose Primary Output and click OK.



Step 3:  Add Custom Actions

Next add the Primary output as a Custom action for the install, uninstall, etc.  To do this, right click on the Custom Actions > Add Custom Action > Application Folder > Primary Output from <your project>.


Step 4:  Add Installer to Windows Service project

In the Windows Service project, add an Installer class by double clicking on the Service component to view the designer.  In the Properties window (usually) at bottom right corner, there will be a link to “Add Installer” similar to the image below.  This creates the project installer class containing the service installer and the process installer.  We will be modifying these in some later steps.


Step 5:  Create the environment config files and folders in the windows service and add them to the MSI.

If the solution had multiple projects, I usually create a separate project for the config files.  Here I will create the folders in the same project. Create the folders and files as shown below.  Basically environment’s config file will be stored here and checked into source control.  To add these to the MSI, go to the properties of each config file and change the Build Action to Content.  Now go back to the MSI, right click on the project > Add > Project Output > Content Files to add these.


Step 6:  Create Custom Action to copy the selected config file to the application folder

Now that environment configs are in the ConfigFiles folder, the selected one needs to be copied to Application folder.  To do this we need to create a custom action.  This is in VBScript but eventually I want to create these custom actions as a helper class in Team Deploy.  To create the custom task, the actual file needs to be created outside the MSI.  I have created a CustomActions folder in the windows service project.   In there I added a copyconfig.vbs file.  This custom action is going to need two values passed into it.  One is the environment and the other is TargetDir.  I couldn’t figure out a way to get current folder inside for the vbscript so I found it easier to just pass it in.  To pass in values into the custom action the name/value pairs are passed into the CustomActionData property.  Also since this is going to overwrite the current app.config files, exclude the app.config by clicking on the Primary Output > Exclude > Add for *.config.

Here is the code for the copyconfig.vbs file.  After you copy and paste this into the vbs file.  Save it and in the properties, change the Build Action to None.   We do not want this copied to the MSI output like the config files. 

on error resume next
dim paramsList
paramsList = split("CustomActionData"), ",")
dim param1
dim param2

param1 = paramsList(0)
param2 = paramsList(1)

dim filesys
dim path

path = param2 & "configfiles\" & param1 & "\"

dim strEvn, objFile, strLogFile

Set objShell = CreateObject("Wscript.Shell")
strEnv = objShell.ExpandEnvironmentStrings("%temp%")
strLogFile = strEnv & "\MSIInstallLog.txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(strLogFile, 8, True)

objFSO.CopyFile path & "*.config", param2, true

if err.number > 0 then
    objFile.WriteLine Now & "::CopyConfigs - " & param1 & err.Description
    objFile.WriteLine Now & "::CopyConfigs - Completed Successfully."
end if


Now add the Custom Action to the MSI by right clicking on the project > View > Custom Actions.  We only want this to run during the installation process, so right click on Install > Add Custom Action > Application Folder > Add File > Browse to Copyconfig.vbs and click OK.  Notice in the setup project that the copyconfig.vbs has the no sign icon on it so it isn’t copied to the application folder.

Next we need to pass in the the values into the Custom Action.  Click on the Copyconfig.vbs in the Custom Actions view.  In the properties window, set CustomActionData = [ENV],[TARGETDIR]. 

Repeat the steps above for AssignService.vbs.   Set CustomActionData = [USR],[PWD],"SampleService"

on error resume next

dim param

param = split("CustomActionData"), ",")

if len(param(0)) > 0 then

    dim strEvn, objFile, strLogFile

    Set objShell = CreateObject("Wscript.Shell")
    strEnv = objShell.ExpandEnvironmentStrings("%temp%")
    strLogFile = strEnv & "\MSIInstallLog.txt"
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFile = objFSO.OpenTextFile(strLogFile, 8, True)
    ' ------ SCRIPT CONFIGURATION ------
    strUser     = param(0)
    strPassword = param(1)      
    strSvcName  = cstr(replace(param(2), chr(34), ""))
    strComputer = "."    
    ' ------ END CONFIGURATION ---------
    set objWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
    set objService = objWMI.Get("Win32_Service.Name='" & strSvcName & "'")
    intRC = objService.Change(,,,,,,strUser,strPassword)
    if intRC > 0 then
       objFile.WriteLine Now & "::" & strSvcName & " - " & "Error setting service account: " & intRC
       objFile.WriteLine Now & "::" & strSvcName & " - " & "Successfully set service account"
    end if
    if err.number > 0 then
        objFile.WriteLine Now & "::" & strSvcName & " - " & err.Description
    end if

end if

This custom action finds the “SampleService” and changes the username and password.  I had to use the service name literal.  There wasn’t anything I could find where I could use a dynamic value.

Step 7:  Modify Project Installer to handle interactive and silent modes

One of the requirements is that the MSI needs to work when using the wizard and when specifying parameters when installing from the command line or remote utility.  Therefore when the user is installing the MSI through the wizard (we will create this in the next step)   it needs to prompt for the user and password.  This is accomplished by setting the service account of the installer to LocalSystem so it won’t prompt but if the username isn’t passed in it switches the service account to User.  This will then prompt the user.

To implement this, add the following code to the ProjectInstaller.

private string GetContextParameter(string key)
    string sValue = "";

        sValue = this.Context.Parameters[key].ToString();

        sValue = "";

    return sValue;

// Override the 'OnBeforeInstall' method.       
protected override void OnBeforeInstall(IDictionary savedState)
    string username = GetContextParameter("usr").Trim();

        if (username == "")
            serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.User;
    catch(Exception e)


private string getvalues()
    string all = "";

    foreach (DictionaryEntry value in this.Context.Parameters)
        all += value.Value.ToString() + ",";
    return all;

Step 8:  Add step to wizard to specify the Environment.

In addition to prompting the user for the user and password, the MSI needs to prompt the user for the environment.  To add a form to the Wizard.  Right click on the setup project > View > User Interface.  Then right click on Start (as shown) and the click Add Dialog.  In this example we have 3 environment, so choose the RadioButtons (3 Buttons) and press Ok.


The Dialog window appears below the Confirm Installation box.  If you build the setup project you will notice the warning, “All custom dialogs must precede the 'Installation Folder' dialog”.  Therefore, click on the up arrow twice to move the RadioButton dialog window  above the Installation folder.  Now click on the dialog and view the Properties window.  There are several empty properties we need to set.  Set the following to something similar to these values:

  • BannerText = “Choose Environment”
  • BodyText = “Please select an environment from the list.”
  • Button1Label = “Production”
  • Button1Value = “Prod”
  • Button2Label = “Test”
  • Button2Value “Test”
  • Button3Labal = “Development”
  • Button3Value = “Dev”
  • ButtonProperty = “Env”
  • DefaultValue = “Prod”


Step 9:  Change build type to loose uncompressed files

The last step is to change the Package Files option under the setup project’s properties from “In setup file” to “As loose uncompressed files.  You will need to do this for each configuration in the project.



These are all of the steps.  I hope it helps.  Attached below is the sample solution with the setup project hosted on the MSDN Code Gallery.  You may use this however you would like.  Now that you know how to build the MSI, it is time to deploy it.  I will walk through that in the next post.

Sample Automated Deployment Solution for building a Windows Service MSI (22k)

The other future parts in the series will include:

  1. Building and Deploying ClickOnce applications
  2. Deploying a web application.
  3. Hopefully more!  Let me know what you would like to see.



Thursday, July 30, 2009 5:14:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [1] -
Team Build | Team Deploy | Team Foundation Server

# Monday, July 20, 2009

I just received my new Google Voice number.  One of the cool features is creating a “Call me” widget.   Here’s the link for you to call me.  If you have any questions about VSTS or TFS including information about our Team System and Team Foundation Server 2008 Jumpstart program or if you just want to say “hi”, click on the link below.  Depending on the amount of SPAM and solicitors, this may be directed to voice mail so please leave a message and I will call you back.  Also, feel free to contact us through normal methods on our website at



Monday, July 20, 2009 6:12:00 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
General | Team Foundation Server

# Tuesday, July 14, 2009

Build automation and more specifically deployments are one of my favorite things to do with Team Build in Team Foundation Server 2008.  Deployments are not an “out of the box” feature but there are several ways to accomplish this.  I am going to cover some of these different approaches and strategies for doing deployments with TFS.  Before I get into doing the actual deployments, I want to give a some background and guidance behind why creating automated deployments are part of a good software configuration management (SCM) process. Then I will go into creating a MSI that supports automated deployments.   The subsequent posts will cover a variety of topics including deploying the MSIs with Team Deploy and deploying Click Once applications.  Lastly I will explore what automated deployments will look like in TFS 2010.  As always, if you have any questions or if there is something you want me to see, send me an email, tweet, or comment.

This deployment process has been an evolution of the past 3 years.  I began getting engaged in this process when I used to be part of a process where the development team created a MSI for test and a couple weeks before production our PC configuration team would create a new MSI with their tools.  We would do some testing of this MSI over those couple weeks before going to production.  Unfortunately this process led to a few mistakes, mostly due to the fact we were changing too much too late in the game.  We began to think of an improved process with better configuration management in mind.  We created a few rules to that we wanted our new process to follow. 

Rules for MSIs

  • Create the MSI early in the development process – As early as a development team has most of the projects for the deployment created, compile the assemblies and create the MSI.  For Agile teams this can be as early as Iteration 1.
  • Create a MSI that is easily updated – MSBuild and Team Build by default do not support building setup projects.   There are some workarounds to being able to build a MSI, but I prefer creating the MSI and choose “As  loose, uncompressed files” for the build in the setup project properties.   This will leave the files out of the MSI and basically add pointers to the files in the MSI.  This makes updating the MSI as easy as using the copy task.  If a new project is added to the solution, add the new dependency, rebuild the MSI, and add the new assembly to the copy process (more on this later)
  • Config files – This could probably be a post by itself.  Basically there are a couple rules I have about config files. 
    • The config files for all of the environments must be checked in to source control.  Usually I will create a library project in the solution and add all of the config files to it.
    • Along with all of the files being in source control, there can be no editing or copying of config files once the application is deployed.  For too long I would always copy the config file from the installed location.  Then I would do the uninstall, reinstall, and then copy/modify the config file based on the one I copied to another file.   I did this because I didn’t know if there were changes or particular production settings that were not in the MSI.  
    • Lastly the MSI should contain all of the config files for all of the environments.  These can be removed after the installation process is done but the key is that the config files are not copied from another location.  This will become more clear when I start to walk through the process.
  • Because the MSI contains the config files for multiple environments, the user must be able to select the appropriate environment to install.  The MSI should allow the the user to specify the environment in the wizard or through a command line argument when installing it in quiet mode.
  • If the MSI is a windows service, the user must also be able to specify the username and password through the wizard or command line.


By following these rules, it provides the following enhancements

  • Tested and Proven MSI – Just like we shouldn’t deploy code that we haven’t tested, the same is true for the deployment process.  By using the same MSI in the Test environment, throughout the development cycle, and in production, this will help ensure that that are no surprises when deploying to production.
  • Ready for Automated Deployments – The MSI will include key features that are required for use in automated deployments.   These include the config files for multiple environments, easily updateable from Team Build, and supports passing environment and username/password by command line.

In the next part, I will go over the steps and processes for creating a MSI to support these rules and help enforce your configuration management process.  I will demonstrate how to do all of this within Visual Studio without using any 3rd party tools.


Tuesday, July 14, 2009 5:13:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [1] -
Team Build | Team Foundation Server

# Thursday, July 09, 2009

This week I was informed my presentation, “An Iteration in the Life of an Agile Team with Team System 2010” was added to the schedule at the Heartland Developer Conference 2009.  I am excited to be presenting at this event that usually sells out around 600 attendees.  It is going to be huge. There are a lot of great speakers and topics.  Scott Guthrie is one of the keynote speakers!  My session is October 16th at 2:00pm.  I am looking forward to this session that will be a unique, real world way of demonstrating some of the new features of Team System 2010.

Presentation Summary

This presentation will demonstrate a complete two week scrum iteration from the planning meeting to development, testing, and bug fixing to deployment utilizing the features found in Team System 2010. New features include gated check-ins for Continuous Integration (CI), Test-Driven Development (TDD), Product Backlog enhancements including hierarchical views and support for multiple teams to work from a single backlog. The presentation also demonstrates the enhancements to the TFS Portal and Web Access to support Agile planning and displaying graphs such as burn-down charts. Each walk through includes a live demonstration of the feature in Visual Studio Team System 2010.

I hope to see everyone there!



Thursday, July 09, 2009 12:48:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
HDC | Team Foundation Server

# Tuesday, July 07, 2009

Is Microsoft’s new search engine, Bing, finally making a dent in Google’s dominance?  While overall I have read that they are making a little progress, I have noticed some substantial changes in my blog statistics that I’m jokingly calling the “Bing of Life” attack (as to the Ping of Death).  In fact Microsoft has passed Google for the top spot in referrals.


Although Microsoft doesn’t make up the biggest share of search engine traffic the percentage is close to 40%.  They used to only have 5 to 10% share.


So Microsoft is getting the numbers but how about the quality?  When I compare the search engine phrases, these results still favor Google.  The Bing search phrases appear to be one word items like Server and Deploy.  The Google ones are what I would expect visitors to be searching for to get to my site.  Here’s the comparison:

Google - Phrases example
csla validation 
csla 3.6 codesmith
csla samples 
csla templates 
"The workspace" "already exists on computer"
csla examples

Microsoft Network - Phrases

I don’t get enough traffic to my blog to know if these results are scale to larger sites, but I thought it was interesting anyway.  If you have a website or blog, are you seeing any kind of traffic or percentage increase from Microsoft’s Bing?


Tuesday, July 07, 2009 9:55:00 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -

# Thursday, July 02, 2009

In many of my projects I will create a new release branch when our code is ready for production.  This gives me a snapshot of the code when it went to production.  This allows us to make any bug fixes in the production code base but continue adding new features to dev and test branches.  This has always been a manual step to create the branch until i was talking to a client and suggested that I could automate this process.    I hadn’t done a branch from within Team Build but I knew it could be done via the command line so I figured we could automate it.    I used the Exec task to create the branch like this:

<Exec WorkingDirectory="$(SolutionRoot)"
       Command="$(TF) branch $/MyTeamProject/Main/ $/MyTeamProject/Releases/ /noget"

Next the build needed to check in the branch:

<Exec WorkingDirectory="$(SolutionRoot)"
  Command="$(TF) checkin /comment:&quot;***NO_CI***New Release Created&quot; /noprompt
  /override:&quot;New Release Created&quot; /recursive"
  IgnoreExitCode="true" />

This was the easy part.  One of the challenging parts was the workspace mappings.  Even though I could get this to work by creating a mapping at the root of the team project, I didn’t want to pull down all of the branches under source control.   If I didn’t have the mappings at a high enough level to cover the target, the build will generate an error like this:

No appropriate mapping exists for $/MyTeamProject/Releases/

Since with each release this problem would compound, I wanted to only pull down the necessary source control.  To solve this problem I created a custom task that takes a parent folder, in this case $/MyTeamProject/Releases/ and either cloaks all of the subfolders to only add a new row, or it will cloak all of the folders except for the one specified.   The second option is beneficial if the source folder for the branch is dynamic also.  This will also create a dynamic mapping between the WorkspaceServerItem and WorkspaceLocalItem.

The task is called CreateDynamicMapping and here is the call:


This example uses the ParentFolder to get all of the child folders, loops through each of them and if it finds a match to the WorkspaceServerItem (It won’t in this example because it isn’t a child folder of the ParentFolder) it will keep that folder, otherwise it cloaks the folder so it is not pulled down.

Creating this task was my first opportunity to use the TFS API.  With the help of some great posts by Shai Raiten, this ended up straight forward and even wrapped some unit tests around make sure it worked.  Here’s the main part of the code for the task:

TeamFoundationServer tfs = new TeamFoundationServer(teamFoundationServerUrl);

// Get a reference to Version Control.
VersionControlServer versionControl = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

Workspace[] workspaces = versionControl.QueryWorkspaces(workspaceName, workspaceOwner, null);

if (!parentFolder.Contains("*.*"))
    parentFolder += "/*.*";
    parentFolder = parentFolder.Replace("//", "/");
RecursionType recursion = RecursionType.OneLevel;
ItemSet itemSet = versionControl.GetItems(parentFolder, recursion);
foreach (Item item in itemSet.Items)
    if (workspaceServerItem.ToUpper() != item.ServerItem.ToUpper())

workspaces[0].CreateMapping(new WorkingFolder(workspaceServerItem, workspaceLocalItem));

This is what you need to automate the branching process from with a TFS Build.   I didn’t talk about how to get your version number to branch to.  This will depend on your projects requirements.  You may want to use get the assembly version of one of the assemblies you compiled to use this as the folder or in this instance the source folder was dynamic too.


Thursday, July 02, 2009 5:01:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
Team Build | Team Foundation Server

# Thursday, June 25, 2009


Today I was installing Team Foundation Server 2008 on a Windows Server 2008 server where I installed WSS 3.0 SP2 when I realized I was going to get error connecting to an existing Windows SharePoint Services server.  The error is TF220037: The Windows SharePoint Services administration site could not be found at the following location:  http://myserver:17012



Basically this error means that it can’t find the WSS Central Administration site.  Usually when I see this error it means that the WSS Central Administration site is installed on a different port.  The easiest way I have found to get the correct port number is to open the Central Administration site and look at the port in the URL.   In my example below the correct port is 21810.  I simply updated the port on the installation screen on the left and it worked fine.

To open Central Administration,  go to Start > Administrative Tools > Central Administration.


That’s it! I hope it helps.


Thursday, June 25, 2009 9:04:00 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
Team Foundation Server

# Thursday, May 28, 2009

Thanks everyone for attending the meeting on Tuesday.   It was a great time.  I thought the attendance was good, especially for the day after a holiday and there were a lot of great questions asked.   I hope everyone picked up some good tips that will help them implement Team System and Team Foundation Server.   If you run into any problems feel free to contact me.  I would be happy to try to help.

Here are the slides. (603.07 KB)


Thursday, May 28, 2009 3:31:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [2] -
Team Foundation Server

# Sunday, May 24, 2009

Team Foundation Server 2008 requires administrators to manage user and group permissions in three locations.  Users must be added to the appropriate permissions in TFS, SharePoint, and SQL Server Reporting Services.  Doing this manually is cumbersome.  The TFS Admin Tool is a free add-on to Team Foundation Server that makes this process much easier.

I was installing the latest version 1.4 on Windows Server 2008 and received the message “Please wait while the installer finishes determining your disk space requirements.”  when I clicked on Next after the EULA.


I had a hunch it was UAL and I needed to do “Run As Administrator” to make this work.  Unfortunately with MSIs there is no “Run As Administrator” option in the context menu.  To work around this I opened an “Administrator Command Prompt”.  Here I ran MsiExec against the Msi and it worked fine.

C:\Users\Administrator>msiexec /i c:\users\Administrator\Desktop\TFSAdminToolSetup.msi


Sunday, May 24, 2009 10:18:00 PM (Central Daylight Time, UTC-05:00)  #    Comments [0] -
Team Foundation Server

# Wednesday, May 20, 2009

Last week I added a MSI for installing Team Deploy.   I am excited about this because this simplifies the installation process.  Simply run the MSI and it installs all of the components for Team Deploy.  Team Deploy still requires the separate download and installation of PSTools.   Also, if you have an older version of PSTools, download the latest version to help ensure it works with the latest version of Team Deploy.

Here is the listing of installed files with the MSI.


I have also included a “real world” type deploy script and TFSBuild.proj file based on some great feedback I have received.  These should show some good examples of how to use it.

As always you can find Team Deploy at


Wednesday, May 20, 2009 3:53:00 AM (Central Daylight Time, UTC-05:00)  #    Comments [2] -
Team Build | Team Foundation Server

Visual Studio ALM MVP
Microsoft Visual Studio ALM MVP
<July 2009>
About the author/Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2016
Mike Douglas
Sign In
Total Posts: 107
This Year: 0
This Month: 0
This Week: 0
Comments: 90
All Content © 2016, Mike Douglas
DasBlog theme 'Business' created by Christoph De Baene (delarou)