Introduction

Our automatic deployment system consists of:

  • TFS for building and packaging
  • Octopus for deploying and transforming

We extensively use Octopus. One of the steps that it can perform is installing a Sitecore update package created by TDS during the build process.
The flexibility of Octopus allows us to write scripts, like PowerShell, to execute on our servers.
But for that, we need Sitecore Powershell Remoting.

Prerequisites

So before even trying to execute the full script below, make sure you have this setup correctly:

1) A server where the Sitecore Powershell Extensions are installed.
This can also be a local running instance of Sitecore or a dedicated server where the Sitecore Content Management is running.
2) You have activated the Powershell Remoting service by setting the enabled attribute in the remoting services section to true in Cognifide.PowerShell.config

<powershell>
    <services>
        <remoting enabled="true" requireSecureConnection="false">
            <authorization>
                <add Permission="Allow" IdentityType="Role" Identity="sitecore\PowerShell Extensions Remoting" />            
            </authorization>
        </remoting>
    </services>
</powershell>

3) Optionally you can add a specific user that you allow to execute remote scripts. In the example below, I gave the custom created user sitecore\OctopusRemotingUser the permission to use PowerShell remoting.

<powershell>
    <services>
        <remoting enabled="true" requireSecureConnection="false">
            <authorization>
                <add Permission="Allow" IdentityType="Role" Identity="sitecore\PowerShell Extensions Remoting" />
                <!-- Give the user permission that will execute the PowerShell scripts loaded from the remote environment. -->
                <add Permission="Allow" IdentityType="User" Identity="sitecore\OctopusRemotingUser" />
            </authorization>
        </remoting>
    </services>
</powershell>

4) Have a Sitecore update package in a location that is accessible by Sitecore. I've mentioned Octopus above, another step that it does is actually copy the created update package to the server so that the script can use it.
It gets installed in the following fysical folder:
C:\inetpub\wwwroot\yoursitename\sitecore\admin\Packages\scitems.update
5) A location from where to run the script. Mine is Octopus, but you can use this locally too. Just follow the instructional video by Michael West

The PowerShell remoting script

On to the script itself.
In Octopus it looks a bit different since several variables are passed into the script. But the script below will work when testing locally.

Since installing a package can be a long running task, we use Wait-RemoteScriptSession to poll the executing job every 5 seconds.

$SitecoreInstanceUri = "https://remotesitecore"
$SitecorePackagePath = "C:\inetpub\wwwroot\yoursitename\sitecore\admin\Packages\scitems.update"

#Create a new script session
$session = New-ScriptSession -Username OctopusRemotingUser -Password b -ConnectionUri $SitecoreInstanceUri

Write-Host "Installing Sitecore update package '$SitecorePackagePath' on '$SitecoreInstanceUri'"

#Invoke the script, actually start installing the package
$jobId = Invoke-RemoteScript -Session $session -ScriptBlock {
	$package = $using:SitecorePackagePath
	Write-Log "Installing Sitecore update package '$package'"
	Install-UpdatePackage -Path $package -UpgradeAction Upgrade -InstallMode Install
} -AsJob -Verbose

#Double check if a jobId was created. If we don't have an id, something is wrong
if (!$jobId) { 
	Write-Host "No jobId was created. Please check if your Powershell Remoting is activated on the target instance '$SitecoreInstanceUri'"
}

#Poll every 5 seconds and check if the script has completed
Wait-RemoteScriptSession -Session $session -Id $jobId -Delay 5 -Verbose

Write-Host "Sitecore update package installed."
Stop-ScriptSession -Session $session

Script variant

A slightly more complex script without Wait-RemoteScriptSession but a custom wait function.
The main differences with the script above are:

  1. custom polling with Start-Sleep -Seconds 5
  2. a try-catch block since we experienced some problem last year where the script would keep on running forever. But this should already be solved: https://github.com/SitecorePowerShell/Console/issues/652
$SitecoreInstanceUri = "https://remotesitecore"
$SitecorePackagePath = "C:\inetpub\wwwroot\yoursitename\sitecore\admin\Packages\scitems.update"

#Create a new script session
$session = New-ScriptSession -Username $Username -Password $Password -ConnectionUri $SitecoreInstanceUri

Write-Host "Installing Sitecore update package '$SitecorePackagePath' on '$SitecoreInstanceUri'"

$jobId = Invoke-RemoteScript -Session $session -ScriptBlock {
	$package = $using:SitecorePackagePath
	Write-Log "Installing Sitecore update package '$package'"
	Install-UpdatePackage -Path $package -UpgradeAction Upgrade -InstallMode Install
} -AsJob -Verbose

if (!$jobId) { 
	Write-Host "No jobId was created. Please check if your Powershell Remoting is activated on the target instance '$SitecoreInstanceUri'"
}

#doneScript is the script that gets executed every 5 seconds and checks the state of the executed script. It returns a custom PowerShell object with Name, IsDone and Status
$doneScript = {
        $backgroundScriptSession = Get-ScriptSession -Id $using:jobId
        $isDone = $backgroundScriptSession -eq $null -or $backgroundScriptSession.State -ne "Busy"
        [PSCustomObject]@{
            "Name" = $backgroundScriptSession.Id
            "IsDone" = $isDone
            "Status" = "$($backgroundScriptSession.State)"
        }
    }
	
try {
    $keepRunning = $true
    while($keepRunning) {
        #Execute the doneScript and check the response.
        $response = Invoke-RemoteScript -Session $session -ScriptBlock $doneScript
        if($response -and $response.IsDone) {
            $keepRunning = $false
            Write-Host "Polling job $($response.Name). Status : $($response.Status)."
            Write-Host "Finished polling job $($id)."
        } else {
            Write-Host "Polling job $($response.Name). Status : $($response.Status)."
            Start-Sleep -Seconds 5
        }
    }   
} catch {
    if ($_.Exception.Message -eq "ScriptSessionNotFound,Cognifide.PowerShell.Commandlets.ScriptSessions.GetScriptSessionCommand") {
        Write-Host "Package installation triggered application pool recycling."
    }
	else
	{
	 throw
	}
}


Write-Host "Sitecore update package installed."
Stop-ScriptSession -Session $session