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
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.
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
Content, and things we will want to always
- A web project (with our custom layout changes) with
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
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
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 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 a
src/lib/tds/directory (or something similar).
- Open the
HedgehogDevelopment.SitecoreProjects.targetsfile, and update all
AssemblyFile="$(MSBuildThisFileDirectory)HedgehogDevelopment.SitecoreProject.Tasks.dll"(should be lines 2-26). Then update one more line,
- Add a
TDSLicence.configfile to that same directory (and yes, use that specific spelling). Inside this config, put
<license Owner="__tdsOwner__" Key="__tdsKey__" />and save.
The Powershell scripts will make sense later when we get to deploying, but for now, go here, grab everything, and drop it into a
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:
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 Taskto the
Build Definition, find
Replace Tokensin the list of tasks and hit add.
- Update the fields as follows: root directory
lib, target files as
**\*.configand under Advanced, change the
Token Prefix/Suffixto a double underscore.
- It should end up looking like:
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;
/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:
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
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.
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
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
If you want the release to execute on a successful build, check the
Continuous deployment checkbox.
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.
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 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
Then let's fill that task in:
- The script path will be the path to your
Utilsartifact and the
<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 Errorif 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.ps1script in this instance from the
<siteUrl> <publishTargets> <publishMode> <connectionTimeout(optional)> <maxTimeout(optional)>
- There's no need for
Working Folderto 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
ReleaseIPAddrand give it the value
- Add a
Powershell Scripttask 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
Typeset it to
Inline Scriptand in the
Inline Scripttext 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 name="vsts_release_agent" IP="__ReleaseIPAddr__" />to the
packageInstallation/Whitelistin 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
- All feature development is built through Pull Requests and named in a
feature/branchscheme (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
- 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)
hotfix/branchis created off of
origin/masterat 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/masterand 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.