With the introduction of Azure websites, which provide automated deployments from source control systems such as TFS and GitHub, it has become relatively easy to configure automated deployments to Azure, so that you can do a check-in and watching your deployment happen. Most of the online demos that you'll find online recommend that if you're building a web site for deployment and you need to store sensitive values such as storage connection strings, you should put them in your web.config. If you use a source control repository that provides restricted access to your source code, such as TFS, you can happily add these values to your web.config and check them in, and know that they are safe from the eyes of others. However, if you're using a public source control system, such as GitHub, you might not want others to see your sensitive configuration settings.
Azure websites allow you to modify your configuration settings using the Azure Management portal, which partially addresses this problem by letting you ensure your cloud web site has the correct configuration values, and ensures they are secure. However, what about settings that you'd like to have in your web.config locally, as well as in the cloud? For example:
- Service bus connection strings
- Storage connection strings
- OAuth provider secrets and more....
If you add them to your web.config and check them into GitHub, anyone can see (and possibly use) them. BUT if you only add them in by using the Azure Management Portal, they won't be available locally. Surely, there must be a way to have them available in both, right? In this article, I'll present a way to avoid this problem, allowing you to store your secret configuration settings locally, keep them out of source control, and also use the correct settings in the cloud.
NOTE: If you're using Node.js, I found a really good tutorial which addresses the same problem here.
Source Code Download
All source code for this article is available here. I've also uploaded a zip file of the solution for those who can't access GitHub on CodeProject.
There are a couple of methods I've seen thus far for keeping config secrets out of GitHub. The first involves:
- Adding a configuration file with your secret values in it
- Checking it in
- Using git update-index --assume-unchanged command to tell Git to ignore changes to this file in future, and adding it to your .gitignore file
- Making changes to this file which will not be checked in
Whilst this works if you only have a single branch, as soon as you change branches, this will break as Git detects changes in your config file, and you'll have to either undo them, or check them in.
Another option is to use pre and post-commit hooks to automatically add/remove your secret config values when checking in and out, as described here. Unfortunately, the solution provided uses python script, which we can't use automatically from within a standard Visual Studio ASP.NET solution.
We want a method that is easy to implement, will work well for ASP.NET web projects (Both MVC and ASP.NET webpages), and integrate with Azure and GitHub. And that's exactly what I'll provide.
To give you a full explanation of how this technique works, we first need to setup a website and deploy it to Azure from GitHub. To do so, we need to:
- Create an example ASP.NET website in Visual Studio 2012 using the MVC 4 internet application template
- Configure source control with Git, and check our site into GitHub so the source code is publicly available for all to see
- Sign up for an Azure trial account
- Create a website
- Setup automated deployments to Azure from our GitHub repository
Create a web site and set up source control with GitHub
The article here gives a good example of how to setup continuous deployment to Azure from GitHub, and I recommend reading it if you're not familiar with Git or Azure. For our example, I've downloaded Git Explorer for Windows, and created a local repository named
CodeProjectConfigStrategy, as shown below:
Next, I'll open up Visual Studio 2012, and create a new MVC Web Application, ensuring that its
Location is the same folder as my Git repository I created earlier, as shown below:
Next up, I'll exclude NuGet packages, as described in this article. I also added an entry to my .gitignore file to exclude the whole /packages directory from source control. My solution now looks similar to the following:
Finally, I'll go back to GitHub Explorer, commit my changes, and press the Sync button so my changes are pushed to GitHub. Now I need to create a site in Azure and deploy to it.
Creating the site in Azure and setting up automated deployments
As mentioned earlier, the article here provides a good example of how to setup automated deployments from GitHub, in the section titled "Deploy files from a repository web site like BitBucket, CodePlex, Dropbox, GitHub, or Mercurial." If you're following along, I'd recommend reading it so you're familiar with how to integrate your Azure website with GitHub for automated deployments.
In my case, I:
- Signed up for an Azure free trial and logged into the Azure Management Portal
- Created a website named codeprojectconfigstrategy (http://codeprojectconfigstrategy.azurewebsites.net/)
- Configured the site so that it was automatically deployed from the GitHub repository I created earlier
The end result is the site as follows, which is nothing special:
Keeping our configuration secrets safe
We now have a vanilla MVC web app. Imagine if we wanted to add something which required the use of a configuration value, for example a connection string for Azure service bus, or perhaps an API key for OAuthn using Facebook? This could apply to any situation in which you need to store sensitive values in your web.config file, but I'll use the Azure service bus connection string as an example. How will we go about this?
For demonstration purposes, you might have code to access the service bus connection string such as this:
string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
And in your web.config, you might put the storage connection string for your Azure service bus as below:
<add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://exampleservicebus.servicebus.windows.net/;SharedSecretIssuer=owner;SharedSecretValue=longlongsecretguid" />
Now this will work fine when debugging locally, and also if we check in and deploy it to Azure, as our cloud instance will be able to access the connection string in the web.config. But if we check this file in now, others who access our GitHub repository can see and use our connection string. So, how do we stop this?
Moving sensitive config settings to a separate file
First, we'll add two new files to our solution, one named HiddenSettings.config, and another called HiddenSettings.template.config. Both of these should have their build action set to Content, just like the web.config file does. This means they'll be deployed automatically when the site is published.
Next, we'll update our web.config file to use the values in our HiddenSettings.config file, by updating the
AppSettings element as follows (If you're not familiar with how this works, see this link):
Next, we open our HiddenSettings.template.config file and add the following contents:
We then open our HiddenSettings.config file and add entries for all of the sensitive configuration settings that we want to override. In our case, we only have one, as follows:
Next, we go back to our web.config file and replace our sensitive value with a dummy value, to indicate to anyone else using our solution that this value needs to be updated with their own chosen value.
<add key="Microsoft.ServiceBus.ConnectionString" value="ThisWillBeOverriddenAtRuntimeByYourSecretValue" />
Finally, we open up our .gitignore file in the solution root directory, and add an entry for our HiddenSettings.config file so it doesn't get checked into source control. E.g.
Add a post-build event so that our HiddenSettings.config file will be created automatically on the build server
At this stage, we've moved our sensitive values into a separate configuration file, and added an entry to .gitignore to ensure they don't get into source control. However, if we check this in, the deployment to Azure will fail, as when performing a publish on the build server, the publishing process won't be able to find the HiddenSettings.config file, as it doesn't exist since it was never checked in. That's where our empty HiddenSettings.template.config file comes in! What we'll do is configure a post-build event, so that if the build process can't find the HiddenSettings.config file (e.g. if it's on the build server) then it should create it automatically, and populate it with the contents of the HiddenSettings.template.config file.
To do this, we need to:
- Open our MvcApplication1.csproj file in Notepad or another text editor
- Find the AfterBuild element (commented out by default)
- Uncomment it, and replace it with the following code:
As you can see, this looks for the HiddenSettings.config file, and if it doesn't exist, simply creates it and copies the contents of HiddenSettings.template.config into it.
Checking in and updating our settings in Azure
We can now check our project into GitHub, safe in the knowledge that our secrets are safe. However, we need to ensure that those settings are available to our app when deployed to Azure. Azure web sites allow you to override configuration values in the web.config file with values you provide in the management portal. To do this, I:
- Go the management portal
- Open the codeprojectconfigstategy website and click on the Configure tab
- Navigate to the App Settings section, and add an entry for Microsoft.ServiceBus.ConnectionString, with a value that points to our production service bus, as shown below:
Now our Azure site will have the correct value, as will our local site, and none of our sensitive data is available for others to see in GitHub! Brilliant!
Using the Code
If you'd like to see the solution, all of the source code is available on GitHub here.
Feel free to download and browse the code as you wish. Please also let me know if you have any comments or ideas on how I could improve this solution.
Points of Interest
Whilst this isn't the only solution to the initial problem, I feel like it makes best use of the built-in features that already come with ASP.NET and Azure websites, such as:
Finally, if you found this article useful, be sure to check out my main CodeProject article - http://www.codeproject.com/Articles/584534/YouConf-Your-Live-Online-Conferencing-Tool, which contains a multitude of tips like this to help you build a rock-solid Azure website.
- Storing configuration settings in a separate file
- Utilizing the post-build process
- Automated source control with GitHub
- Updating app settings in the Azure Management Portal
This was just one of the many discoveries I made during the Azure Developer Challenge.
Thanks for reading and I hope you've learned something useful!
- 2013/06/04 - Initial article
- 2013/06/09 - Added source code download in addition to GitHub link