Deploy ASP.NET projects from TeamCity with Web Deploy
If you have a build setup, and it builds successfully, you might wonder what to do now? Or maybe you know exactly what you want to do. You want to deploy your CrazyCoolProject and show it to the rest of the world, and start to make some money. Well, I can not promise you lots and lots of gold, but I can help you deploy your project from TeamCity, using Microsofts WebDeploy and an Azure Website.
Are you ready for a good time? Then get ready for the
night line deploy pipeline.
If you have read my previous post on how to build ASP.NET applications in TeamCity with MSBuild Tools 2013 and .NET Framework 4.5 SDK, this post will continue from where that left of.
Install Web Deploy
Log on to your TeamCity server and download Web Deploy 3.5. Click “Install this extension” to download WDeploy.exe. This will start to install the Web Platform Installer, and it will automatically select the Web Deploy 3.5 package for installation. Click “Install” and click “I Accept”, but only if you do accept of course. When it’s finished just exit the Web Platform Installer.
TeamCity GUI not available?
If you use the standard port for accessing your TeamCity GUI (port 80), you might run in to some problems after your next reboot of the server. If you try to access your buildserver and you get an error like “The webpage cannot be found” the problem has most likely to do with the installation of webdeploy.
Locate your TeamCity folder and navigate to the catalina log file, eg. TeamCity\logs\catalina.2014-02-18.txt. The error is
SEVERE: Failed to initialize end point associated with ProtocolHandler [“http-nio-80”]
java.net.BindException: Address already in use: bind
If we fire up a command windows and run netstat -n -o, we can see that processId, or PID, number 0 (or some other low number) is using port 80. But PID 0 is the System Idle Process? What is happening? It turns out that the Web Deployment Agent Service runs on port 80 as System. Lets open up the Services settings and scroll down to “Web Deployment Agent Service”. Open it up, and click the Stop button and set its startup type to Manual. Also in Services, find “TeamCity Server”, right click it, and hit Restart. After a while your TeamCity GUI should be responding on port 80 again.
Build package configuration
In Azure Websites you have the option to deploy directly from sourcecontrol eg. a GitHub account. This is a sweet feature when you have a simple solution that does not need the extra possibilities that a deploy from TeamCity can provide. In our first attempt to deploy from TeamCity we will mimic this feature in Azure Websites. By that I mean that this will be a fairly simple deploy-routine. When something happens on the master-branch, TeamCity will build the solution, and deploy it if the build was successful.
If you have not configured a connection to your source control system in TeamCity you should do that now. You can read about how to do it with Git in my previous post.
Now, go to TeamCity -> Administration, on the Projects-page click on the project you wish to deploy. On that page click the button “Create build configuration”. Give the build configuration the name “1. Build package”. In the “Artifact paths”-inputfield paste this
and leave the rest of the options at its default values. I will come back to the artifacts-paths in the next section. Continue with clicking the button “VCS settings”. Select the VCS root, or configure a new one, that you wish to deploy from. In my running example I select “OrbitalJournalism master”. Leave the other options default for now and click “Add Build Step”.
On the page “New build step” set the Runner Type to be MSBuild. Name it something like “Build package”, and set the “Build file path” to %WebProjectName%\%WebProjectName%.csproj and set the “Target” to Package. Click “Save”.
Nuget-Note! If you use nuget-packages you might want to set up an additional build step to download the nugets before building the package. See my previous post for how to set up the Nuget Installer-step.
Set build parameter for packaging
Before we can test the build we need to set the build parameters. On the main page edit the settings for our newly created project configuration and on there on the right menu click “Build Parameters”. My web-project name is OrbitalJournalism.Web so I will set the WebProjectName-parameter to that.
The system.Configuration lets you decide what type of web.config you will use for this build. If this is a release build and deploy to production, you can set this parameter to Release. Then the web.config-transformer will kick in and use the web.release.config file for the transformation. Read more about web.config transformation on Troy Hunts epic post, You’re deploying it wrong. Since I do not have a release web.config in this project, I will set the system.Configuration parameter to Debug.
On the main page hit the Run-button associated with the “1. Build package” configuration. Since this is our first run, let us cross our fingers. There is a few things that can go wrong, but… Keep calm and cross your fingers.
When the build is finished, and successful, click the “Artifacts” link. Here you should see a folder named “PackageTmp” and five other files. These artifacts are some of the files produced by the msbuild-package step. Because we configured a artifacts-path in this buildstep they are going to be kept with the buildresult. The artifacts are what we are going to deploy in the next step.
Deploy package configuration
Now we need to create another build configuration. Where was that again? TeamCity -> Administration -> ProjectName (eg. OrbitalJournalism), and the button “Create build configuration”. Name his one “2. Deploy package”. After typing the name click “VCS settings”, but this time we don’t need to connect a VCS, just click the “Add Build Step” straight away.
On the page “New build step” set the Runner Type to be “Command Line”. Set the Run parameter to “Executable with parameters”. In the textbox for “Command executable” put in the path to msdeploy.exe
"C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe"
And we need some parameters to send to msdeploy.exe. Paste the chunck of text below into the “Command parameters” input field.
-source:package='%teamcity.build.checkoutDir%\%WebProjectName%.csproj.zip' -dest:auto,computerName="%system.MsDeployServiceUrl%",userName="%system.UserName%",password="%system.Password%",authtype="basic",includeAcls="False" -verb:sync -setParamFile:"%teamcity.build.checkoutDir%\%WebProjectName%.csproj.SetParameters.xml" -AllowUntrusted -setParam:"IIS Web Application Name"="%system.SiteName%" -verbose
If you want to know more about each of these parameters, run the msdeploy.exe in a command window, or look at the page for Web Deploy Operation Settings.
After adding this buildstep, we must configure a reference to the package we build in the previous configuration. On the “2. Deploy package” settings-page, click “Dependencies”. Click the “Add new snapshot dependency”, select 1. Build package and hit “Save”. Next we click the “Add new artifact dependency”. In the first dropdown select the one with 1. Build package, and in the dropdown “Get artifacts from” select Build from the same chain. In Artifacts rules enter **.
Once again we need to go into the “Build parameters” page and set some of our parameters there. It is possible to set root-project properties that is shared between the configurations (eg common for “1. Build package” and “2. Deploy package”), but for now each configuration has its own parameters. The ones we need to set are (again) are WebProjectName that should be the same as last time, OrbitalJournalism.Web.
The other parameters are system.MsDeployServiceUrl, system.UserName, system.Password and system.SiteName. The MsDeployServiceUrl is the url to your IIS server where WebDeploy is enabled. The url should look something like this
The parameter sitename is the name of your site on the IIS-server.
Another thing you should do before we are finished is to go to the “General Settings” for the deploy-configuration. In the “Build number format”, clear the input field and start typing %dep. here you can select the build number of the build you are depended on (the one we set up on the dependencies page). Choose the one that ends with .build.number (eg. %dep.OrbitalJournalism_1BuildPackage.build.number%). Remember to Save. Now you can easily see what buildnumber is currently deployed.
Deploy to Azure Website
If you are deploying to an Azure Website, log in to your Azure account, and go to the Dashboard of the website you are deploying to. On the right side click the link “Download the publish profile”. Open the .PublishSettings file in something like notepad. Here you can see all the parameters you will need
publishUrl="waws-prod-am2-001.publish.azurewebsites.windows.net" msdeploySite="orbitaljournalism" userName="$orbitaljournalism" userPWD="Something that look encrypted but is actually not"
Put these into the corresponding parameters in the parameters list of the 2. Deploy package configuration.
Run the deploy
Now we are ready to hit the “Run” button for our “2. Deploy package” configuration. This will trigger a build on 1. Build package and run that if it has changes or if no package exists yet. When 1. Build package is finished (successfully) our deploy starts.
Warning! This is in principle the same as the Azure Websites deploy straight from source-control. So when something changes in your source-control, the new code base will be build and deployed without any human intervention. So be warned.
In the “1. Build package” configuration go to the “Build triggers” page and click the “Add new trigger” button. Choose “VCS trigger” and just click “Save”. And go to the “Build triggers” page for the “2. Deploy package”. Click the “Add new trigger” here as well, but here we choose “Finish Build Trigger”, and in “Build configuration” we select our “1. Build package”, and check the “Trigger after successful build only” option. Hit “Save”.
What is gonna happen now is that when something gets checked in to our VCS, the “1. Build package” will start running. If, and only if, that build was successful the “2. Deploy package” will start running. And since “2. Deploy package” has a dependency to “1. Build package” we are sure that it is the package from the newly ran build that is going to be deployed.
Rollback deployWhat if, for some reason, the deployed package is faulty and you want to re-deploy the previous package? This is super-duper easy since we have the artifacts stored pr. package build. On the main page, locate the button “…” next to the Run-button for our “2. Deploy package” configuration. The “…” button prompts for a custom build. Go to the “Dependencies” tab and in the dropdown you can select the package you want to redeploy.
You are awesomeYou made it? And everything worked out? Great! You are now one of the cool, and smart, guys (and girls!) that has automated your deploy. I recommend scrolling back up and start the AC/DC song again, and think of all the time you are going to save in the days and years to come. From this point on the next thing to do could be to put some more project-configurations between building the package and deploying it. Run some tests, run some db-migrations, deploy to a staging server, run a powershell-script, send a mail to QA etc. Make the whole deploy routine automated. If you can do it manually, you can probably write a script for it.
I hope this post helped you on your way to be a lord of the builds. Any ideas for improving this post? Or any gotchas I should mention? Please let me know in the comments below, any feedback is appreciated.