martes, 1 de julio de 2014

Best practices for SharePoint deployment with Powershell

Powershell is a monster who lives in a dark area between the developers’ and the ITPros’ worlds. It’s, of course, much better that our beloved old-days batch files evil, but it’s still far away from what a developer would call “language”. Unless you live surrounded by them and you master all the details of this technology, it’s always a source of problems and delays in your projects. If you think the last statement is true, you should continue reading this post.

Like it or hate it, powershell is the best choice you can take when it comes to deploy a SharePoint project. It allows a developer to setup the deployment process, execute it as many times as needed to fine-tune it and, last but not least, scripts can be delivered to any ITPro to execute the actual deployment process in any environment they want. Of course, scripts can be created by the same people in charge of the deployment process but there are a couple of reasons that come to my mind (and probably I’m missing a lot) to encourage every developer to take care of the scripts:

  • Nobody will know better than you what your development does, so nobody will know better than you how the final result will look like.
  • How many times do you deploy your solution to your development environment while you are working on your project? Some dozens of times, per day. If you don’t take advantage of this to fine-tune your deployment process, you are wasting a lot of time.

So, if we agree on the fact that powershell is important, and we also agree on the fact that a standard developer won’t have it among the list of mastered skills, how can we get to a position where we find ourselves comfortable? Following a list of simple rules.

Creating functions in separated files

Initially, powershell seems something unstructured for most developers, and they are probably right. You normally start with a .ps1 file and start writing your statements on it. If you want to improve readability, you create functions at the beginning of the file. This is better than nothing, but if you separate the functions in different files you will get some benefits in terms of reusability and simplicity of your files. Imagine you have a script containing something similar to the figure below:

  1. $web = Get-SPWeb "http://localhost"
  2. $list = $web.Lists["Documents"]
  3. $assembly = "MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcde, processorArchitecture=MSIL"
  4. $class = "MyReceiver"
  5. Write-Host "Adding ItemAdding receiver" -foreground cyan
  6.   
  7. try {
  8.     $list.EventReceivers.Add("ItemAdding", $assembly, $class)
  9.     Write-Host "Receiver added successfully" -foreground green   
  10. }
  11. catch {
  12.     Write-Host "Error adding receiver : $_" -foreground red   
  13. }

Of course at that time everything seems to be correct but as the size of the script is increased, so it does its complexity. Imagine I take the lines above and I create a new file called Add-EventReceiver.ps1 with the following contents:

  1. param([string]$siteURL, [string]$listName, [string]$receiverType, [string]$assembly, [string]$class)
  2.  
  3. $web = Get-SPWeb $siteURL
  4. $list = $web.Lists[$listName]
  5.   Write-Host "Adding $receiverType receiver" -foreground cyan
  6.  
  7. try {
  8.     $list.EventReceivers.Add($receiverType, $assembly, $class)
  9.     Write-Host "Receiver added successfully" -foreground green   
  10. }
  11. catch {
  12.     Write-Host "Error adding receiver : $_" -foreground red   
  13. }

Apparently the code is really similar, but the main script will actually look like:

  1. $assembly = "MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcde, processorArchitecture=MSIL"
  2. $class = "MyReceiver"
  3. .\Add-EventReceiver.ps1 "http://localhost" "Documents" "ItemAdding" $assembly $class

If needed, you can organize the files in folders to emulate namespaces and, of course, you can easily share your functions’ folders across multiple projects. No more copy & paste and no more top-level complex scripts. Other developers will appreciate it.

Beware with GAC deployment

One of the most common mistakes I see on SharePoint deployment scripts is connected with how you deal with deploying DLLs to GAC. The general rule is, you can’t make any use of something included into a DLL in the same powershell process that included this DLL in GAC. For example, imagine you have a WSP that contains a feature with an associated receiver that is included in the same WSP. If you have a script that deploys the solution, and therefore copies the DLL to the GAC, and after that tries to activate the feature, you will see an error indicating that the feature can’t be activated because the receiver class can’t be found. The solution is, again, separating files and launch separated processes to deploy solutions and to perform the additional tasks. You can use an approach similar to the one below:

  1. $path = split-path $myInvocation.MyCommand.Definition
  2.  
  3. powershell -file (join-path $path 'Deploy-Packages.ps1') $paramX $paramY
  4. Restart-Service W3SVC,WAS -force
  5. powershell -file (join-path $path 'ActivateFeatures.ps1') $paramZ

Notice the Restart-Service command in the middle of the two script invocations. Depending on what you are doing you will need to restart W3SVC, SPTimerV3, or any other service that could be using cached versions of components that you are updating in your deployment. Bear that in mind when building your scripts.

Commenting is fine, documenting is better

I’m not against commenting code, especially when the code is difficult to understand without those comments. Well, to be honest, I am against comments such as:

  1. # I get the site located in http://localhost and store it in the $site variable
  2. $site = Get-SPSitehttp://localhost”
 
Leaving the decision on what should and shouldn’t be commented to every developer, I would encourage you to document every powershell script if you want others to take advantage of them. And, in the term “others” I also include yourself in the future.

Take a look at the example shown earlier on this post regarding adding event receivers. The function seems very simple, and probably it is, especially if you ask the author for his opinion. If you are not the author and you are about to use it in your project, you will miss a popup explaining what the function will do when invoked and what values you can provide to the receiverType parameter. Of course you can open the script and try to do some research but, is it that difficult to improve other people life’s quality? I don’t think so. Just take a look at the following article http://technet.microsoft.com/en-us/magazine/ff458353.aspx.
You will basically need to add a header to the file. You can take the following listing as an example:


  1. <#
  2.  
  3. .SYNOPSIS
  4.  
  5. Lorem Ipsum.
  6.  
  7.  
  8.  
  9. .DESCRIPTION
  10.  
  11. Blah blah wow blah blah.
  12.  
  13.  
  14.  
  15. .PARAMETER siteUrl
  16.  
  17. Url of the site collection you want to perform execute the process.
  18.  
  19.  
  20.  
  21. .PARAMETER listName
  22.  
  23. Name of the list you want to attach the receiver to
  24.  
  25.  
  26.  
  27. .PARAMETER receiverType
  28.  
  29. Type of events you want to handle. Possible values are ItemAdding, ItemAdded, ...
  30.  
  31.  
  32.  
  33. .PARAMETER assembly
  34.  
  35. Strong name of the assembly containing the event receiver class
  36.  
  37.  
  38. .PARAMETER class
  39.  
  40. Full name of the class implementing the event receiver code
  41.  
  42.  
  43. .EXAMPLE
  44.  
  45. Read computer names from Active Directory and retrieve their inventory information.
  46.  
  47. Add-EventReceiver -siteUrl http://localhost -listName Documents -receiverType ItemAdding -assembly X -class Y
  48.  
  49.  
  50.  
  51.  
  52. .NOTES
  53.  
  54. More blah blah.
  55.  
  56. #>

You can probably think it’s not worthy the time you will spend writing this information, and especially maintaining it. You are probably right, I’m typically against commenting things. However, isn’t it professional to give others something that they can ask for help as simple as writing Get-Help .\AddEventReceiver.ps1?

image

If you don't want to do this to help other developers, at least do this on this artifacts you are going to give to those who are are being in charge of the deployment. This world is too small and it's always good to let people think you are a good professional.

And, of course, we could be speaking about powershell for years. If you like to know more, I'm pretty sure you will be able to find a lot of information out there. If you ask me who to follow if you are interested on that topic, I would definitely recommend Gary Lapointe. He has been the first blog to visit when facing powershell issues in the SharePoint world since the 2003 era (stsadm by that time).

Another guru to follow is Brian Lalancette. He changed every SharePoint developer's life when he released the glorious AutoSPInstaller, something that, by the way, I have a draft post to publish that is more than one year old! Someday I will find a couple of hours to finish it an publish it. Stay tunned!

0 comentarios: