Config file transforms with Azure Devops

For a long time now our primary CI setup has been based around Team City and Octopus deploy, but as reliable as it is there are things I don’t like about it:

  1. It’s not a SASS setup meaning there’s a VM to occassionaly think about and updates to install. While Octopus is now availiable as a SASS option, Team City is not and moving Octopus will only solve half the problem.
  2. That VM they both sit on every so often gets and issue with it’s hard disk being full.
  3. It’s complicated to recommend the same setup to clients. You end up having to go through multiple things they need to buy which then require some installation and ongoing maintenance. Ideally we would have a setup thats easy for them to replicate and own themselves with minimal maintenance.

So when we took over a site recently that typically came with no existing CI setup in place, I decided to take a look at using Azure Devops instead. You can use Azure Devops with Octopus Deploy but as it claims to be able to manage releases as well as builds we went for doing the whole thing just in Azure Devops.

Getting a build set up was relatively straight forward so I’m going to skip past that bit, but in short we ended up with a build that will create a web deploy package and publish it as an artifact. Typical msbuild type stuff.

File transforms and variable substitution

The first real tricky point came with replacing variables in config files during a release to each envrionment. We were using the IIS Web App Deploy task to deploy the application to IIS on a VM (no new Azure Web App Services in this setup ūüė¶ as I said we took over the site and this was just to get automated deploys of what they already have). A simple starting point with this is some built in functionality for XML Variable Substituion in the IIS Web App Deploy task.

Quite simply you can add all your varibles to the variable list, set the scope for which envrionment you want it to apply to and the during the deploy they are replaced in your config. Unlike some tag replacement tools I’ve used in the past this one actually uses the name of the connecting string or app setting you need to set, so if you need to set a connection string named web, the variable name will be web.

This is also where my problem stated. The description for what XML variable substitution does is:

Variables defined in the build or release pipeline will be matched against the ‘key’ or ‘name’ entries in the appSettings, applicationSettings, and connectionStrings sections of any config file and parameters.xml

This was a Sitecore solution and for Sitecore most of your config settings are in Sitecores own Sitecore section of the config file. So in other words the connection string will get updated but the rest won’t.

Parameter and SetParameter XML files

My next issue was trying to find a solution is actually quite hard. Searching for this problem either gave me a lot of results for setups using ARM templates (as I said, this was a solution we took over and that kind of change is not on the agenda), or you just get the easy bit above. Searching for Sitecore and Azure Devops also leads you to a lot of results on a cloud infrastructure setup (again not what we’re doing here, at least in the short term). Everything that was coming up felt far more complicated than the solution should be.

However the documentation on the XML variable substitution did have one interesting sentance.

If you are looking to substitute values outside of these elements you can use a (parameters.xml) file, however you will need to use a 3rd party pipeline task to handle the variable substitution.

A parameters.xml file isn’t something I’ve used before which makes this sentance a bit cryptic. The first half says I can do what I want with an xml file, but the second half says I’ll need something else to actually do it.

After a bit of reasearch this all comes back to web deploy. When you do a build that outputs a web deploy package, you get 5 files.

A zip file containing the actual site, a command file which has the script to do the deploy and a set parameters file which is used to set config variables during the deploy. The others aren’t so imporant.

To have different config set on different envrionments you just need to edit the set parameters file. But first you need to have the parameter in the set parameters file so that you can actually change it and this is where the parameters.xml file comes in.

Creating the parameter files

Add a file called parameters.xml file to the root of your project and then add parameters as follows.

<?xml version="1.0" encoding="utf-8" ?&gt;
<parameters&gt;
  <parameter name="DataFolderLocation" defaultvalue="#{dataFolder}"&gt;
    <parameterEntry kind="XmlFile" scope="App_Config\\Include\\Z.Project\\DataFolder\.config$" match="/configuration/sitecore/sc.variable[@name='dataFolder']/patch:attribute/text()" /&gt;
  </parameter&gt;
</parameters&gt;

Some important parts:

default valueThe value that the config setting will get set to
scopeThe path to the file containing the setting
matchAn XPath expression for find the part of the config file to update

Once you have this the build will start producing a SetParameters.xml file containing the extra parameters.

<?xml version="1.0" encoding="utf-8"?&gt;
<parameters&gt;
  <setParameter name="IIS Web Application Name" value="Default Web Site/SiteCore.Website_deploy" /&gt;
  <setParameter name="DataFolderLocation" value="#{dataFolder}" /&gt;
</parameters&gt;

Note: I’ve set the value to be something I intend to replace in the release process.

Replacing the tokens

With our SetParameters.xml file now contining all the config we need to update, we need a step in the release process that will replace all the tokens with the correct values.

To do this I used a replaced tokens task https://marketplace.visualstudio.com/items?itemName=qetza.replacetokens

Config options need to be set for:

Root DirectoryPath to the folder containing the SetParameters.xml file
Target filesA list of files to have replacements done in. In our case this was SiteCore.Website.SetParameters.xml
Token prefixThe prefix on tokens to be search for. Ours was #{
Token suffixThe suffix to denote the end of a token. Ours was }

Lastly in the IIS Web App Deploy step the SetParameters file needed to be selected and the new variables added to the variable list in Azure Devops. The variable names need to be called the bit between your prefix and suffix. i.e. #{datafolder} would be called datafolder.

If you don’t set the variables then the log’s will show warning for each one it couldn’t find.

2019-09-24T17:17:21.6950466Z ##[section]Starting: Replace tokens in SiteCore.Website.SetParameters.xml
2019-09-24T17:17:23.9831695Z ==============================================================================
2019-09-24T17:17:23.9831783Z Task         : Replace Tokens
2019-09-24T17:17:23.9831816Z Description  : Replace tokens in files
2019-09-24T17:17:23.9831861Z Version      : 3.2.1
2019-09-24T17:17:23.9831891Z Author       : Guillaume Rouchon
2019-09-24T17:17:23.9831921Z Help         : v3.2.1 - [More Information](https://github.com/qetza/vsts-replacetokens-task#readme)
2019-09-24T17:17:23.9831952Z ==============================================================================
2019-09-24T17:17:27.2703037Z replacing tokens in: C:\azagent\A1\_work\r1\a\PublishBuildArtifacts\SiteCore.Website.SetParameters.xml
2019-09-24T17:17:27.3133832Z ##[warning]variable not found: dataFolder
2019-09-24T17:17:27.3179775Z ##[section]Finishing: Replace tokens in SiteCore.Website.SetParameters.xml

With all this set our config has it’s variables configured within Azure Devops for each envrionment,

Setting up local https with IIS in 10 minutes

For very good reasons websites now nearly always run under https rather than http. As dev’s though this gives us a complication of either removing any local redirect to https rules and “hoping” things work ok when we get to a server, or setting local IIS up to have an https binding.

Having https setup locally is obviously a lot more favourable and what has traditionally been done is to create a self signed certificate however while this works as far as IIS is concerned, it still leaves an annoying browser warning as the browser will recognise it as un-secure. This can then create additional problems in client side code when certain things will hit the error when calling an api.

mkcert

The solution is to have a certificate added to your trusted root certificates rather than a self signed one. Fortunately there is a tool called mkcert that makes the process a lot simpler to do.

https://github.com/FiloSottile/mkcert#windows

Create a local cert step by step

1. If you haven’t already. Install chocolatey ( https://chocolatey.org/install ). Chocolatey is a package manager for windows which makes it super simple to install applications. The name is inspired from NuGet. i.e. Chocolatey Nuget

2. Install mkcert, to do this from a admin command window run

choco install mkcert

3. Create a local certificate authority (ca)

mkcert -install

4. Create a certificate

mkcert -pkcs12 example.com

Remember to change example.com to the domain you would like to create a certificate for.

5. Rename the .p12 file that was created to .pfx (this is what IIS requires). The certificate will now be created in the folder you have the command window open at.

You can now import the certificate into IIS as normal. When asked for a password this have been set to changeit

Increasing the Maximum file size for Web.Config

Web-Config-Exceeds-Max-File-Size

This can happen in any ASP.NET Web Application, but as Sitecore 8’s default web.config file is now¬†246 kb this makes it extremely susceptible to exceeding the default 250 kb limit.

To change the size limit you need to modify/create the following registry keys:

HKLM\SOFTWARE\Microsoft\InetStp\Configuration\MaxWebConfigFileSizeInKB  (REG_DWORD)

On 64-bit machines you may also have to update the following as well

HKLM\SOFTWARE\Wow6432Node\Microsoft\InetStp\Configuration\MaxWebConfigFileSizeInKB (REG_DWORD)

You will probably find that these keys need to be created, rather than just being updated. After changing them you will also need to reset IIS.

Alternatively

Alternatively you can leave the default values at 250 kb and split the web.config files into separate files.

More information on doing this can be found here:

http://www.davidturvey.com/blog/index.php/2009/10/how-to-split-the-web-config-into-mutliple-files/

My personal preference for Sitecore projects is to update the the max file size as this allows keeping the web.config file as close to the default install as possible. The benefit of doing this is it makes upgrades easier, rather than needing to know why your web.config doesn’t match the installation instructions.

Setting IP restrictions in IIS

It’s a frequent scenario that a website your in the process of building needs to be accessible over the internet before it should actually be publicly available over the internet. This can come in the form of clients needing to review staging sites before there live, test sites needing to be accessible to testers who may not be in a location that can access private servers, or working jointly with other suppliers.

This scenario presents a lot of dangers such as, the URL of a site could get leaked early ruining a marketing strategy, or the site could end up in Google destroying the SEO value on the clients current site and even worse, actually get real customers visiting it.

There are only 2 real methods of protecting test/staging sites. One is adding authentication to the site restricting access to people with a valid username and password. The other is IP white-listing so only people from a valid IP can access the site.

In the past I’ve seen people suggest using a robots.txt to tell search engines to ignore the site. This is guaranteed to fail, Google will index a site with a robots file saying not to. Your robot’s file may say don’t crawl, but that auto generated Sitemap will be obeyed an the files indexed. There will also come a time the robots file gets copied live de-indexing the live site, or someone forgets the file on staging and the staging site is indexed.

Using IIS to set up IP restrictions

Using IIS to set up IP restrictions is¬†quick and easy, and what’s best about it is you can set it at the server level and not worry about people forgetting to add it to new sites. Better still you can also easily add configuration at a website level to allow certain people to see certain sites rather than the whole box.

Installing the Feature

First you need to make sure you have the feature installed on IIS. To do this on Windows Server 2012:

IP and Domain Restrictions

  1. Go to Server Manager and click “Add roles and features”
  2. Click next to take you from the Before you begin page to Installation Type
  3. Leave Role-based selected and click next
  4. On the Server Selection screen the server your on should be auto selected. Click next
  5. On the Server Roles screen scroll down to “Web Server (IIS)”. IP and Domain Restrictions is located under Web Server (IIS) > Web Server > Security
  6. Click the check box on IP and Domain Restrictions if its not already selected and complete the wizard to install the features.

Configuring IIS

The set up an IP restriction in IIS do the following:

  1. Open IIS and select your server in the left hand treeview. Alternatively if you wanted to add the restrictions to an individual site, select that site.
  2. Within the IIS section you should have an item titled IP Address and Domain RestrictionsIP and Domain Restrictions IIS
  3. The configured IP address will be listed out. To add a new one click the “Add Allow Entry” action on the right.
    IP and Domain Restrictions IIS Setting IPs
  4. This screen allows you to set up allow and deny lists, but the restrictions don’t actually have an effect until you edit the feature settings.
    IP and Domain Restrictions IIS Feature Settings
  5. On this screen you need to set the access for unspecified clients to deny. You can also specify a deny action type which alters the status code between unauthorized, forbidden, not found and abort.

What this doesn’t do

What this won’t do is block all traffic not in the allow list to your server. It will only cover IIS, so if you have other services running on your box like SQL Server, Mongo, Apache etc this will all still be publicly available.

IIS Where are my log files?

This is one of those things that once you know is very very simple, but finding out can be very very annoying.

IIS by default will store a log file for each site that it runs. This gives you valuable details on each request to the site that can help when errors are being reported by users.

When you go searching for them your initial thought may be to go to IIS and look at the site you want the files for. There you will see an item called logging. Excellent you think, this will tell you all you need to know.

IIS Log Files

There’s even a button saying “View Log File…”, but once you click it you realise things aren’t so simple. The link and folder path on the logging page both take you to a folder, containing more folders. In those folders are the logs, but there’s a folder for each site in IIS and they’ve all got a weird name. How do you know which folder has the log files for the site you want?

IIS Log Files Folders

Back on the IIS logging screen there’s nothing to say which folder the files will be in. There isn’t any indication anywhere.

The answer however is very easy. Each folder has the Site ID in its name. You can find the Site ID for your site in IIS either by looking at the sites list

IIS Sites List

or clicking on a site and clicking advanced settings

IIS Advanced Settings