
Artifact Promotion Deployments: Sitecore Built in the Cloud
March 28, 2017

To start off I am guessing some of you reading this are wondering what exactly the headline means so I will ask you to set your current Sitecore build, deployment and branching strategy aside and try to visualize a workflow where all development gets merged into one branch, and all builds go thru Dev
before ever getting to a QA
or Production
server.
I assume you are as skeptical as I was about this (I thought this was complete and utter heresy). It took me actually implementing this approach before I was on board and I’ll do my best to illustrate it to you in this post.
The Plan
Here’s a breakdown of what the project I use throughout this post consists of:
- A standard Sitecore 8.2 install
- A Visual Studio Online Project, for our example we are going to use TFS-git for our source control
- 3 TDS projects (could use Unicorn as well, but for sake of argument, we are using TDS) to manage our
Core
changes, testContent
, and things we will want to alwaysDeploy
- A web project (with our custom layout changes) with
Sitecore.Ship
installed
Below is a high level breakdown of what’s in this post:
- The Project; some of the necessary project setup
- The Build; the details needed in the VSTS Build Definition
- The Release; details around the VSTS Release Definition
- The Branch Strategy; given the new deployment approach, how we plan on managing the code
The Project
Now that you have the high-level project overview, let’s go a little bit into the actual Visual Studio Solution.
You can see in the Solution Explorer
screenshot, the Feydra
solution (yes, it’s named after the very nifty front-end productivity tool that I will blog about at a later date) consists of the three TDS projects, and one Feydra.Web
project.
For our local Debug
target we set up the TDS.Deploy
project to do the local file deployments (unchecking Disable File Deployment
and pointing the deploy folder to our local Sitecore webroot), but on our VSTS build we will want something completely different. Assuming you have all the necessary projects in your solution, create a new build configuration called VSTSBuild.
Now, switch the active configuration to VSTSBuild
and open up the properties of all the TDS projects.
In here, there should be no values for any of the projects under the Build
tab, unlike the Debug
configuration. Instead, we will setup each one of these to create an Update Package
. On the Update Package
tab, check the Generate package during build
, choose Generate only an item package
for the Package Generation Options
, and update Sitecore Assembly Path
to point to your /src/lib/Sitecore
directory (I usually grab the dlls I will need from my webroot/bin and drop them here). I believe the Update Package
requires at least the Sitecore.Zip, Sitecore.Update, Sitecore.Kernel
, and Sitecore.Logging
dlls. Your Update Package
tab should look like:
Now, if you have updated all of the project properties correctly, you should be able to build the entire solution under the VSTSBuild
configuration and see a TDS.Content/bin/Package_VSTSBuild/TDS.Content.update
file.
TDS on VSTS
The next big piece to this is getting TDS to work in a cloud environment.
- Open a file explorer, grab all files from
C:\Program Files (x86)\MSBuild\HedgehogDevelopment\SitecoreProject\v9.0
, and drop them into asrc/lib/tds/
directory (or something similar). - Open the
HedgehogDevelopment.SitecoreProjects.targets
file, and update allAssemblyFile="HedgehogDevelopment.SitecoreProject.Tasks.dll"
toAssemblyFile="$(MSBuildThisFileDirectory)HedgehogDevelopment.SitecoreProject.Tasks.dll"
(should be lines 2-26). Then update one more line,TDSTaskAssemblyPath="HedgehogDevelopment.SitecoreProject.Tasks.dll"
toTDSTaskAssemblyPath="$(MSBuildThisFileDirectory)HedgehogDevelopment.SitecoreProject.Tasks.dll"
and save. - Add a
TDSLicence.config
file to that same directory (and yes, use that specific spelling). Inside this config, put<license Owner="__tdsOwner__" Key="__tdsKey__" />
and save.
Ship Utils
The Powershell scripts will make sense later when we get to deploying, but for now, go here, grab everything, and drop it into a /src/utils
directory.
The Build
This goes without saying, but you could and should have been committing this code throughout the post. At this point, save everything and navigate over to the VSTS instance. Also, one thing I would like to clarify, when I say VSTS I mean Visual Studio Online, but most if not all of these concepts can be applied to the newer versions of TFS and ReleaseManagement. To start setting up the build, we need to create a new Build Definition
in VSTS. On the Build
tab of VSTS, there should be a + New
, click that and you should see:
Select Visual Studio
and click Next
. Now we’ll select our repository, we are using VSTS TFS-git, but as you can see, you can use several other repositories as well:
Whatever you are planning on using, select it and hit Create
. What you should now see is a list of Build Tasks
, the default ones defined for a Visual Studio build. This will get us about half way there, so let’s add a few more things.
- Go out to the marketplace and grab a tool to replace the tokens we put into the
TDSLicence.config
(like this add-on). - Once that’s installed, add a new
Build Task
to theBuild Definition
, findReplace Tokens
in the list of tasks and hit add. - Update the fields as follows: root directory
lib
, target files as**\*.config
and under Advanced, change theToken Prefix/Suffix
to a double underscore. - It should end up looking like:
This Replace Token Task
should appear before the Build Solution
task, that way our variables get passed into the license file before build, allowing for a successful build. This task can also be used to replace any config file; web.config, connectionstrings, include files, etc. Before we get any further, we also need to add/update the necessary variables; BuildConfiguration
, tdsOwner
and tdsOwner
:
Next add /p:OutDir=$(build.stagingDirectory) /p:UseWPP_CopyWebApplication=true /p:DeployOnBuild=true /p:SkipInvalidConfigurations=true
to the MSBuild Arguments
of the Build Solution **\*.sln
task. This will tell MSBuild where to deploy the end site in the scope of VSTS:
Artifacts
The next few tasks we will add are three Publish Artifacts
(all with the Artifact Type: Server
).
- One for the Website:
Path To Publish: $(build.artifactstagingdirectory)\_PublishedWebsites\Feydra.Web
(update this to your project name) - One to publish the Utils:
Path To Publish: utils
- One for TDSPackages:
Path To Publish: $(build.artifactstagingdirectory)\_Packages
It should look something like:
If you want the build to be triggered on a code commit, on the Build Definition
go to the Triggers
tab, check the Continuous Integration (CI)
checkbox, and tell it to Include: /refs/heads/master
.
At this point, the initial Build Defnition
should be all set!
Stay up to date with our email updates!

Never miss out on a Sitecore update again!
Our newsletter features a blog roundup of our top posts so you can stay up to date with industry trends, tutorials, and best practices.
The Release
To preface this entire section, the environment I am working with is an Azure Sitecore Webapp. It contains two nodes, a CM
and a CD
node. It was a quick and easy way to get Sitecore up and running on an environment other than my local. Now that the build is compiling successfully and the Artifacts
are being published, we can access all the artifacts from the Releases
tab in VSTS. Think of a Release
as the formal deploy process, in its simplest form.
The next step is to create an empty Release Definition
:
The second step will ask you what Project
and what Build Definition
you want to use for its authority (ie: what it will use to trigger a release upon successful build and what it will use as its Artifact
source):
If you want the release to execute on a successful build, check the Continuous deployment
checkbox.
The Deploy
Now we need to add some tasks. The first Environment
we are going to define in this Release Definition
is our content authoring server, or CM
. Since we are using the Azure Sitecore Web App, the first task we want to add is a Deploy Azure App Service
. This should only take us associating our subscription and appliance, and it should deploy:
This, in a nutshell, will Web Deploy
the Website
artifact over our Sitecore webroot. You are probably thinking, "well, that's great and all, but what about the transforms, what about the differences between my CD and CM instance?" That's what we'll answer next.
The Transform
Traditionally transforming happens on build, but since we are using one authoritative build, we either need to handle the differences via tokenized Variables
in the VSTSBuild transformed version of the config, or include a config to do the transforming outside of the build.
Above, you see a screenshot of a config
folder that organizes a few configs that will be used, with a config transform task (ConfigTransformation), to manipulate any last changes needed to distinguish environments. These configs can be configured however you need and applied surgically. I chose to take the easiest route and just make transforms specific to the instances themselves.
Above is the ConfigTransform
setup for my CM
instance. This "variables first, surgical transforms second" approach makes you consider what changes you are trying to make from the standard Sitecore webroot.
We also want to add a Tokenization task like we did for the build in this release definition. It will be the exact same but with the Website Build artifact as the root directory (allowing tokenization on any config in the website build).
The Publish
The last part of this entire ball of wax is the actual publish of the TDS items from the initial build. This obviously will vary based on what projects are in your solution, but for our example let's pick TDS.Core
, a package that holds all the TDS changes to the Core
database. First, add a Powershell
task:
Then let's fill that task in:
- The script path will be the path to your
Utils
artifact and thedeploy-sitecorepackage.ps1
script - Arguments:
<siteUrl> <packagePath> <connectionTimeout(optional)> <maxTimeout(optional)>
- The working folder: set it to the TDS project directory to make the above args more readable
Fail on Standard Error
if this fails, we do not want to keep installing packages
You will want to repeat these steps for the other .update
packages in the solution that you want to be installed.
We will also want to add is another Powershell
task to actually publish these items. It will follow very similar rules to the deploy steps, but have some different values:
- Use the
publish-sitecorepackage.ps1
script in this instance from theUtils
artifact <siteUrl> <publishTargets> <publishMode> <connectionTimeout(optional)> <maxTimeout(optional)>
- There's no need for
Working Folder
to be set in this instance
These last handy bunch of Powershell scripts will deploy and publish your update packages to your Sitecore site, but there may or may not be one last roadblock for that work: the Sitecore.Ship Whitelist
values. This varies from project to project, but since my example has build and release both using the Hosted Build/Release Agent, we don't actually know the IP address the request will come from. Below is the very last tasks needed to get this project to succeed:
- Add a Release
Variable
namedReleaseIPAddr
and give it the value0.0.0.0
- Add a
Powershell Script
task to the Release definition (not the same as before, this one will give us the ability to add inline script) - In the Task, for the
Type
set it toInline Script
and in theInline Script
text area, add: $releaseIPAddr = Invoke-RestMethod http://ipinfo.io/json | Select -exp ip
Write-Host "Variable: " $(ReleaseIPAddr)
Write-Host "Current External IP: " $releaseIPAddr
Write-Host "##vso[task.setvariable variable=ReleaseIPAddr;]$releaseIPAddr"
(this will set the ReleaseIPAddr variable to the IP address of the Hosted Release Agent)- Add
<add name="vsts_release_agent" IP="__ReleaseIPAddr__" />
to thepackageInstallation/Whitelist
in the Web.VSTSBuild.Config
The Release Definition should look roughly like:
And at this point, you should be able to commit, build, and deploy all thru one single commit!
The Branch Strategy
Normally, I would follow something similar to git-flow
strategy: setting up a branch or tag for each environment, leaving master mainly untouched until merging in a release and conducting all new work, unrestricted, in the origin/develop
. Promotion to environments would be done thru merging the origin/develop
branch into the origin/qa
and so on up the chain.
In most of the recent projects we used git-flow
on, we would have 1 build per environment, meaning if we had dev, qa, staging
, and production
environments, we would have 4 branches, with 4 builds and deploys. Ultimately this means having 4 builds that do the exact same thing and have the potential of getting out of sync (ie: someone pushes a fix to qa or beta, never merges back into develop and some config files get left hanging out on the server). Given the confines of VSTS, we figured it's time for a different approach.
Introducing our plan: its simple, 1 build to rule them all! Not entirely true, but our original goal was to have one branch trigger all builds and be the authority for all builds that get promoted. What we wanted was something strict, clean, and easy to follow. The strategy we landed on that seems to have been working well so far is:
- All builds built off of the
origin/master
(triggered) - All feature development is built through Pull Requests and named in a
feature/branch
scheme (name it logically, by user story, number, or whatever you need to distinguish them) - All merges are done thru approving Pull Requests (developers are not able to commit/merge directly to
origin/master
) - Develop has a triggered release to update the develop environment automatically
- All upper environments are done manually upon approval of development
- When the need for a hotfix arises:
- Feature level Pull Request approvals freeze (development can continue, but no dev builds will be triggered)
- A
hotfix/branch
is created off oforigin/master
at the commit/tag that production is at - A manual build is created off of the
hotfix/branch
, and promoted thru the dev/qa/staging servers to eventually make it to production - Once that is completed, the hotfix is approved/merged back into
origin/master
and feature level PRs are unfrozen
There are most likely caveats to this approach that we have not run into yet, but it has proven to be fairly straightforward and clean thus far.
Hopefully, this outlined process can help you define your own artifact promotion build process on VSTS and you will never have to touch a NAnt or CCNet config ever again!
If you found this post useful, check out my other posts here.