We have been looking at TFS Deployer for a long time to automate further some of our processes. Today (mid last year, I’ve been sitting on this draft for a while) it finally became high enough priority to get it going. This will allow us to automatically deploy to test server, verify the build, and deploy to a wider internal audience to allow us to get early feedback on our latest product development.
I downloaded the latest source in case I had issues, I would be able to debug them and rebuild the solution myself quickly. I opened TFS Deployer\Trunk\TfsDeployer\TfsDeployer.sln and built the solution. Looking at the installation instructions I was disappointed to find there was no installer and this would be a bit of a manual process. Fortunately however, the instructions are well detailed.
Installing TFS Deployer
Step 1: Copy the application files: I copied from TFS Deployer\Trunk\TfsDeployer\TfsDeployer\bin\Release files, TFSDeployer.exe, TFSDeployer.exe.config and Readify.Useful.TeamFoundation.Common.dll to the path C:\Program Files\TFS Deployer on the TFS Server. I later found that there is an installer to do this, but I don’t know what version it is.
Step 2: Install the service: The service can install itself using TFSDeployer.exe –i. Nice.
Step 3: Determine the service account: I reviewed the permissions required and decide I would like to run this under our TFSService account. It is not a local administrator, but does have the required privileges into TFS.
Step 4: Edit the configuration file: Well documented what each key is and what needs to be set. Although for the most part these are self explanatory, it would be nice to have the description directly in the config file also. I uncomment the system.diagnostics section to allow errors and warnings to be logged to the event viewer.
Step 5: Start the service: Set the service account to the one from Step 3 and start the service. Service starts, then stops. Hmm….
On to the Troubleshooting. I started up a console running as TFSService and ran TFSDeployer.exe –d.
Permission error creating the WCF endpoint. In step 3 were the instructions using httpconfig to give permission for the URL to the required account, TFSService. However I followed the URL given in the error message http://go.microsoft.com/fwlink/?LinkId=70353. This gave me the instructions for giving permissions without having to use a 3rd party application. I run the command: netsh http add urlacl url=http://+:8881/BuildStatusChangeEvent user=<domain>\TFSService, which returns, URL reservation successfully added. Sounds good. (I found this later detailed here). I start the service again and this time it works. To confirm the registration of the service to the TFS Event system, I have a look in the TfsIntegration database, table dbo.tbl_subscription, and I see an entry for BuildStatusChangeEvent at address http://<machinename>:8881/BuildStatusChangeEvent.
Preparing the Deployment Script
Step 1: Create Deployment Script: Create Deployment directory under the TeamBuildTypes/<Build Definition Name> directory in the source control explorer.
Step 2: Create the Mapping File: The file template link is dead, but there is the text just below the link. I copy and paste it into a new file DeploymentMappings.xml which is added to the new folder in the source control explorer. Opening this file with Visual Studio which has the open solution allows it to pick up Intellisense from the xsd from TFS Deployer\Trunk\TfsDeployer\TfsDeployer\Configuration\DeploymentMappings.xsd. Updating the mappings for the transition states is straight forward. Firstly however, I needed sort out exactly what our build quality states should be. This is done by clicking the Manage Build Qualities button on the Build Explorer.
Step 3: Create PowerShell script: There is a demo script on the page to help get started. What is also good and interesting point mentioned is that you have access to the TFS IBuildDetail, making it very simple to retrieve the required paths. I create a script to print out all the available properties to the event viewer (found in TfsDeployer\TfsDeployer\Samples\PrepareForInvestigation.ps1) and then check in the Deployment folder with the mappings file and my script.
Step 4: Change the Build Quality to Test it: I opened up the build explorer, changed the build quality, and refreshed my Event Viewer on the TFS server where TFS Deploy is. Nothing? Huh? I subscribe to the BuildStatusChangeEvent alert through the alerts dialog.
Changing the build quality does send me an email. I check the URL http://<tfsserver>:8881/BuildStatusChangeEvent/BuildStatusChangeEvent and get an empty page with no errors, so I assume the web service is working. I then change the build quality on the Nightly build definition, rather than the Continuous that I have configured TFS Deployer against. Another look in the event viewer is an entry from TfsDeployer, Reading Configuration File:C:\Users\TFSService\AppData\Local\Temp\415dab85-39cb-4e8c-8518-3be1ad6d1e4c\DeploymentMappings.xml failed. The file also does not exist. I assume this is an error because this is no mappings file for this build definition, so it is working for the continuous, just not executing the script properly. Either my script has an error, or it is not running at all. I set the value on the switches to 3 on the TFSDeployer.exe.config for Informational message to be logged, restart the service and change the build definition again. The last of the 11 logged messages is the Eval results: isComputerMatch=False, isOldValueMatch=True, isNewValueMatch=True, isUserPermitted=True. Now it is clear to see what I did wrong here. Now I get Matching mapping found, running script TestTfsDeployScript.ps1 but the script does not appear to run. I set the execution policy of PowerShell to remote signed, Set-ExecutionPolicy RemoteSigned, but still nothing. I started the remote debugger, attached to the process, and stepped through to see what was going on. While it was paused, I had access to the script it had downloaded and run it as TFSService. That worked. I then continued stepping through the code and then… everything worked!? I don’t know exactly why it is all working on that machine now, but it does, and it works a treat.
Most of my actions from the Build Quality change will be deploying the application to different servers. The script will be the same with different server and database connection details. The is where the feature Shared Deployment Resources feature comes in very nicely. This setting, SharedResourceServerPath, is in the TFSDeployer.exe.config. After setting the value to a source control path, i.e. $/MyProj/TeamBuildTypes/Deployment/. Restart the TFS Deployer service to ensure that the new setting is picked up. Extract the content of the deployment script into a new parameterized script saved to the shared deployment directory. Below is the code snippet for a named parameter script with default values.
The script will now be downloaded to the same directory as the mapping script. However, the executing directory is the default (usually C:\Windows\System32) and the mapping script is started up using the full path. Calling the shared script therefore cannot be done with .\ScriptName.ps1, however, it is easy to get the full path of the current executing and call the shared script with that path.
Set User Permissions
Manage Build Quality should allow be done by Quality Assurance. Fortunately, this is very easy to restrict. Right-click on the Project node in Team Explorer and select Security… The complete explanation of the permissions are available here, but all that is required is to Add the Quality Assurance group and select, Edit build quality and Start a build.
Deployment scripts have the potential to do some powerful things, allow the deployment folders to only be checked out/in by Senior Developers. In the Source Control Explorer, right-click the Deployment folder and select Properties… Select the Security tab, uncheck Inherit security settings and check all permissions for groups that require full access (e.g. Administrators, Service Accounts), and give Contributors just Read permission. Ensure your TFSDeployer service user can still access the path.
Improving the installation experience
Now that I have been through all that, I do not want to have to repeat it for each server I wish to put TFS Deployer on. So I have created a WiX unattended installer that will do the steps in the Installing TFS Deployer section above. That is, deploying the program files, setting the config file, installing the service with a specified service user, give the user permission to the URL Reservation and set remote signed execution permission for PowerShell. Now I just need to run this msi from the command prompt with the parameters for my configuration.
C:\Users\Matthew>msiexec /i TfsDeployer_Setup.msi SERVICE_ACCOUNT=<domain>\tfsService SERVICE_ACCOUNT_PASSWORD=tfsServicePassword TFS_URL=http://tfs:8080 SMTP_SERVER=mail.<domain.com> TO_ADDRESS=matthew@<domain.com> FROM_ADDRESS=tfsDeployer@<domain.com>
- Not specifying the SERVICE_ACCOUNT will install the service and attempt to start as LOCAL SYSTEM, which will most likely fail to connect to TFS.
- The Base URL is automatically set as http://<ComputerName>:8881.
- The installer only works on Vista or above. This is due to using netsh for http reservation and I did not want to spend the time getting the Access Control List (ACL) in the form of a Security Descriptor Definition Language (SDDL) string for use with httpcfg on Windows Server 2003 and Windows XP.
- If the service fails to install or start you will get the option to ignore. This will allow everything to be in place to diagnose with TFSDeployer.exe -d.
- To debug errors during the installation add /lv* install.log.
|The TfsDeployer installer. Built from change set 29041.||TfsDeployer Setup Project container Setup.wixproj, and WiX files (Product.wxs, WixUI__en-us.wxl) to build installer from TfsDeployer source.||Patch for TfsDeployer project to apply to the Trunk. Contains Setup project and WiX files.|