With SharePoint 2010 we can now deploy our Solution Packages using PowerShell. What’s cool about this is that it’s a bit easier than it was with 2007 to check if a package is already deployed and conditionally retract, delete, and then re-add and re-deploy. By now most people already know how to do this as it’s fairly straightforward but I thought I’d go ahead and share the script that I use as it’s great for deploying lots of Solution Packages to my various client environments in bulk.
Like most of my scripts this one is driven by an XML file but I have a core function which can be called directly – I just wrap that in another function which can iterate through the XML file thus facilitating bulk installs of packages. First lets look at the XML file:
<Solutions>
<Solution Path="W:\my.sharepoint.package.wsp" CASPolicies="false" GACDeployment="true">
<WebApplications>
<WebApplication>http://portal</WebApplication>
</WebApplications>
</Solution>
</Solutions>
As you can see the structure is fairly simplistic – just provide the path to the WSP file and whether it contains CAS policies and whether it should be deployed to the GAC or not. If it’s a Farm level solution (no web application resources) then simply omit the <WebApplications /> element. If you have more than one solution just add another <Solution /> element. If you’re deploying to multiple web applications then add as many <WebApplication /> elements as is needed.
Now we’ll take a look at the wrapper function which loops through the XML:
function Install-Solutions([string]$configFile) { if ([string]::IsNullOrEmpty($configFile)) { return } [xml]$solutionsConfig = Get-Content $configFile if ($solutionsConfig -eq $null) { return } $solutionsConfig.Solutions.Solution | ForEach-Object { [string]$path = $_.Path [bool]$gac = [bool]::Parse($_.GACDeployment) [bool]$cas = [bool]::Parse($_.CASPolicies) $webApps = $_.WebApplications.WebApplication Install-Solution $path $gac $cas $webApps } }
As you can see the code just loads the passed in file as an XmlDocument object and grabs each Solution element ($solutionsConfig.Solutions.Solution) and then iterates through each object using the ForEach-Object cmdlet. For pure convenience I grab each attribute and assign it to a local variable. And finally I call the Install-Solution function which is shown below:
function Install-Solution([string]$path, [bool]$gac, [bool]$cas, [string[]]$webApps = @()) { $spAdminServiceName = "SPAdminV4" [string]$name = Split-Path -Path $path -Leaf $solution = Get-SPSolution $name -ErrorAction SilentlyContinue if ($solution -ne $null) { #Retract the solution if ($solution.Deployed) { Write-Host "Retracting solution $name..." if ($solution.ContainsWebApplicationResource) { $solution | Uninstall-SPSolution -AllWebApplications -Confirm:$false } else { $solution | Uninstall-SPSolution -Confirm:$false } Stop-Service -Name $spAdminServiceName Start-SPAdminJob -Verbose Start-Service -Name $spAdminServiceName #Block until we're sure the solution is no longer deployed. do { Start-Sleep 2 } while ((Get-SPSolution $name).Deployed) } #Delete the solution Write-Host "Removing solution $name..." Get-SPSolution $name | Remove-SPSolution -Confirm:$false } #Add the solution Write-Host "Adding solution $name..." $solution = Add-SPSolution $path#Deploy the solution if (!$solution.ContainsWebApplicationResource) { Write-Host "Deploying solution $name to the Farm..." $solution | Install-SPSolution -GACDeployment:$gac -CASPolicies:$cas -Confirm:$false } else { if ($webApps -eq $null -or $webApps.Length -eq 0) { Write-Warning "The solution $name contains web application resources but no web applications were specified to deploy to." return } $webApps | ForEach-Object { Write-Host "Deploying solution $name to $_..." $solution | Install-SPSolution -GACDeployment:$gac -CASPolicies:$cas -WebApplication $_ -Confirm:$false } }Stop-Service -Name $spAdminServiceName Start-SPAdminJob -Verbose Start-Service -Name $spAdminServiceName #Block until we're sure the solution is deployed. do { Start-Sleep 2 } while (!((Get-SPSolution $name).Deployed)) }
The code looks more complicated than it really is. I first start out by getting the solution name which I always assume to be the filename of the WSP file. I then use that to get the SPSolution object using Get-SPSolution. If a value comes back then I check if it has been deployed and if it has then I retract it by calling Uninstall-SPSolution. The trick is knowing whether it has web application scoped resources and if it does then we need to retract using the -AllWebApplications parameter. Once retracted I stop the SharePoint Administration Service (SPAdminV4) so that I can call Start-SPAdminJob and force the retraction timer job to execute. Once the Start-SPAdminJob cmdlet returns I then restart the SharePoint Administration Service. With the solution retracted I can now delete the solution from the solution store using Remove-SPSolution (I re-get the solution to make sure that I get no errors due to the current variables state being invalid).
Once deleted I can then add the new solution to the store using the path provided (Add-SPSolution). Now that it’s in the store I can check if it has web application resources or not – if it does not then the deployment is simply a matter of calling Install-SPSolution and specifying whether it should be deployed to the GAC and if it contains CAS policies. If it does contain web application resources then I have to loop through all the web application items in the provided string array ($webApps) and then pass each one into a separate call to Install-SPSolution.
You now have two options for adding your solution: you can call the first function and pass in an XML file or you can call the second function directly. I’ll first show how to call the second function directly:
PS C:\>Install-Solution "w:\my.sharepoint.package.wsp" $true $false @("http://portal","http://mysites")
Now lets look at how to call the first function given an XML file named “solutions.xml” containing a structure similar to that shown above:
PS C:\>Install-Solutions "w:\solutions.xml"
Hopefully you’ll find this script useful for deploying your custom SharePoint 2010 Solution Packages.


