Showing posts with label MSBuild. Show all posts
Showing posts with label MSBuild. Show all posts

Sunday, February 9, 2014

PSBuild makes working with MSBuild.exe easier from PowerShell

PSBuild is a project started by Sayed Hashimi to simplify working with MSBuild.exe from PowerShell. It is hosted on gitHub.  I really like this project because I love PowerShell and if I can do things in PowerShell then I am more inclined to use PowerShell first. 

PSBuild installs into your Modules folder and is available once you install it by the following very simple command. I love projects that go one extra mile to make setup as easier as possible.  Open PowerShell and copy the following command and paste into console by just right clicking your mouse.

(new-object Net.WebClient).DownloadString("https://raw.github.com/ligershark/psbuild/master/src/GetPSBuild.ps1") | iex

Once you have installed it, you can list all the commands available by this command.

Get-Command –Module PSBuild

image


The most important command we are interested here is Invoke-MSBuild cmdlet.

Get-Help Invoke-MSBuild

There are lots of examples on how to use Invoke-MSBuild

Get-Help Invoke-MSBuild -Examples

image


MSbuild is very powerful and rightly so as it is used to build visual studio projects.  With Visual Studio 2013 MSbuild is being shipped with Visual Studio and is now referred to as MSBuild 2013. Previously it was shipped with .Net framework and versioning was also different now the version is bumped up to 12.0 from 4.0 to match with VS2013 (it is 12.0). It also comes as separate package known as Build Tools Package. This allows you to install the Build Tools Package as standalone and create a light weight build server. Path to MSBuild has also been changed and Invoke-MSBuild makes it easier for us to not know these different versions and hunt around for paths.  If you want to get MSBuild.exe then you can use one of the cmdlets as


Get-MSbuild


image


Another one to check the versions


& (Get-MSBuild) /version


image


If you want to get the MSBuild.exe help then


& (Get-MSBuild) /help


As you have noticed,  you can invoke MSbuild like this if you want but Invoke-PowerShell is better because you don’t have to second guess the syntax of properties and target that you will specify.  You pass properties as hash table. Let’s do something interesting here.


I am going to create an empty asp.net mvc website and try to build it and then publish to IIS website using a publish profile.


image


Simple call to build the project.
PS C:\BadSourceCodes\demoWebApp\demoWebApp> Invoke-MSBuild .\demoWebApp.csproj


image


You can get the latest log file


PS C:\Get-PSbuildLog


And if you want to open the log file then


PS C:\Open-PSBuildLog


Since this is a web project you can setup web publishing inside visual studio.  You can see these series of article on how to do web publishing here. I have created a website in IIS7 and setup Web Deploy publishing for that website.  Thanks to Sayed for his blog post - I was able to publish my website to the IIS website I by passing these parameters.


>Invoke-MSbuild .\demoWebApp.csproj –DefaultProperties (@{'Configuration'='Release';'DeployOnBuild'='true';'PublishProfile'='Default Settings';'username'='myusername';'password'=’password';'AllowUntrustedCertificate'='true'})


image


With PSBuild you can create a script that you can schedule to auto run on your build server that will automatically build the project and deploy the web project to IIS website.


Go check out the project on github and happy automating.

Thursday, February 28, 2013

Web Deploy Check list for deploying website to IIS7 from Visual Studio and TFS

Web Deploy is one of the mechanisms to publish websites to IIS from Visual Studio.  You can utilize this mechanism to publish from Visual Studio, TFS or manually if you have existing website packaged as a zip file.  If you haven’t checked this gem of resources (Vishal Joshi's Blog) on web deploying and publishing then please do yourself a favor.  In this post I am going to talk about a checklist to make sure before you wish to publish site to IIS.  Here is the list:

  • Installed the latest Web Deploy on windows server (Check this IIS website).  
  • Web Management Service is started and running
  • Web Deployment Agent Service started and running
  • Created a website inside IIS and created application pool for it  
  • Given the user IIS Manager Permissions at the site level inside IIS
  • Configured website for Web Deploy publishing
  • Saved Web publish settings on a local desktop
  • Import Web publish settings from the local desktop from Visual Studio publish dialog
  • Publish url is over Https 
  • Checked connectivity to the site from the Visual Studio publish dialog 

Now before you jump on to the TFS side of things to publish, please make sure that you have done following things:

  • Team Foundation Server Build service account is added to IIS Manager Permission section for the website inside IIS
  • Able to publish successfully from visual studio using web deploy?
  • Check the MSBuild parameters for web deploy (I have provided what works for me below).
If you have some more checklist you have formed over the years then let me know I will add to this list.

What-If-Error
When you click publish from visual studio you will have to select a publish method and at that point select Web Deploy.  You have the option to import the profile you have created from that publish dialog.  This will pre-populate all the details.  You will have to provide the user name and password for the user who has the permission in IIS Manager Permissions as described in earlier.  At this point if you see any errors then this is a nice website where all the errors are listed.  I think you will see these errors only if you do some mistake while manually typing out the details in the publish dialog.  I haven’t found any issues if you import site details through import mechanism.

Here are TFS MSBuild parameters I use to publish a website from TFS to IIS7.

/p:DeployOnBuild=True
/p:DeployTarget=MsDeployPublish
/p:CreatePackageOnPublish=True
/p:MSDeployPublishMethod=WMSVC
/p:MSDeployServiceUrl=https://ServerName:8172/MSDeploy.axd /p:DeployIisAppPath="NameofWebsiteOnIISServer"
/p:AllowUntrustedCertificate=True
/p:UserName="domain\serviceaccount"
/p:Password="AwesomeSecurePassword"

If you are still unsuccessful in publishing the website from TFS Build then it could be that your TFS Build server is 2010 and you are using Visual Studio 2012 and it might be missing some binaries.  If you install Visual Studio 2012 on the build server then this issue could be resolved.

Tuesday, June 19, 2012

Deploying Website and Windows Service using MSDeploy, Powershell and InstallUtil from TFS Build


We have a visual studio solution which has 4 projects into it.  One of them is a web project, another windows service project, class library and console application, all under source control inside Team Foundation Server 2010 in one team project.
TeamProject
   Solution.sln
   >Web Project
   >Windows Service Project
   >Class Library Project
   >Console Application Project

When we queue a new build using Team Foundation Build then all the content gets dumped into one single drops folder (a shared folder on your staging server).   It is a total mess and we need to have our structure as shown above on the destination drops folder.  We would like to have control over the structure of the destination files dropped by TFS Build.  I was able to achieve this by following this guideline posted on MSDN “Control Where the Build System Places Your Binaries”.  


First we will take a look into how to deploy Web Application using Web package in TFS Build using powershell.  Alright now when we look into the drop folder for our website on the destination folder we can see a _publishedpackages folder which has a .zip file.  We are interested in this folder.  We will grab the .zip file which is our package and deploy it using MSdeploy.


MSDeploy is a command line utility used to deploy webpackages to IIS.  All good but there is one problem and that is everytime a new build is queued a new folder is created with date and version number appended to it.  Our files are inside dropsfolder\builddefinition\latestfolder.6192012.1\.  Inside this folder if you have customized Process template then each project will have its own folder and inside that there will be Release\Debug folder.  Then we have to get the path to our .zip file for the website which we need to pass to MSDeploy to publish website to IIS.  This can get cumbersome and slow down our cycle. Why? Everytime a new build is dropped you will have to go through this pain.

1. Login into the server
2. Fire up command line
3. Locate the path to the latest build folder and find the path to the _PublishedPackages folder
4. Pass those parameters into MSDeploy and then run it.

What we want is combine steps 2 to 4 into one powershell script and store it inside TFS that will do the job for us.  You will have to modify process template to run a powershell script during build process. Check this excellent article from Ewald Hofman on how to execute powershell script from tfs. One might think that there are different ways of achieving same functionality and automating it directly from build definition itself but I learnt something in this process which I thought might be useful to somebody.


$latestbuildfolder = Get-ChildItem "C:\dropsfolder\BuildDefinition\" | Sort-Object LastWriteTime -Descending | SELECT-Object -First 1 $webpackagelocation = "\ProjectName\WebProject1\Release\_PublishedWebsites\WebProject1_Package\WebProject1.zip" $finalstring = $latestbuildfolder.FullName + $webpackagelocation & 'C:\Program Files\IIS\Microsoft Web Deploy v2\msdeploy.exe' -verb=sync -dest=auto "-source=package=$finalstring"

In the first three lines we try to grab the path to the web package and then pass the path to MSDeploy commandline utility to deploy to IIS web server.

Line1: First we list all the folders using Get-ChildItem and then sort them in descending order and get the first item.  This first item is our last build that was queued.  Everytime when you run the script it will make sure we get the latest folder.
Line2: This line is very simple because for all the future builds we know where our .Zip file will be sitting.  So it is direct path to the zip file.
Line3: Just concatenating two strings from line1 and 2 but be careful here the $latestbuildfolder variable holds the folder as the item so to get the path you need $latestbuildfolder.FullName.
Line4: If you want to run commandline utilities through powershell then you will have to first put “&” ampersand varialbel then include commandline utility in quotes and then provide other variables or arguments in quotes.  MSDeploy is little bit tricky here.  You provide all other variables in without quotes and include the –source=package-$finalstring in quotes.  To find out the exact syntax that worked, I played with lots of combinations and eventually a friend of mine in the office helped me achieve this.  You can also use Join-Path cmdlet of powershell to join paths.

Now you can do almost anything now with MSDeploy, TFSBuild and Powershell.  MSDeploy is a very powerfull tool and the one that is less exploited for deploying Websites guess.
Let’s try to install windows service using Powershell so that this wil happen automatically everytime.  We also have to take care of one more problem, and that is uninstalling windows service everytime we deploy a new version.  Below is my powershell script to uninstall windows service:


$service1 = Get-WmiObject -Class win32_service -Filter "Name='OurServiceName'" 
 if ($service1.State -eq 'Running') 
 { 
 Write-Host "Stopping $service1.Name service" stop-service OurServiceName & 'C:\Windows\Microsoft.Net\Framework\v4.0.30319\installUtil.exe' /u $service1.PathName
 } 


In the above script, I am using Get-WMIObject win32_service to get our windows service because Get-Service was not giving me the path to the executable that was used to install the service.  The executable path to the service is revealed only through win32_service instance of the class.   This way we don’t have to cycle through our drops folder and find previous versions of windows service.
Let’s take a look at our installation powershell script code:

$latestbuildfolder = Get-ChildItem "C:\dropsfolder\BuildDefinition\" | Sort-Object LastWriteTime -Descending | SELECT-Object -First 1
$windowsServiceFolder = "\ProjectName\WindowsService\Release\WindowsService.exe"
$exepath = $latestbuildfolder.FullName + $windowsServiceFolder
& 'C:\Windows\Microsoft.Net\Framework\v4.0.30319\installUtil.exe' /username=companyname\srviceaccount /password=password1 $exepath
Start-Service -Name OurServiceName
$getlatestservice = Get-Service -Name OurServiceName
$getlatestservice


In the above code pay close attention to the installutil.exe code where we specify /username and /password because we don’t want to have that user prompt annoy us everytime we try to install our windows service.  For this to work you have to make some changes to your windows service installer code.  I have used code to make it work.  There is an appropriate BeforeInstall event where you need to add this code because there are BeforeInstall events for ServiceProcessInstaller and ServiceInstaller objects.

Now putting it all together we want to put our powershell script into TFS as we don’t want to have it on the debug or production machine.  We want it under source control.


$session = New-PSSession -ComputerName Server20 
Invoke-Command -Session $session -ScriptBlock { $service1 = Get-WmiObject -Class win32_service -Filter "Name='OurServiceName'" 
 if ($service1.State -eq 'Running') { 
 Write-Host "Stopping $service1.Name service" stop-service OurServiceName 
& 'C:\Windows\Microsoft.Net\Framework\v4.0.30319\installUtil.exe' /u $service1.PathName 
 } 
$latestbuildfolder = Get-ChildItem "C:\dropsfolder\BuildDefinition\" | Sort-Object LastWriteTime -Descending | SELECT-Object -First 1 
$webpackagelocation = "\ProjectName\WebProject1\Release\_PublishedWebsites\WebProject1_Package\WebProject1.zip" 
$finalstring = $latestbuildfolder.FullName + $webpackagelocation 
& 'C:\Program Files\IIS\Microsoft Web Deploy v2\msdeploy.exe' -verb=sync -dest=auto "-source=package=$finalstring" 
$windowsServiceFolder = "\ProjectName\WindowsService\Release\WindowsService.exe" $exepath = $latestbuildfolder.FullName + $windowsServiceFolder 
& 'C:\Windows\Microsoft.Net\Framework\v4.0.30319\installUtil.exe' /username=companyname\srviceaccount /password=password1 $exepath 
Start-Service -Name OurServiceName $getlatestservice = Get-Service -Name OurServiceName $getlatestservice 
}

The first two lines are of importance we are running these command using powershell remoting (Powershell remoting should be enabled on the remote server).  We are create a new session specify computer name and run our powershell commands.  These commands will run on the remote machine just like they would run on a client machine.  We are all set.  Queue a new build and see this magic happen.

One more step closer to continuous integration!!!