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.

image

 

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.

image

 

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>.

image

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.

image

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.

image

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(session.property("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
else
    objFile.WriteLine Now & "::CopyConfigs - Completed Successfully."
end if

objFile.Close

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(session.property("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
    else
       objFile.WriteLine Now & "::" & strSvcName & " - " & "Successfully set service account"
    end if
    if err.number > 0 then
        objFile.WriteLine Now & "::" & strSvcName & " - " & err.Description
    end if
    objFile.Close

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 = "";

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

    }
    catch
    {
        sValue = "";
    }

    return sValue;
}       

// Override the 'OnBeforeInstall' method.       
protected override void OnBeforeInstall(IDictionary savedState)
{
    try
    {
    base.OnBeforeInstall(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.

image

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.

image

 

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.

 

Mike

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

Visual Studio ALM MVP
Microsoft Visual Studio ALM MVP
Archive
<February 2012>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910
Blogroll
About the author/Disclaimer

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

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