SharePoint Advanced Large Scale Deployment Scripting – “Deploy” (part 1 of 3)
29 Jul 2011 5 Comments
I have been battling deployments for a while and finally decided to do something about it.
The challenge is to handle a dozen WSP packages on farms that host 20-50 web applications (or host header named site collections) to complete the deployments in a timely manner, ensure that the required features are uniformly activated every time while minimizing the human error element of the poor guy deploying through the night.
The deployment scripts are equally applicable to both SharePoint 2007 and 2010 – the need is roughly the same and the APIs are largely unchanged. Some minor differences in service names are covered by the code.
To keep this post reasonable in length I’ve split the subject into three parts:
Part 1 is the main (powershell) deployment scripts primarily targeted at your test/QA/production deployments
Note: This took a long time and countless iterations to develop and it will likely take you, dear reader, at least a few hours to implement in your environment.
My design goals were to solve
- Automatic solution deployments (including timer jobs resets)
- Consistent activation of features across the farm while allowing for different types of sites
- Easy, fast and reliable execution
- Not too awkward configuration of solutions and features for farms with many sites – it will never be a no-brainer
- Exact same configuration (file) to be re-used across Dev, Test, QA and Prod tiers
The cost is some fairly complex scripts that took quite a while to write and test.
The same script works equally well for both 2007 and 2010.
There is almost no difference between SharePoint 2007 and 2010 in this area – 2010 comes with some new CmdLets for PowerShell but they are often just convenient access to the API. I’m taking the extra effort to hit the API directly which is essentially unchanged for deployment purposes. One of the really cool new features in 2010 is the ability to control solution upgrade in detail which is of course supported by these scripts simply by choosing the upgrade option for a given solution.
The idea is that you have the same configuration file for all your app tiers, and
- When you need to deploy a new version of some of your WSPs you just provide them and execute the scripts
- Script will scan the local farm and map the URLs with the provided WSPs and deploy them
Run through every identified Site and match feature activations with the configuration file and fix any discrepancies
The features are
Deploy WSP files regardless of whether or not they were deployed beforehand (uninstall followed by install is the default installation mode for me)
- If solution deployment goes wrong the behavior is to restart all SharePoint related services on all servers in farm and retry (a few times). It surprisingly often solves problems like locked files etc.
- If a solution contains timer jobs the timer service is restarted on all servers in farm. Finally
- Upgrade existing WSP files
(Part 2) Scan the farm and ensure that required features are activated (or perhaps deactivated) on specified sites
- Farm and WebApp scoped features are by default deactivated after an uninstall/install – this script reactivates them
- Features are always (de)activated with the “force” parameter just for good measure
- Yes, it’ll wait for web config modifications jobs when required
- (Part 2) Re-activate selected features i.e. deactivate first then activate
(Part 3) Automatic deployment and activations for dev, test and QA environments
The Configuration File
The hearth of the scripts is the configuration file.
The one configuration file can be re-used for both handling solution deployment and feature activations across all tiers of your farms. You can also conceivable use the same config file for different types of farms, e.g. the intranet and extranet, however I feel that in those scenarios it is better to keep those things separate.
The configuration file contains:
A number of “Sites” which is the URLs of either (host header named) site collections or web applications
- The local farm will be scanned and the sites found in the config file that are present in the local farm is the ones that we work on – the rest are ignored (e.g. running in test it’ll skip all the production URLs)
- Each site node contains a number of FeatureSets but no list of features or solutions
A number of “FeatureSets” that groups WSPs and features. Features are either “activated”, “deactivated” or “reactivated”
- (The feature thingie is in part 2)
- For a given site the union of all the solutions specified in all the FeatureSets associated with the sites defines what solutions should be deployed to that site.
A number of “Solutions” that list the WSP names and their individual options, i.e. deploy to central admin, all content URLs, upgrade or (re)install, does it contain timer jobs (which requires timer service to be restarted on all farms)
- Note: The scripts deploy all the solutions in a given “drop” directory and ignore all the configuration nodes without a matching WSP file
To demo it I’ve downloaded a couple of WSPs from CodePlex and created the following configuration file for my dev box:
<?xml version="1.0" ?> <Config> <Sites> <!-- note: urls for all environments can be added, the ones found in local farm will be utilized --> <!-- DEV --> <Site url="http://localhost/"> <FeatureSet name="BrandingAndCommon" /> <FeatureSet name="Intranet" /> </Site> <Site url="http://localhost:8080/"> <FeatureSet name="BrandingAndCommon" /> <FeatureSet name="MySite" /> </Site> <Site url="http://localhost:2010"> <FeatureSet name="CA" /> </Site> <!-- TEST --> <Site url="http://test.intranet/"> <FeatureSet name="BrandingAndCommon" /> <FeatureSet name="Intranet" /> </Site> ... <!-- PROD --> <Site url="http://intranet/"> <FeatureSet name="BrandingAndCommon" /> <FeatureSet name="Intranet" /> </Site> ... </Sites> <FeatureSets> <FeatureSet name="BrandingAndCommon"> <Solution name="AccessCheckerWebPart.wsp" /> <Feature nameOrId="AccessCheckerSiteSettings" action="activate" ignoreerror="false" /> <Feature nameOrId="AccessCheckerWebPart" action="activate" ignoreerror="false" /> <Solution name="Dicks.Reminders.wsp" /> <Feature nameOrId="DicksRemindersSettings" action="activate" ignoreerror="false" /> <Feature nameOrId="DicksRemindersTimerJob" action="activate" ignoreerror="false" /> <!-- Todo - all sorts of masterpages, css, js, etc. --> </FeatureSet> <FeatureSet name="Intranet"> <Solution name="ContentTypeHierarchy.wsp" /> <Feature nameOrId="ContentTypeHierarchy" action="activate" ignoreerror="false" /> </FeatureSet> <FeatureSet name="MySite"> </FeatureSet> <FeatureSet name="CA"> </FeatureSet> </FeatureSets> <Solutions> <!-- list of all solutions and whatever params are required for their deployment --> <Solution name="AccessCheckerWebPart.wsp" allcontenturls="false" centraladmin="false" upgrade="false" resettimerservice="false" /> <Solution name="ContentTypeHierarchy.wsp" allcontenturls="false" centraladmin="false" upgrade="false" resettimerservice="false" /> <Solution name="Dicks.Reminders.wsp" allcontenturls="true" centraladmin="false" upgrade="false" resettimerservice="true" /> </Solutions> </Config>
Note: I more or less picked some random WSPs from CodePlex for demo purposes.
Note 2: The scripts will respect the order given in the config file. The Sites are processed in order, the FeatureSets listed first within a “Site” node is handled in top-down order. The solutions deployed to the farm are deployed in the order they are listed in the “Solutions” node.
Note 3: XML is case sensitive; therefore, all the named sections are case sensitive. The one exception is that I accept different casing between the WSP file names and the name they are given within the config file.
There are two sections here:
- A number of SharePoint PowerShell scripts that include each other as needed
A few batch files that start it all. I generally prefer to have some (parameter less) batch files that executes the PowerShell scripts with appropriate parameters
I have gold-plated the scripts quite a bit and created a small script library for my SharePoint related PowerShell.
The PowerShell scripts are too long to write in the post, they can be downloaded here. The list is (\SharePointLib):
*DeploySharePointSoluitions.ps1: Start the WSP deployments. Parse command line arguments of config file name and directory of WSP solutions
*(Part 2) EnsureFeatureActivation.ps1: Start the feature activation scripts (need command line arguments)
*(Part 3) QADeploymentProcess.ps1: Deployment process for dev, test and QA environments (need command line arguments)
SharePointDeploymentScripts.ps1: Main script that defines a large number of methods for deployment and activations.
SharePointHelper.ps1: Fairly simple SharePoint helper methods to get farm information
*RestartSharePointServices.ps1: Restart all SharePoint services on all servers in the local farm (see runtime notes below)
Services.ps1: Non-SharePoint specific methods for handling services, e.g. restarts
Logging.ps1: Generic logging method. Note that there are options for setting different log levels, output files, etc.
The ones with an asterisk are the ones that you are supposed to execute – the others just defined methods for the “asterisk scripts”.
The batch files (same download) (root of zip download):
01 Deploy Solutions.bat: Start the solution deployment. It will deploy all the WSP files dropped in “\SolutionsDropDir\” that are also specified in the “\DeploymentConfig.xml”:
02 Activate Features.bat (Part 2): Works with the “\DeploymentConfig.xml” file and ensures that all features are activated.
In other words you’ll execute “01 Deploy Solutions.bat” to deploy all your solutions.
By default you will get one log file from each batch file named after the operation, one log file with a timestamp and in case of any errors an additional error log with the same timestamp. In other words if the error log file is not created then no errors occurred.
Note: Scripts must be executed on one of your SharePoint servers which sufficient rights. That is more or less local admin and owner on the individual databases (or local admin on the SQL box as well).
Note 2: I write quite a bit of logging it is invaluable in times of trouble. You can change the amount in the logging.ps1 file by adjusting the loglevel variable.
Note 3: In case of deployment errors the default behavior is to restart every SharePoint service on every server and then retry. These service restarts are performed async, so it should take no more than a minute or two, no matter how many servers you have (almost).
Note 4: Any WSP found in the drop dir that has not been specified in the config is considered an error – therefore you know that all the wsps provided are deployed if no errors are reported.
The time it takes to search the local farm for relevant site collections / web applications is negligible so you shouldn’t worry unduly about impact on runtime to add 300 URLs to cover all your possible environments including every developer’s individual preferred setup.
But there is no magic and it simply runs in the time it takes to deploy solutions to your environment. If it takes about 3 minutes for a full retract, remove, add, deploy then it takes 30 minutes for 10 WSPs.
The default way of handling deployment errors is to restart all SharePoint services in the farm. The clever way of doing that is to send the stop/start commands to all services first and then wait for the operations afterwards. That way the time taken to do restarts is about the time of restarting the slowest service in the farm once – it usually takes 1-2 minutes regardless of the number of servers. In my opinion this is quite cool 🙂
If you are wondering about my exception handling note that the scripts were originally made for PowerShell 1.0 and later I decided to only target 2.0. Some statements may be written prettier without me taking the time to do so.
These scripts save us a ton of time and prevents a lot conversations of the sort “oh I forgot to activate that feature, 2 secs, there you go” – but of course quite some time has been spent creating the deployment scripts and fixing errors within them.
Some of the scripts are quite usable for other purposes. For instance, the remote service restart script I have not seen anywhere else – this is simpler than installing PowerShell remoting (WinRM?) on every server.
If you make some brilliant changes then let me know so I can include it in my “official” version.
Please also leave credits in there so it’ll be possible for your successors to find the source and documentation.
Grab the scripts here (updated with part 2 and 3).
Note: Updated Aug 29 2011.