Deploying Enterprise Templates
Infusion Development Corporation (http://www.infusiondev.com)
Microsoft® Visual Studio® .NET 2003
Summary: Learn how to deploy Enterprise Templates with Visual Studio .NET 2003 and Active Directory. (14 printed pages)
Enterprise Templates allow architects and managers to promote and enforce project structures and design patterns in Microsoft® Visual Studio® .NET 2003. An Enterprise Template prescribes the project skeleton a user receives when he or she creates a project with that template. See the Enterprise Templates Booklet for a thorough treatment on creating and exposing Enterprise Templates in Visual Studio .NET. This article illustrates the last step in the Enterprise Template cycle: deployment.
As with any software component, an Enterprise Template must be deployed to the machines on which it will be used. For templates, this process consists primarily in copying the appropriate files to Visual Studio .NET's subdirectories. This article highlights the techniques that can be used to facilitate this procedure. Of particular importance is the use of Active Directory to deploy templates in corporate environments with potentially hundreds of machines.
An Enterprise Template consists of numerous files that specify the structure, constraints and content of the template. As shown in Table 1, these files must be copied to different subdirectories within the main Visual Studio .NET directory (which we will denote %VSNETRoot% and is usually located in C:\Program Files\Microsoft Visual Studio .NET 2003).
Table 1. Enterprise Template File Locations
|File (or extension)||Location within %VSNETRoot%||Purpose|
|YourCustomHelpFile.xml||Common7\IDE\HTML\XMLLinks\1033||Visual Studio .NET consults the files within this directory to determine how it formats its Dynamic help Window. You place custom XML files within this directory to give a template custom help. (Note that 1033 is the language code for English; it will differ for other language versions of Visual Studio .NET.)|
|.csproj, .vbproj and .vcproj (Language Project files)||EnterpriseFrameworks\Projects\SomeDir||A Visual Studio .NET language project file. A project file points either to a bona fide static project structure that is recreated verbatim by Visual Studio .NET for a template instance, or it points to an empty project file to which JScript wizard code dynamically adds content.|
|.html or .hxs||User defined location||Custom help content for the template. Such content can be packaged as either regular HTML (.html) or in Microsoft compiled help format (.hxs). Custom help files can reside in any directory.|
|.tdl||EnterpriseFrameworks\Policy||The Template's policy file, written in Template Description Language, which allows one to customize, control and restrict the Visual Studio .NET environment when an instance of the template is created.|
|.etp||EnterpriseFrameworks\Projects||The main template file that is used by Visual Studio .NET to create instances of the template.|
|.js||EnterpriseFrameworks\EFWizards||Microsoft JScript® files are used with template subproject wizards. Subproject wizards allow you to write JScript code that is executed on-the-fly when a project instance of the template is created in Visual Studio .NET. In effect, subproject wizards allows you to insert custom programmatic operations into the template creation process. Much of the flexibility afforded by Enterprise Templates is a result of this capability.|
|.vsdir||EnterpriseFrameworks\ProxyProjects||Visual Studio .NET consults .vsdir files within the ProxyProjects directory to populate the project options that appear in the Add New Project and New Project Dialog Boxes. To expose your template to Visual Studio .NET you must create a custom .vsdir file and place it within this directory.|
|.vsz||EnterpriseFrameworks\SomeDir||The .vsz file is used with subproject wizards and is serves as a "pointer" to the real wizard that performs the programmatic operations.|
In essence, deploying an Enterprise Template consists in copying the files outlined in Table 1 to their appropriate directories. There are numerous techniques to perform this operation. In this article we will consider three approaches: a batch file, a Microsoft Installation (.MSI) file, and deployment by means of Active Directory. Whatever the technique, there are some general guidelines a deployment procedure should adhere to:
- It should be a one-step process—the user should not be prompted for information as the destination directories are known a priori (custom help files are an exception, but even these files can be placed in a prescribed directory). Once the user initiates the installation procedure, it should gracefully and silently copy the files of the template.
- It should check for the existence of Visual Studio .NET 2003 prior to the installation routine. This can be accomplished by means of checking the registry or the Visual Studio .NET root directory itself.
- The installation process should not require the user to close any open instances of Visual Studio .NET (although some aspects of a template such as custom content require the IDE to be reopened to take effect).
Technique #1: Batch File
One way to deploy an Enterprise Template is to create a batch file that is executed from the command prompt (batch files, of course, herald from the pre-Microsoft Windows® days of DOS). As a batch file is really a sequence of programmatic operations that are executed on-the-fly by Windows, there are many ways to a write a batch file that deploys an Enterprise Template. One such example is given in Listing 1. The first parameter of this batch file must be the %VSNETRoot% directory.
Listing 1. AddTemplate.cmd: batch file to copy an Enterprise Template
@echo off :: Ensure Visual Studio .NET home directory was passed as a parameter: if %1 == "" goto Usage :: Map the network folder that contains the Enterprise Template files: net use z: \\SomeComputer\SomeFolder :: Ensure that all the ET subdirectories exist: if not exist %1 goto NoNewTemplatesDirectory if not exist %1\Policy goto NoNewPolicyDirectory if not exist %1\EFWizards\MyWizard goto NoNewEFWizardDirectory if not exist %1\"CSharp Building Blocks" goto NoNewBuildingBlocksDirectory if not exist %1\Projects goto NoNewProjectsDirectory if not exist %1\ProxyProjects goto NoNewProxyProjectsDirectory :: Create the directories to store the new template: mkdir %1\Projects\MyTemplate mkdir %1\ProxyProjects\MyTemplateFolder :: Copy the template files: xcopy z:\Policy\MyPolicy.TDL %1\Policy\ /s xcopy z:\EFWizards\MyWizard\* %1\EFWizards\MyWizard\ /s xcopy z:\"CSharp Building Blocks"\* %1\"CSharp Building Blocks"\ /s xcopy z:\Projects\MyTemplate\* %1\Projects\MyTemplate\ /s xcopy z:\ProxyProjects\MyTemplateFolder\* %1\ProxyProjects\MyTemplateFolder\ /s goto Exit :: Display batch file usage conventions: :Usage echo Usage: AddTemplates vsefdir newtemplatesdir echo Parameters: echo vsefdir - The EnterpriseFrameworks directory of the current Visual Studio install echo newtemplatesdir - The directory containing the Visual Studio .NET files. echo These files should be stored under newtemplatesdir echo in subdirectories named Policy, Projects, and ProxyProjects goto Exit :NoNewTemplatesDirectory echo The specified Visual Studio .NET directory, %1, does not exist. goto Exit :NoNewPolicyDirectory echo The specified Visual Studio .NET directory, %1, does not have a 'Policy' subdirectory. goto Exit :NoNewEFWizardDirectory echo The specified Visual Studio .NET directory, %1, does not have a 'Policy' subdirectory. goto Exit :NoNewBuildingBlocksDirectory echo The specified Visual Studio .NET directory, %1, does not have a 'Policy' subdirectory. goto Exit :NoNewProjectsDirectory echo The specified Visual Studio .NET directory, %1, does not have a 'Projects' subdirectory. goto Exit :NoNewProxyProjectsDirectory echo The specified Visual Studio .NET directory, %1, does not have a 'ProxyProjects' subdirectory. goto Exit :: Cleanup operations -- disconnect from network drive :Exit net use z: /delete
The batch file in Listing 1 is merely an illustration of the required boilerplate operations; it does not include robust error-handling mechanisms that a corporate deployment scheme would most certainly require. In addition, the illustrated batch file requires the user to specify the location of the Visual Studio .NET home directory. In the ideal, a polished batch file would obtain this information automatically from the registry. The batch file procedure has some generic drawbacks as well:
- There are no rollback procedures (as there are with .MSI files).
- Limited programming flexibility (for instance, error handling) with batch files.
- No built-in facilities for extracting/making registry entries.
Technique #2: Microsoft Installation (.MSI) Files
Microsoft Installation (.MSI) files work in conjunction with the Microsoft Windows Installer, which was first packaged with the Microsoft Windows 2000 and Microsoft Windows Millennium operating systems (and is available as redistributable for Microsoft Windows 95 and Windows 98 and Microsoft Windows NT® 4.x). When an .MSI file is run from the Windows Explorer or Command Prompt, the Windows Installer executes the installation steps embedded within it. Most often, these steps consist in gathering installation parameters from the user (for example, program options and/or directory locations), and copying program files to their appropriate directories. In addition to installation capabilities, the Windows Installer is equipped to perform uninstall actions or recover from errors that occur during the installation process.
The Enterprise Developer and Enterprise Architect versions of Visual Studio .NET include the capability to produce .MSI files. This is accomplished by means of the Setup and Deployment Project types exposed by the environment. In this section we illustrate how to use Visual Studio .NET to create an .MSI file that deploys an Enterprise Template.
Tangent: Setup and deployment projects in Visual Studio .NET
Before we illustrate how to create an .MSI file to deploy Enterprise Templates it is worthwhile to outline the general procedure of creating a setup project in Visual Studio .NET. As illustrated in Figure 1, Visual Studio .NET includes a Setup Project template to create .MSI files.
Figure 1. Setup Projects in Visual Studio .NET
When you create a project with this template the Visual Studio .NET environment manifests itself into the rather odd appearance illustrated in Figure 2.
Figure 2. The Visual Studio .NET IDE for Setup projects
The premise behind Figure 2 is straightforward: simply drag the files that constitute your application into the Application Folder (as well as icon shortcuts into the User's Desktop and User's Programs Menu folder). When you build the project in Visual Studio .NET, instead of producing an .EXE or .DLL, the environment produces an .MSI file that contains all the files dragged into the Application Folder. When the .MSI file is executed on another machine, the Windows Installer prompts the user for the destination directory and then un-compresses and copies all the project files to it.
Example: Create an Enterprise Template .MSI in Visual Studio .NET
Installing an Enterprise Template differs from regular program installation in that the destination directories are known ahead of time (the user need not be prompted for a directory). Furthermore, the directory locations for a template (the Visual Studio .NET program directory) must be extracted from the registry during installation time. This more complicated approach requires the creation of two projects in Visual Studio .NET:
- A Setup project to produce the .MSI file you will deploy to other machines.
- A Microsoft Visual Basic® .NET application that determines the Visual Studio .NET root directory by means of the registry, and then performs the actual file copying of the Enterprise Template files.
These two programs work in conjunction as follows:
- For the Setup project, instead of dragging the files of the template into the Application Folder directory (see Figure 2), you will instead drag them into the User's Personal Data Folder (this will be illustrated shortly). This folder will function as a temporary directory of sorts—the location where the Windows Installer will uncompress the template's files.
- Next, you will instruct the Setup project to run the Visual Basic .NET application. This application will determine the Visual Studio .NET root directory by means of the registry, and then copy the files from the aforementioned temporary directory (User's Personal Data Folder) to the proper Visual Studio .NET directories.
The step-by-step procedure to create both of these projects is given below:
- Open Visual Studio.NET.
- On the File menu, point to New, and choose Project.
- In the New Project dialog box, select Visual Basic Projects in the Project Type pane, and then choose Windows Application in the Templates pane. In the Name box, type "MyInstaller"
- Click the OK button to close the dialog box. The project is added to Solution Explorer.
- On the Project menu, choose Add Module, then select Module in the Add New Item dialog box. Change the name to "ActionModule.vb" and click the Open button to close the dialog box.
- Use the Code Editor to add the code in Listing 2 to ActionModule.vb. This code retrieves the Visual Studio .NET root directory by means of the registry, moves the template files from their temporary directory to the Visual Studio .NET and, finally, deletes the temporary files.
Listing 2. ActionModule.vb: Visual Basic .NET code to copy Enterprise Template files
Imports System.IO Imports System.IO.Path Imports System.IO.Directory Imports Microsoft.Win32 Module ActionModule Public Sub Main() Dim destRoot As String Dim srcRoot As String Dim regLocal As RegistryKey = Registry.LocalMachine destRoot = regLocal.OpenSubKey("SOFTWARE").OpenSubKey ("Microsoft").OpenSubKey("VisualStudio").OpenSubKey ("7.1").GetValue("InstallDir") destRoot = destRoot.Remove(destRoot.Length - 12, 12) & "EnterpriseFrameworks" Dim regUser As RegistryKey = Registry.CurrentUser srcRoot = regUser.OpenSubKey("SOFTWARE").OpenSubKey ("Microsoft").OpenSubKey("Windows").OpenSubKey ("CurrentVersion").OpenSubKey("Explorer").OpenSubKey ("User Shell Folders").GetValue("Personal") srcRoot &= "\ET\" MoveFolder(srcRoot, destRoot) End Sub Public Sub CopyFile(ByVal fileIn As String, ByVal fileOut As String) Dim fileReader As StreamReader Dim fileWriter As StreamWriter Dim str As String fileReader = File.OpenText(fileIn) fileWriter = File.CreateText(fileOut) While fileReader.Peek <> -1 fileWriter.WriteLine(fileReader.ReadLine()) End While fileReader.Close() fileWriter.Close() End Sub Private Sub MoveFolder(ByVal srcDirIn As String, ByVal destDirIn As String) Dim srcDir As String Dim srcFile As String Dim srcFileInfo As FileInfo Dim destFileInfo As FileInfo Dim destDirInfo As DirectoryInfo Dim srcDirInfo As DirectoryInfo ' Add trailing separators to the supplied paths if they don't exist. If Not srcDirIn.EndsWith(DirectorySeparatorChar.ToString()) Then srcDirIn &= DirectorySeparatorChar End If If Not destDirIn.EndsWith(DirectorySeparatorChar.ToString()) Then destDirIn &= DirectorySeparatorChar End If 'If destination directory does not exist, create it. destDirInfo = New DirectoryInfo(destDirIn) If destDirInfo.Exists = False Then destDirInfo.Create() destDirInfo = Nothing ' Get a list of directories from the current parent. For Each srcDir In GetDirectories(srcDirIn) srcDirInfo = New DirectoryInfo(srcDir) destDirInfo = New DirectoryInfo(destDirIn & srcDirInfo.Name) ' Create the directory if it does not exist. If destDirInfo.Exists = False Then destDirInfo.Create() ' Since we are in recursive mode, copy the children also MoveFolder(srcDirInfo.FullName, destDirInfo.FullName) srcDirInfo = Nothing destDirInfo = Nothing Next ' Get the files from the current parent. For Each srcFile In GetFiles(srcDirIn) srcFileInfo = New FileInfo(srcFile) destFileInfo = New FileInfo(Replace(srcFile, srcDirIn, destDirIn)) 'If File does not exist. Copy. If destFileInfo.Exists = False Then srcFileInfo.CopyTo(destFileInfo.FullName) End If srcFileInfo = Nothing destFileInfo = Nothing Next End Sub End Module
- In the Solution Explorer, select the MyInstaller project. On the Project menu, choose Properties. In the ActionModule Property Pages dialog box, select the Startup object property and set it to Sub Main. Click the OK button to close the dialog box.
- On the Build menu, choose Build MyInstaller.
- If you want, you may remove the Form1.vb file as it will not be used. On the Project menu, right click on Form1.vb and choose delete. Click the OK button and the file will be removed.
Next, add the deployment project to the Visual Studio .NET Solution:
- On the File Menu, point to Add Project, and select New.
- In the Add New Project dialog box, select Setup and Deployment Projects in the Project Type pane, and then choose Setup Project in the Templates pane. In the Name box, type "CustomActionInstaller".
- Click OK to close the dialog box. The File System Editor is displayed.
- In the File System Editor, right click the File System on Target Machine. Choose Add Special Folder and then select User's Personal Data Folder (see Figure 3). This folder is the equivalent of the user's My Documents folder and will be used as a temporary location to uncompress and copy files.
Figure 3. Adding a Special Folder to Visual Studio .NET
- Open an instance of Windows Explorer and navigate to the folder containing your Enterprise Template files (it is a good idea to copy all the template files and folders to a given subdirectory on the system for easy access).
- Drag these files into the User's Personal Data Folder.
Next, you must instruct the Setup Project to run the Visual Basic .NET program after it has uncompressed the template files to the User's Personal Data Folder.
- In the File System Editor, right click the application folder. On the Action menu, point to Add, and choose Project Output.
- In the Add Project Output Group dialog box, select the Primary Output for the MyInstaller project. Click OK to close the dialog box.
- Right click the CustomActionInstaller project in the Solution Explorer. On the View menu and choose Customs Actions from the list. The Customs Actions Editor is displayed.
- In the Custom Actions Editor, right click the Install node. On the Action menu, choose Add Custom Action.
- In the Select Item In Project dialog box, double-click the Application Folder.
- Select the Primary output from MyInstaller item.
- Click the OK button to close the dialog box.
- In the Properties window, select the InstallerClass property and set it to false.
Next, customize the project's User Interface so as not to prompt for the user for a destination directory:
- Right click the CustomActionInstaller project in the Solution Explorer. On the View menu and choose User Interface from the list. The User Interface Editor is displayed.
- Within the Install section, right click the Installation Folder item and choose delete. This form gives the developer the opportunity to select a destination folder which is something we do not want—and hence must delete.
- Within the Install section, right click the Confirm Installation item and choose Delete.
- Within the Install section, select the Welcome item.
- In the Properties window, select the WelcomeText property and set it to an appropriate message such as:
"The Enterprise Template is being copied and configured. Please wait."
- Within the Install section, select the End item.
- In the Properties window, select the UpdateText property and set it to an appropriate message such as:
"The Enterprise Template has been successfully moved to your machine. The template will appear in Visual Studio .NET's New Project Dialogue Box."
Finally, create the .MSI file which will be used to deploy the template:
- On the Build menu, choose Build CustomActionInstaller.
- The resulting file, CustomActionInstaller.MSI, can now be copied and executed on other machines to deploy the template.
As can be seen, creating an .MSI file for Enterprise Template deployment can be somewhat tricky given the interplay between the Visual Basic .NET application and the Setup project (note that other approaches such as C# Console application may also be used). However, this effort is worthwhile as the user is presented with a straightforward installation procedure that consists of a single .MSI file. Moreover, since .MSI files are executed by the Windows Installer, uninstall facilities and error recovery features are automatically a part of the installation routine. Lastly, .MSI files work with our next deployment scheme—Active Directory.
Technique #3: Active Directory
Active Directory, introduced with Windows 2000, allows organizations to centrally manage and share information on network resources. One of the features of Active Directory is the ability to create a Group Policy that allows an administrator to customize the end user's desktop environment. Part of the Group Policy's functionality is Software Installation Services that allows the Administrator to install software to the user's computer. To leverage Software Installation Services you must add an .MSI file to the Group Policy for the Active Domain. The MSI file is deployed the next time a user logs in.
The step-by-step procedure to leverage Software Installation Services by means of Active Directory is given below:
- Run the Active Directory Users and Computers snap-in.
- Find the Organizational Unit you wish to apply the policy to.
Note For those not familiar with Active Directory, an Organizational Unit is a branch of the directory tree which represents a logical division within an organization. An Organizational Unit (and the directory objects within them, be they users, computers, printers, file shares) inherits certain security attributes (Access Control Entries, or ACEs). They may inherit Group Policy settings as well (unless the OU has been configured to deny policy inheritance), and they may have their own Group Policy Objects (GPOs) assigned to them.
- Click the Action menu and choose the Properties option.
- On the Properties screen select the Group Policy tab.
- Choose New and give the new Group Policy Object a name (for example, Enterprise Template Distribution).
- Select the new Group Policy Object and click the Edit button. The Group Policy screen will appear.
- Within the Tree tab choose User Configuration, then Software Settings and then Software Installation.
- From the Action menu choose New and then Package. An Open window will appear.
- Navigate to the location of your MSI file. Click the OK button. The Deploy Software window will appear.
- On the Deploy Software window, choose the Assign option. Click the OK button.
- Right-click on MyInstaller (MSI name) and choose Properties.
- Within the Properties window, choose the Deployment tab. Check the Uninstall this application when it falls out of the scope of management and Do not display this package in the Add/Remove Programs control panel. Click the OK button.
- Close the Group Policy Editor.
- In the Organizational Unit Properties window, select the Group Policy tab and press the Properties button. This will open the Enterprise Template Distribution Properties window.
- Choose the Security tab and click the Advanced button. The Access Control Setting For Enterprise Template Distribution window opens. On the Permissions tab find the Authenticated Users with AppyGroupPolicy permission. Press the Remove button to delete this entry.
- Click the Add button. This will open the Select User, Computer, or Group screen. Choose the users or groups you want the Policy to be applied to. Click the OK button.
- Scroll down the Permissions list until you find Apply Group Policy. Check the Allow box associated with it.
- Close all the open screens by clicking the OK button.
- Wait for the Group Policy refresh interval to pass and then notify the developers of the update.
Deployment by means of Active Directory is ideal when targeting a large number of machines or developers. The hassle to the user is minimal—they simply have to log off or restart their machine. However, the procedure requires the cooperation of the network administrator and error messages are displayed in the EventLog.