Automated SharePoint 2010 Farm Level Solution Deployment and Retraction Process – Part II – Building PowerShell Script

Article Series Content

This is second part of the article series and it provides the step by step process of building PowerShell script to automate the SharePoint 2010 Farm Level Solution deployment/retraction process. If you haven’t read the first part of this series, I would recommend checking it out here.

Since PowerShell 2.0 is one of the pre-requisites, SharePoint Developer VM typically contains bits of PowerShell 2.0. Although Microsoft provides PowerShell 2.0 IDE for custom scripts, I like to use PowerGUI from Quest for PowerShell development – http://powergui.org/index.jspa.  Please check it out. It’s free download and provides nice debugging capabilities.

When I started creating PowerShell scripts to automate the SP2010 custom solution deployment/retraction process, one of my main goals was to build most generic farm level solution deployment script which would not only deploy/retract solutions and activate/deactivate features at the site collection level but also support activation/deactivation at all the sub sites level. If you are planning to deploy/retract solution at single site collection level, you can use this script right away without any changes. If you have more than one site collection, it would require slight modification.

Apart from most generic script, some of major features I wanted to support in this PowerShell script are as below.

  • It must support Parameters. Most of the environment specific information must be parameterized.
  • It should load SharePoint PowerShell Plugin automatically. If you have used SharePoint PowerShell Shell, it automatically loads this plugin but from custom windows PowerShell script, it must load manually and must check if it’s already loaded to avoid exceptions.
  • Since one of the goals of this script is to handover the deployment to SharePoint admins, Logging of PowerShell script execution transcript is key. It would create new file for each run. If Admins ever informs developers that your script has been failed, you can ask for this log file for troubleshoot
  • Major goal of this automated script to deploy or retract solutions in single click. Either you run PS manually or run using batch files; it should deploy/retract solutions and activate/deactivate features in single click.
  • As many of you know, during SharePoint Solution deployment and retraction process, SharePoint creates timer job to deploy and retract solutions from all the WFEs. We need to ask PowerShell to wait until these timer jobs complete their tasks. This PowerShell script should support waiting for timer job execution while deploying and retracting solution.
  • Support of Nullable $siteFeatureNames and $webFeatureNames parameters for deployment only option, no activation of features. Additionally you can pass one of these parameters, if Site or Web Level features are not applicable. Not all solutions would contain both web and site level features.
  • Support Multiple Site and Web Level Features – Site feature and web features are passed in as array parameters
  • Support Activation of features at Multiple Webs – Activate/Deactivate Web Level features on all webs in site collection
  • Check for web application level resources to deploy or retract solutions at the web application level or all web application level
  • Prompt the script execution details with different color codes – green for major steps, yellow for detailed info, and red for errors. If you are running these scripts manually on the server, you should be able to see the execution messages with different color codes.

To create the PowerShell script to automate Solution deployment/retraction process, please follow these steps. You can access final version of this script as 3rd part of this series here. Please note that I have posted this full working copy of this PowerShell script at TechNet Scripts library and you can find it here.

Step 1 – Define the Parameters to passed in PowerShell script

Define parameters to passed in web application URL, site collection URL, solution WSP Name, Site Level Features, Web Level Features, Log file name, and Action.  Last parameter would determine whether it’s deployment or retraction process. Ask calling application to enter SD for solution deployment and SR for solution retractions.

param (
 [string]$solutionName = "$(Read-Host 'Enter the Solution WSP Name. [e.g. Sample.wsp]')",
 [string]$webAppUrl = "$(Read-Host 'Enter the Web Application URL. [e.g. <a href="http://sp2010vm]'/">http://sp2010vm]'</a>)",
 [string]$siteUrl = "$(Read-Host 'Enter the Site Collection URL. [e.g. <a href="http://sp2010vm/sites/site]'">http://sp2010vm/sites/site]'</a>)",
 [string[]]$siteFeatureNames = $null, #"$(Read-Host 'Enter the Site Level Features. [e.g. @('Feature1', 'Feature2')]')",
 [string[]]$webFeatureNames = $null, #"$(Read-Host 'Enter the Web Level Features. [e.g. @('Feature1', 'Feature2')]')",
 [string]$logFileName = "$(Read-Host 'Enter the Log File Name. [e.g. Sample.log]')",
 [string]$action = "$(Read-Host 'Enter [SD] to deploy solutions or [SR] to retract the solution')"
)

Step 2 – Define the Main function.

As many of you know, PowerShell is all about defining functions and these functions can be called later to execute the PowerShell script logic. Main function is heard and sole of this script. Please note that since this PowerShell script would support only if Batch files, log files, and WSPs exists in same directory during deployment process, first step is to retrieve current location of the script. Next step is to create the log file to output the transcript. It would delete the log file, if it already exists.

One of the major tasks of the main function would be loading Microsoft.SharePoint.PowerShell snapin. This PS script will be using many of the SharePoint cmdlets available in Microsoft.SharePoint.PowerShell snapin and it is necessary to load before using the cmdlets. It is also necessary to check whether it’s been already loaded, otherwise this step would throw an error.

Depends on action switch, this function would run deployment or retraction process which will further call other secondary functions for deployment/retraction, activation/deactivation process.

# Defination of main function
function main() {
 # find the current directory
 $currentDirectory = Get-Location
   
 # delete existing logfile
 $logFilePath = "$currentDirectory\$logFileName"
 if (Test-Path $logFilePath)
 {
  Remove-Item $logFilePath
 }
 
 # create new log file and start logging
 Start-Transcript $logFilePath

 # check to ensure Microsoft.SharePoint.PowerShell is loaded
 $snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'Microsoft.SharePoint.Powershell'}
 if ($snapin -eq $null)
 {
  Write-Host "Loading SharePoint Powershell Snapin"
  Add-PSSnapin "Microsoft.SharePoint.Powershell"
 }
 
 # deploy the solution
 if ($action -eq "SD")
 {
  Write-Host "Step 1 - Add Solution Package: " $solutionName -foregroundcolor Green
  AddSolution
  
  Write-Host "Step 2 - Deploy Solution Package: " $solutionName -foregroundcolor Green
  InstallSolution
  
  Write-Host "Step 3 - Timer Job to deploy the Solution Package" -foregroundcolor Green
  Wait4TimerJob

  Write-Host "Step 4 - Activate Site Collection Level Features" -foregroundcolor Green
  ActivateSiteFeatures
  
  Write-Host "Step 5 - Activate Web Level Features" -foregroundcolor Green
  ActivateWebFeatures
 } 
 
 # retract the solution
 if ($action -eq "SR")
 {
  Write-Host "Step 1 - Deactivate Web Level Features" -foregroundcolor Green
  DeactivateWebFeatures
  
  Write-Host "Step 2 - Deactivate Site Collection Level Features" -foregroundcolor Green
  DeactivateSiteFeatures
  
  Write-Host "Step 3 - Uninstall Solution Package: " $solutionName -foregroundcolor Green
  UnInstallSolution
  
  Write-Host "Step 4 - Timer Job to Retract the Solution Package" -foregroundcolor Green
  Wait4TimerJob
  
  Write-Host "Step 5 - Remove Solution Package: " $solutionName -foregroundcolor Green
  RemoveSolution
 }
 
 # stop the logging
 Stop-Transcript
}

Step 3 – Define all the Helper Functions

Define the AddSolution function which would use Add-SPSolution PowerShell command to add the solution to the configuration database. Please note that this step just adds the solution entry in the configuration database. It doesn’t deploy the solution or doesn’t deploy the custom files on the SharePoint Servers yet.

# Add the solution package
# Adds a SharePoint solution package to the farm Solution gallery, It doesn't deploy the uploaded solution yet.
function AddSolution()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 if ($solution -eq $null)
 {
  Write-Host "Adding solution package" -foregroundcolor Yellow
  $solutionPath = "$currentDirectory\$solutionName"
  Add-SPSolution -LiteralPath $solutionPath -Confirm:$false
 }
}

Define the InstallSolution function which would use Install-SPSolution to deploy solution to specific web application or all web applications. If solution contains web part, module, event receiver, content type, or any other web application specific SharePoint project item, it would deploy solution to the specific web application otherwise, it would deploy to all the web applications. This step would call Install-SPFeature automatically and adds features entry in the configuration database.IT will also copy all the features and code files the SharePoint Root, copy DLLs into the GAC, and add necessary safe control entries to the Web.Config database.

# Deploy the solution package 
# Deploys an installed SharePoint solution on all the WFE servers in the farm.
# Deploying solution in the farm installs the feature automatically. It installs all the features
# on each server at the farm, web, site collection, and site level but It doesn't activate them.
# Since it provisions the file on each WFE, it is a timer job.
function InstallSolution()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 $solutionId = $solution.Id
 if ($solution -ne $null)
 {
  $solutionDeployed = Get-SPSolution -Identity $solutionId | where-object {$_.Deployed -eq "False"}
  if ($solutionDeployed -eq $null)
  {
   if ( $solution.ContainsWebApplicationResource )
   {
    Write-Host "Deploying solution package to web application: " $webAppUrl -foregroundcolor Yellow
    Install-SPSolution -Identity $solutionName -WebApplication $webAppUrl -GACDeployment -Confirm:$false
   }
   else
   {
    Write-Host "Deploying solution package to all web applications" -foregroundcolor Yellow
    Install-SPSolution -Identity $solutionName -GACDeployment -Confirm:$false
   }
  }
 }
}

Define the ActivateSiteFeteatures function which would use Enable-SPFeature PowerShell command to activate all the site collection level features specified in $siteFeatureNames parameter. This would also add feature activation entry in the Content database features table.

# Activate the Site level features
function ActivateSiteFeatures()
{ 
 if ($siteFeatureNames -ne $null)
 {
  $spSite = Get-SPSite $siteUrl  
  foreach($siteFeatureName in $siteFeatureNames)
  {
   Write-Host "Trying to Activate Site Collection Level Feature: " $siteFeatureName -foregroundcolor Yellow
   $siteFeature = Get-SPFeature -site $spSite.url | where-object {$_.displayname -eq $siteFeatureName}
   if ($siteFeature -eq $null)
   {
    Write-Host "Activating Site Level Features at " $spSite.url -foregroundcolor Yellow
    Enable-SPFeature –identity $siteFeatureName -URL $spSite.url -Confirm:$false
   }
  }  
  $spSite.Dispose()
 }
}

Define the ActivateWebFeatures function which would use Enable-SPFeature PowerShell command to activate all the web level features specified in $webFeatureNames parameter in all the webs in specific site collection. This would also add feature activation entry in the Content database features table.

# Activate the Web level features
function ActivateWebFeatures()
{
 if ($webFeatureNames -ne $null)
 {
  $spSite = Get-SPSite $siteUrl
 
  #Cycle through all webs in the collection and activate all the features
  foreach($spWeb in $spSite.AllWebs)
  { 
   foreach($webFeatureName in $webFeatureNames)
   {  
    Write-Host "Trying to Activate Web Level Features: " $webFeatureName -foregroundcolor Yellow
    $webFeature = Get-SPFeature -web $spWeb.url | where-object {$_.displayname -eq $webFeatureName} 
    if ($webFeature -eq $null)
    {
     Write-Host "Activating " $webFeatureName " at " $spWeb.url -foregroundcolor Yellow
     Enable-SPFeature –identity $webFeatureName -URL $spWeb.url -Confirm:$false
    }
   }
  }
  
  $spWeb.Dispose()
  $spSite.Dispose()
 }
}

Define the DeactivateWebFeatures function which would use Disable-SPFeature PowerShell command to deactivate all the web level features specified in $webFeatureNames parameter in all the webs in specific site collection. This would also remove feature activation entry from the Content database features table.

# Deactivate the Web level features
function DeactivateWebFeatures()
{
 if ($webFeatureNames -ne $null)
 {
  $spSite = Get-SPSite $siteUrl
 
  #Cycle through all webs in the collection and deactivate all the features
  foreach($spWeb in $spSite.AllWebs)
  { 
   foreach($webFeatureName in $webFeatureNames)
   {  
    Write-Host "Trying to Deactivate Web Level Features: " $webFeatureName -foregroundcolor Yellow
    $webFeature = Get-SPFeature -web $spWeb.url | where-object {$_.displayname -eq $webFeatureName} 
    if ($webFeature -ne $null)
    {
     Write-Host "Deactivating " $webFeatureName " at " $spWeb.url -foregroundcolor Yellow
     Disable-SPFeature –identity $webFeatureName -URL $spWeb.url -Confirm:$false
    }
   }
  }
  
  $spWeb.Dispose()
  $spSite.Dispose()
 }
}

Define the DeactivateSiteFeatures function which would use Disable-SPFeature PowerShell command to deactivate all the site collection level features specified in $siteFeatureNames parameter. This would also remove feature activation entry from the Content database features table.

# Deactivate the Site level features
function DeactivateSiteFeatures()
{
 if ($siteFeatureNames -ne $null)
 {
  $spSite = Get-SPSite $siteUrl
  foreach($siteFeatureName in $siteFeatureNames)
  {
   Write-Host "Trying to Deactivate Site Collection Level Feature: " $siteFeatureName -foregroundcolor Yellow
   $siteFeature = Get-SPFeature -site $spSite.url | where-object {$_.displayname -eq $siteFeatureName}
   if ($siteFeature -ne $null)
   {
    Write-Host "Deactivating Site Level Features at " $spSite.url -foregroundcolor Yellow
    Disable-SPFeature –identity $siteFeatureName -URL $spSite.url -Confirm:$false
   }  
  } 
  $spSite.Dispose()
 }
}

Define the UnInstallSolution function which would use UnInstall-SPSolution to retract solution from specific web application or all web applications. If solution contains web part, module, event receiver, content type, or any other web application specific SharePoint project item, it would retract solution from the specific web application otherwise, it would retract from all the web applications. This step would call UnInstall-SPFeature automatically and removes features entry from the configuration database. IT will also delete all the features and code files from the SharePoint Root, remove DLLs from the GAC, and remove necessary safe control entries from the Web.Config database.


# Retract the solution package
# Retracts a deployed SharePoint solution from the farm entirely for all web application or given web application.
# This step removes files from all the front-end Web server.
# Please note that retracting solution in the farm uninstalls the feature automatically, if it hasn't uninstalled using UnInstall-SPFeature.
# Since it removes the file on each WFE, it is a timer job.
function UnInstallSolution()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 $solutionId = $solution.Id
 if ($solution -ne $null)
 {
  $solutionDeployed = Get-SPSolution -Identity $solutionId | where-object {$_.Deployed -eq "True"}
  if ($solutionDeployed -ne $null)   
  { 
   if ( $solution.ContainsWebApplicationResource )
   {
    Write-Host "Retracting solution package from web application: " $webAppUrl -foregroundcolor Yellow
    UnInstall-SPSolution -Identity $solutionName -WebApplication $webAppUrl -Confirm:$false
   }
   else
   {
    Write-Host "Retracting solution package from all web applications" -foregroundcolor Yellow
    UnInstall-SPSolution -Identity $solutionName -Confirm:$false
   }
  }
 }
}

Define the RemoveSolution function which would use Remove-SPSolution PowerShell command to remove the solution from the configuration database. Please note that this step removes the solution entry from the configuration database and completely cleans up the custom solution deployed on the SharePoint environment. 

# Remove the solution package
# Deletes a SharePoint solution from a farm solution gallery
function RemoveSolution()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 if ($solution -ne $null)
 {
  Write-Host "Deleting the solution package" -foregroundcolor Yellow
  Remove-SPSolution $solutionName -Confirm:$false
 }
}

Implement the Wait4TimerJob which will be helper function used after InstallSolution and UnInstallSolution process to ask PowerShell script to wait until solution properly deployed or retracted from all the servers in the farm. It checks timer job status to check whether it’s been completed or not.

# Wait for timer job during deploy and retract
function Wait4TimerJob()
{
 $solution = Get-SPSolution | where-object {$_.Name -eq $solutionName}
 if ($solution -ne $null)
 {
  $counter = 1  
  $maximum = 50  
  $sleeptime = 2 
  
  Write-Host "Waiting to finish soultion timer job"
  while( ($solution.JobExists -eq $true ) -and ( $counter -lt $maximum ) )
  {  
   Write-Host "Please wait..."
   sleep $sleeptime 
   $counter++  
  }
  
  Write-Host "Finished the solution timer job"   
 }
}

Step 4 – Call the Main Function to Execute PowerShell Script Logic

Last step of the PowerShell script is to call the “Main” function defined earlier. This will ask PowerShell engine to run deployment and retraction logic.

#Run the Main Function
main

This concludes the step by step PowerShell script creation process for automated SharePoint solution deployment and retraction process. Next article in this series will provide full version of this PowerShell script and walk through step by step process of how to use this PowerShell script in real world using batch files to automate the process.

Advertisements
This entry was posted in SP2010 Admin PowerShell, SP2010 Dev PowerShell. Bookmark the permalink.

One Response to Automated SharePoint 2010 Farm Level Solution Deployment and Retraction Process – Part II – Building PowerShell Script

  1. Pingback: Simple PowerShell Deployment Scripts | SoftArtisans, Blogged

Comments are closed.