Wednesday, April 4, 2012

Deploy window service artifact to a remote server after a successful build on TeamCity


There is a new version of this approach: http://thoai-nguyen.blogspot.com.au/2012/10/deploy-dot-NET-window-service-TeamCity-Updated.html




Deploy window services from TeamCity is such a pain since you don't have something like WebDeploy as deploying a web application. Optopus is a product on the market to do it but I don't want to pay for this simple task. So here is the way I did.

Tool: powershell, window commands, wget, 7zip
Prerequisite: TeamCity server and the remote server have to be configured to use Remote Commands in Windows PowerShell


1/ Config both servers follow the above link. TeamCity server has to be a trusted client to the remote server and vice versa

2/ Download wget and put somewhere on the remote server such as C:\DeploymentScripts\wget.exe

3/ Download and install 7zip on remote server where you installed your windows service.

4/ Write deployment script and put somewhere on the remote server such as C:\DeploymentScripts\MyApp\deploy.bat

It can be a powershell script but I decided to use a batch file for simplicity.
Basically, the script when triggered, will

  • Use wget to download the latest artifact from TeamCity.
  • Use 7zip to extract the artifact, copy it to a temporary folder.
  • Save the artifact to Artifacts folder
  • Backup the whole current service folder
  • Stop target window service
  • Copy over new files and old configuration files
  • Start target window service
  • Clean up

Here is the script, replace some value in the script to match your requirement.
@echo off:: Deployment scripts for my window service

SETLOCAL
:: 0/ --------- Set some local variables
SET Environment.ExecutingFolder="C:\DeploymentScripts\MyApp"
SET Environment.7zip="C:\Program Files (x86)\7-Zip\7z.exe"
SET Environment.Wget="C:\DeploymentScripts\wget.exe"
SET TeamCity.User=van
SET TeamCity.Password=vanpassword
SET TeamCity.BuildTypeId=bt19
SET TeamCity.Artifact=MyAppService.{build.number}.zip
SET TeamCity.Branch=Default Branch
SET MyApp.TargetFolderName=MyApp.Service
SET MyApp.TargetFolderPath=C:\WhereMyAppInstalled\%MyApp.TargetFolderName%
SET MyApp.ServiceName=MyAppServiceName
SET MyApp.ImageName=MyApp.Service.exe
CD /D %Environment.ExecutingFolder%

 
ECHO 1/ --------- Get latest artifact from TeamCity, MyApp %TeamCity.Branch% ----------------- bt19: build type ID for MyApp on %TeamCity.Branch%
%Environment.Wget% -q --http-user=%TeamCity.User% --http-password=%TeamCity.Password% --auth-no-challenge http://my.teamcity.url.com/repository/download/%TeamCity.BuildTypeId%/.lastSuccessful/%TeamCity.Artifact%
REN *.zip* *.zip
ECHO Found following artifact
DIR /B *zip


ECHO 2/ --------- Extract the artifact to folder __Temp ---------------
%Environment.7zip% e -y -o__Temp *.zip


ECHO 3/ --------- Store the artifact ------------------ 
MOVE /Y *.zip Artifacts\


ECHO 4/ --------- Backup current service folder --------------- 
for %%a in (%MyApp.TargetFolderPath%) do set Temp.LastDate=%%~ta
SET Temp.LastDate=%Temp.LastDate:~6,4%-%Temp.LastDate:~0,2%-%Temp.LastDate:~3,2% %Temp.LastDate:~11,2%%Temp.LastDate:~14,2%%Temp.LastDate:~17,2%
ECHO Last deployment: %Temp.LastDate%
ECHO Now backup files to folder %MyApp.TargetFolderName%.%Temp.LastDate%
XCOPY /E /I /H /R /Y %MyApp.TargetFolderPath% "%MyApp.TargetFolderName%.%Temp.LastDate%"


ECHO 5/ --------- Stop %MyApp.ServiceName% service ---------------
net stop %MyApp.ServiceName%
ECHO Wait 15 seconds
ping 1.1.1.1 -n 1 -w 15000 > nul
ECHO Try to kill the process %MyApp.ImageName% if it's still running
TASKKILL /F /FI "IMAGENAME eq %MyApp.ImageName%" /T > nul

ECHO 6/ --------- Deploy new files & copy over old configs ----------------------
XCOPY /E /H /R /Y __Temp %MyApp.TargetFolderPath%
COPY /Y "%MyApp.TargetFolderName%.%Temp.LastDate%\*.config" %MyApp.TargetFolderPath%


ECHO 7/ --------- Start %MyApp.ServiceName% service ---------------
net start %MyApp.ServiceName%


ECHO 8/ --------- Cleanup --------------------------------- 
RD /S /Q __Temp
ENDLOCAL

5/ Open powershell as Administrator on TeamCity server and execute following command:
read-host -assecurestring | convertfrom-securestring | out-file C:\Users\Administrator\MyAppSecurePassword.txt

Then enter the password for the remote server. The secure password will be stored in the above file. It will then be used by TeamCity for future powershell commands against the remote server.

6/ Go to TeamCity management website, add a new project with 1 step that will execute the following powershell command:
$username = "Administrator"
$password = cat C:\Users\Administrator\MyAppSecurePassword.txt | convertto-securestring
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
$serverNameOrIp = "xxx.xxx.xxx.xxx"
Invoke-command -computername $serverNameOrIp -ScriptBlock {C:\DeploymentScripts\MyApp\deploy.bat} -Credential $cred

Notes:
  • Administrator is the Admin username on the remote server
  • C:\Users\Administrator\MyAppSecurePassword.txt is the secure file that store the secure password we created at step 5.
  • serverNameOrIp value has to be changed to the remote server's ip address where you installed the windows service

So after the code is checked in and successfully built, we can trigger the deployment by clicking "Run" from the above created project. The deploy.bat script above can be triggered anytime from the remote server for testing purpose. It's simple enough to read and modify when more steps need to be involved.


Cheers

0 comments:

Post a Comment