Monday, June 20, 2011

How to 2 : Deploy SharePoint 2010 Solutions (WSP) files from Team Foundation Server 2010 Build

In part one we configured our build machine, executed a basic build and then added some msbuild switches to out put a WSP file. Now we are going to custom the build template to allow us to execute a custom powershell script and deploy our SharePoint solution package on our build server. The steps are very involved and it was difficult to put them to words without being too wordy. If you need some assistance drop a comment on the post and I will try to get back to you. Hope you enjoy!

Step 4: Wire Up a Simple PowerShell script
Fire up notepad and type into it: Write-Host "Hello World!"

Save the file as HelloWorld.ps1.

1.Open the command prompt
2.To test the script the execution policy needs to be changed by executing the command: powershell set-executionpolicy remotesigned
3.Now execute the command: powershell "& 'C:\[PathToFile]\HelloWorld.ps1'"
Note: remember to replace the [PathToFile] with the actual path of the file

Verify the output says Hello World!

Now drop the PowerShell script into the root of your project and check it into Source Control. Next we need to customize the Build process template to accept a parameter to execute our PowerShell script.

Open up the Build Template again and navigate to the Process Tab. In here click the Show details down arrow under the Build process template: heading.



Click the New... button

Copy the Default Template and name your new file name SharePointDeployment.xaml; Click OK.



Save your Build Definition and then switch over to the Source Control Explorer. Beneath the Project you are working on should be a folder named BuildProcessTemplates. Take a look inside this folder to find your new build process template. Double click your new template to open up the workflow designer.

Locate the Try Catch Action which says "Try Compile, Test, and Associate Changesets and Work Items." I like to click the up arrows in the top right corner so this entire action is collapsed. This will make it easier to find your place in the future and add items.

Now, before we can start to add new elements to our workflow we need a way to indicate which script to run when the build executes. To do this, we will use an Argument. I like to think of arguments like constructor overloads. They can optionally be set when the build is executed. This is just one aspect of arguments, for now add a new one by clicking Arguments in the bottom right and scrolling down to the bottom row. The type should be string and direction should be in.



Now, go to the toobox in the visual studio and from the Control Flow group add an if statement just below the Try block we collapsed previously.

Click into the Condition text box and begin typing… whoa! Intellisense, now we are getting back to familiar ground. If you have a VB.NET background you will feel at home because this workflow is implemented using VB.NET. Your condition is checking if a DeploymentScript value was given so in VB we write "DeploymentScript IsNot Nothing".

Drop a Sequence activity into the Then box of our If statement which will encapsulate our actions. For the Sequence activity name it Start Deployment.

Now drop a ConvertWorkspaceItem activity into the Sequence activity, followed by an InvokeProcess activity.

Before we can use our DeploymentScript value we are passing into our build we need to convert it to something the build process can find in the file system. We use the ConvertWorkspaceItem activity to change our path to something the build will recognize. But we will need a variable to store the value. Variables are exactly what you think they are, variables like in any other coding language. They are scoped to a location so click on the Start Deployment Sequence activity and click Variables in the bottom left. Go to the bottom and create a new one named "DeploymentScriptFilename" of type String. Notice the scope is pre-set for you to Start Deployment.



Now, right-click on the ConvertWorkspaceItem and choose properties, set the properties like you see in the screenshot below.



This activity takes our Argument "DeploymentScript" and converts it to a path the build process can find and then stores it in the "DeploymentScriptFilename" variable. Now we are ready to execute the script.

Now, it is time to setup the properties of the InvokeProcess activity exactly like the screenshot below.



I've pulled the full text for the Arguments property out for you here: String.Format(" ""& '{0}'"" ", DeploymentScriptFilename)

To see the output of our elegant script, drop a WriteBuildMessage activitie below the handle standard output and handle error output. For the message properties set each one to be the value of the textbox immediately above the activity.



Save your changes; the workflow should look like this:


We need to set the value of the Argument "DeploymenScript" we created. Save and check-in the Deplo

Now it is time to save and check-in our build template and PowerShell script. Open Source Control Explorer and check-in the NewBuildProcessTemplate.xaml. In solution explorer right click on your project and choose Add Existing Item. Browse to the PowerShell script and click Add. It should appear in your project root now. Check the file into source control.

Open up your build definition to the Process tab and notice the new Misc grouping with the value DeploymentScript. Before we enter a value here navigate to the Workspace tab and verify there is an entry for the project you are currently working on. It should look something like $/FOLDER/ProjectName.

Go back to the process tab and type the path to your PowerShell script file into the DeploymentScript field (eg. $/FOLDER/ProjectName/HelloWorld.ps1).



Save your build definition and queue up a new build. Before submitting it, click the Parameters tab and change the Logging Verbosity to Detailed. This will give us the output of our PowerShell script file.


When the build finishes, expand the log and locate the Hello World message. Tip: you can watch the build by double clicking on it in the Queue.


Step 5: Wire Up a SharePoint Deployment PowerShell script
Once this is working, it is time to expand on our PowerShell Script to deploy the SharePoint solution. In order to do this we will need to have a fairly powerful SharePoint deployment script and know 2 things:

1.The location of the solution files
2.The solution file name
Let's dissect the script first. Comments are preceded with the # character. The solution does quite a few things.

1.Sets up some parameters which can be passed into the script and stores them in variables
2.Adds the SharePoint PowerShell snap-in so we have SharePoint script context
3.Checks for the solution in the solution store
4.Checks if the solution is deployed and retracts it
5.Removes the solution
6.Adds the solution
7.Deploys the solution
1: #Store the name of the file we will use this later to add the solution 2: 3: param ( $solutionName, $solutionFileLocation ) 4: 5: $solutionPackageName = [System.String]::Format("{0}", $solutionName) 6: 7: $path = [System.String]::Format("{0}", $solutionFileLocation) 8: 9: Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction "SilentlyContinue" 10: 11: write-host "Checking for solution:" $solutionPackageName 12: 13: $solution = Get-SPSolution | where-object {$_.Name -eq $solutionPackageName} 14: 15: if ($solution -ne $null) { 16: 17: write-host "Solution found" 18: 19: if ($solution.Deployed -eq $true) { 20: 21: write-host "Solution Installed, Uninstalling" 22: 23: Uninstall-SPSolution -Identity $solutionPackageName -Local -Confirm:$false 24: 25: } 26: 27: write-host "Solution Uninstalled" 28: 29: Remove-SPSolution -Identity $solutionPackageName -Confirm:$false 30: 31: write-host "Solution Removed" 32: 33: } 34: 35: write-host "Adding New Solution" 36: 37: Add-SPSolution -LiteralPath $path 38: 39: write-host "Installing Solution" 40: 41: Install-SPSolution -Identity $solutionPackageName -Local -GACDeployment 42: 43: write-host "Solution Installed"It is always a good idea to test scripts in the PowerShell ISE before adding them to your build. In my scenario I am deploying a Farm solution with GAC resources so my deployment script might be different from yours.

Now that we've dissected the script we are ready to add it to the project and deployment. Copy and paste the above script into a new PowerShell script file and add it to your project root as DeployFarmSolution.ps1 (remember to check it in).


Now to the two information challenges we face.

Solution File Names
I did not find a way to get these dynamically so I coded them into an Argument in the build process template. This gives me the flexibility of updating them from the GUI of the Build Definition if I decide to change the name. The name of the Argument I use is "SolutionName". It should look like the below screenshot.


To set the variable we need to save AND check-in the template. Then open up the build definition from Team Explorer by right-clicking on the Build and choosing Edit Build Definition. In the Process tab there should be another item in the Misc grouping named SolutionName. Put the name of the solution into this field (eg. Solution.name.wsp). While you are in here go ahead and change the DeployMent script Argument to be your new DeployFarmSolution PowerShell script.


Location of the Solution files
There is probably a better way to do this but I know solutions are copied into the Binaries directory right off of the root of the workspace. I can use the built in workspace variable to find its location and append the string literal "Binaries\SolutionName.wsp" to the end.

If you don't already, open up the Build Process Template (workflow designer) we created earlier and get the properties for the Execute Deployment Script action. Change the Arguments property value to be:
String.Format(" ""& '{0}' {1} '{2}\Binaries\{3}'"" ", DeploymentScriptFilename, SolutionName, BuildDirectory, SolutionName)

We are using the String.Format function to call our script {0}, pass in the solution name {1}, and finally concatenate the Build directory with the string literal "\Binaries\" and our solution name to get the full path to our solution file. It is that simple!

Go ahead and queue up another build with the Logging Verbosity set to detailed


I've used this process with a few modifications to execute multiple scripts which can be stored in a string[] Attribute. Here is a screenshot of the multiple deployment log.

No comments:

Post a Comment