Create a custom component to deploy SharePoint Solutions – Part 2

In the first part of the series we have adjusted a build template in order to copy content from source control to the drop location after the build. This content can then be used to supply release management with information about the deployment. This functionality will be used to copy a configuration file to the drop location. With PowerShell were our custom component will exist off we can read out that configuration file.

As you can read we will create our custom component in specific steps and we will start with the configuration file.

 Configuration File

The configuration file will contain all of the information that is needed to deploy a SharePoint solution package. As example you would like to know to which web application the solution needs to be deployed to.

As example take a look at the following sample configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<!-- 
  Section to specify the configuration for the deployment
-->
<Configuration>
  <!--
    Section to specify environment information
  -->
  <Environment>
    <DEV>
      <WebApplications>
        <WebApplication ID="1" Url="http://sample.msftplayground.local"></WebApplication>
      </WebApplications>
    </DEV>
    <TST>
      <WebApplications>
        <WebApplication ID="1" Url="http://sample-tst.msftplayground.local"></WebApplication>
      </WebApplications>
    </TST>
    <ACC>
      <WebApplications>
        <WebApplication ID="1" Url="http://sample-acc.msftplayground.local"></WebApplication>
      </WebApplications>
    </ACC>
    <PROD>
      <WebApplications>
        <WebApplication ID="1" Url="http://sample.msftplayground.local"></WebApplication>
      </WebApplications>
    </PROD>
  </Environment>
  <!--
    Section to specify information on solutions were to deploy to or retract.
    -->
  <Solutions>
    <!--
    Section to specify the solutions that need to deployed.
    -->
    <Deploy>
      <!--
      Section to specify specific solution information
      
      Name = Name of the solution 
      Upgrade = Upgrade the solution if it exists
      DeployToAll = Deploy to all content applications
      DeployNotExits = Deploy the solution if it does not exist when upgrade is set to true
      WebApplication = The web application to deploy to specified with the ID when deploy to all is false
      -->
      <Solution Name="MSFTPlayground.SharePoint.wsp" Upgrade="true" DeployToAll="false" DeployNotExists="true">
        <WebApplication ID="1" />
        <WebApplication ID="2" />
        <WebApplication ID="3" />
      </Solution>
    </Deploy>
    <Retract>
      <!--
      Section to specify specific solution information
      
      Name = Name of the solution 
      Remove = Remove the solution after retraction
      RetractAll = Retract from all content applications
      -->
      <Solution Name="MSFTPlayground.SharePoint.wsp" Remove="true" RetractAll="true">
        <WebApplication ID="1" />
        <WebApplication ID="2" />
        <WebApplication ID="3" />
      </Solution>
    </Retract>
  </Solutions>
</Configuration>

In the configuration file you can specify which solution package you would like to deploy and to what web application. The web applications are specified in the “Environment” section. The configuration file contains a “Environment” section to be able to use the same mechanism for multiple environments that use different URLs. Within the solution section we then refer to a specific web application by its ID.

To make it more complex we also added some other properties (Upgrade, DeployToAll) to make the components more advanced.

PowerShell

With the configuration file we can start writing the PowerShell that will read out the configuration file and deploys the solution.

param(
    [string]$filename = $(throw "Specify the Config filename that is located in the drop folder"),
    [string]$environment = $(throw "Specify the Environment you would like to deploy to.")
)

if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
    Write-Host "Loading Microsoft SharePoint PowerShell" -ForegroundColor Green
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
Write-Host "#### Start deploying solutions ####"

if ((Test-Path $fileName) -eq $true) {
    . ".\functions.ps1"
    Write-Host "Loading configuration file $filename" -ForegroundColor Green

    try{
        $config = [xml] (Get-Content $fileName)
        Deploy-Solutions
    }catch{
        Write-Host $_.Exception.Message -ForegroundColor Red
        throw "Exception occured while deploying solutions"
    }
}else {
    throw "Error loading configuration file, exiting the deployment"
}

Write-Host "#### End deploying solutions ####"

This PowerShell file needs to be run with two parameters:

  • Filename: The path to the configuration file in the drop location.
  • Environment: The environment you would like to deploy to. This should be the same as the environment section you have specified in the configuration file.

Within the PowerShell file the SharePoint PowerShell module is loaded and besides that I load a custom functions file. In the functions file I have a couple of PowerShell methods that I use to do some advanced stuff on the configuration file, and also some more SharePoint functions. When everything is loaded the configuration file is loaded in the  config variable and we call the function “Deploy-Solutions” that is within the same file.

function Deploy-Solutions(){
    $path = ".\";
    $type = "Deploy";

    foreach($solution in $config.Configuration.Solutions.Deploy.Solution){
        
        $solutionName =  $solution.Name;
        $currentDirectory = Get-Location
        $solutionPath = [IO.Path]::GetFullPath([String]::Concat($currentDirectory, "\" ,$path, "\", $solutionName))    
        Write-Host " > Solution path is '$solutionPath'" -ForegroundColor Yellow

        $webApplicationScoped = Get-SolutionProperty $solutionName "ContainsWebApplicationResource"
        $solutionDeployed = Get-SolutionProperty $solutionName "Deployed"
                               
        [bool]$deployToAll = Check-SolutionConfigProperty $solutionName "DeployToAll" $type
        [bool]$upgrade = Check-SolutionConfigProperty $solutionName "Upgrade" $type
        [bool]$deployNotExists = Check-SolutionConfigProperty $solutionName "DeployNotExists" $type
        $webApps = Get-WebAppsForSolution $solutionName $environment $type

        # Check if file exists
        if ((Test-Path $solutionPath) -eq $false) {
            Write-Host " > Solution file doesn't exist!" -ForeGroundColor Red
            throw "Solution file doesn't exist!";
        }
        $deploySolution = $false;

        if (Check-SolutionExists $solutionName) {
            Write-Host " > Solution exists in solution store" -ForegroundColor Yellow
            if(!$upgrade){
                Write-Host " > Solution exists in solution store" -ForegroundColor Yellow
                                               
                if($solutionDeployed) {
                    Write-Host " > Solution is deployed, uninstall solution $solutionName" -ForegroundColor Yellow
                    if ($webApplicationScoped) {
                        $solution = Get-SPSolution $solutionName
                        $deployedTo = $solution.DeployedWebApplications

                        foreach ($webApp in $deployedTo) {
                            Write-Host " > Uninstall from web application '$webApp'" -ForeGroundColor Yellow
                            Uninstall-SPSolution -Identity $solutionName -WebApplication $webApp -Confirm:$false  -ErrorAction Stop
                            Wait-ForJobToFinish($solutionName)
                        }                                                    
                    } else {
                        Write-Host " > Solution doesn't contain web application resource, uninstall globally." -ForeGroundColor Yellow
                        Uninstall-SPSolution -Identity $solutionName -Confirm:$false -ErrorAction Stop
                        Wait-ForJobToFinish($solutionName)
                    }
                    Write-Host " > Solution uninstalled" -ForeGroundColor Green
                }
                Write-Host " > Remove solution $solutionName from solution store" -ForeGroundColor Yellow
                Remove-SPSolution -Identity $solutionName -Confirm:$false -ErrorAction Stop
                Write-Host " > Solution removed" -ForeGroundColor Green        
                $deploySolution = $true                          
            }else{
                Write-Host " > Upgrade SPSolution $solutionName" -ForegroundColor Yellow
                Update-SPSolution -LiteralPath $solutionPath -Identity $solutionName -Force -GACDeployment -ErrorAction Stop       
                Wait-ForJobToFinish($solutionName)                
                $deploySolution = $false
            }

            
        }else{
            Write-Host " > Solution does not exists in solution store" -ForegroundColor Yellow

            if(($upgrade -And $deployNotExists)){
                Write-Host " > Solution does not exists and will be added because it needed to be deployed when it did not exists"  -ForegroundColor Yellow
                $deploySolution = $true
            }

            if(!($upgrade)){
                Write-Host " > Solution does not exists and will be added in order to deploy"  -ForegroundColor Yellow
                $deploySolution = $true
            }
        }
        
        if($deploySolution){
            Write-Host " > Add solution $solutionName to solution store" -ForeGroundColor Yellow
            Add-SPSolution -LiteralPath $solutionPath -ErrorAction Stop
            Write-Host " > Solution added" -ForeGroundColor Green

            Write-Host " > Deploy solution $solutionName" -ForeGroundColor Yellow
            $webApplicationScoped = Get-SolutionProperty $solutionName "ContainsWebApplicationResource"

            if ($webApplicationScoped) {
                if ($deployToAll) {
                    Write-Host " > Deploying to all Web Applications" -ForeGroundColor Yellow
                    Install-SPSolution –Identity $solutionName -GACDeployment -AllWebApplications -Force -ErrorAction Stop
                    Wait-ForJobToFinish($solutionName)
                } else {
                    foreach ($webApp in $webApps) {
                        Write-Host " > Deploying to $webApp" -ForeGroundColor Yellow                                                          
                        Install-SPSolution –Identity $solutionName -WebApplication $webApp -Force -GACDeployment -ErrorAction Stop
                        Wait-ForJobToFinish($solutionName)
                    }
                }
            } else {
                Install-SPSolution –Identity $solutionName -GACDeployment -Force -ErrorAction Stop
                Wait-ForJobToFinish($solutionName)
            }
        }

        $success = Get-SPSolutionSuccessState $solutionName

            if($success){
                Write-Host " > Solution $solutionName installed" -ForeGroundColor Green
            }
            else{
               Write-Host " > Solution $solutionName could not be installed correctly." -ForeGroundColor Green
               throw "Error occured while installing solution $solutionName"
            }
    }                          
}

In the Deploy-Solution function we navigate trough the xml and deploy the solution packages specified in the configuration file. Within the Deploy-Solution function we also use some functions that are specified in the functions.ps1 file.

With the PowerShell files in place we can start adding things to Release Management. The first action in Release Management is creating a new Tool. By going to “Inventory” – “Tools”  and click “New” to create a new tool.

ReleaseManagement-New-Tool

In this screen you will have to supply all required information:

Name: Deploy SharePoint Solutions
Description: Tool for deploying SharePoint solutions
Command: powershell (Because we would like to run powershell)
Arguments: -command .\Deploy-SPSolution.ps1 “__ConfigFileName__” “__Environment__”
Resources: functions.ps1 and Deploy-Solution.ps1

ReleaseManagement-New-Tool-Complete

When specifying __ (double underscores) before and after a value in the arguments section Release Management will recognize this as a parameter that you can specify in for example the Release Template.

The executing part of the tool specifies what the Release Management agent should execute. In this example it will execute the PowerShell file “Deploy Solution.ps1” with the specified parameters. Because Release Management does not have this file included we add it as resource the same for the functions file.

A tool only cannot be used within a Release Template. In order to use it within a Release Template we also need to define a component. An component is a object that will take the files (from for example the build drop location) and combines it with the tool we specify (A component can do much more but is not relevant for this post).

To define a component go to: Configure Apps – Components and then select “New”. First step is to fill in a name and make a selection for the source. As we will use the drop location for the source select the option: Builds with application and enter “ \” as the path to the package. Configuring it this way will make sure that the root drop location is copied to the executing path of the build agent together with the tool files we will specify in the next step.

ReleaseManagement-New-Component

The tool needs to be specified in the deployment tab. The deployment tab is the action you would like to take (tool you would like to execute). Here we will select the tool we created and the other fields are filled in for us.

ReleaseManagement-New-Component-Deployment

With all this in place we can save the component and use it within a Release Template. In Part 3 we will discuss how to create the release template together with the component and start the release template after a build.

Related Posts

Deploy your Azure Website with VSTS Build From VSTS (Visual Studio Team Services) your can deploy to a Azure Website directly directly from a build. Setting this up will require a couple of st...
Release Management in Visual Studio Team Services Visual Studio Team Services is the formally know Visual Studio Online. The old name brought a lot of confusion I think the new name will do a lot bett...
Creating the Release Template that runs after the Build – Part 3 With the component created in part 2 we can start using it to deploy applications to our SharePoint environment. We start by creating a new template. ...
Adjust the ReleaseTfvcTemplate.12.xaml build template to work with the BizTalk D... If you want to use the BizTalk Deployment framework in combination with Release Management you need to adjust the default build template that comes wi...
Deploy SharePoint Solutions with Release Management In one of my previous post (Configuring Web Site Binding with Release Management) I showed how you can make a custom action for Release Management to ...
Offline Installation SharePoint 2013 In many situation you would like to do a offline installation of SharePoint 2013 or you are required to do a offline installation because you do not h...

3 comments

  • Hi,
    Can i have the entire source code for the config files and the powershell scripts?
    Thanks..

  • Hi Kumar,
    thanks for this wonderful article around deploying powershell via release management. one question though, is the functions.ps1 the script for the deploy-solutions?
    Farah

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.