Applying some DevOps Rigour to Azure Websites’ AppSettings


Summary: Configuring Azure Websites Application Settings in the portal is fine for quick and dirty prototypes, but for anything serious you need to apply some DevOps rigour. This post describes how you can store your Application Settings into a per-environment JSON file that can be checked into source control and automatically applied to your website during or after deployment.

Azure Websites include the ability for administrators to configure application settings and connection strings without needing to redeploy any code or config files. If you’re unfamiliar (or only vaguely familiar) with this feature, I suggest you detour with a quick read of Windows Azure Web Sites: How Application Strings and Connection Strings Work, as there is more to this feature than you might expect. In particular, ASP.NET developers are often confused by the fact that the Azure Websites application settings are completely distinct from the appSettings stored in web.config, yet the values from the Azure settings are injected at runtime into the .NET configuration system, taking precedence over whatever is in web.config. This means you do not need to use any new APIs (in contrast to what you do with Azure Cloud Services) to take advantage of this configuration management capability.

When you first deploy an Azure Website, you won’t have any custom Azure application settings defined, even if the application does have appSettings defined in web.config. Although it is possible to add settings at any time (with the supplied values replacing whatever is baked into web.config), it is better to plan in advance which settings should be configurable by administrators post-deployment by pre-populating settings and their default values. This makes it obvious to administrators what settings can be changed and what their current values are.

Entering each application setting manually through the Azure portal is going to be time consuming and error-prone. So instead, let’s apply some good DevOps processes whereby we can:

  • Define the correct application settings that should be deployed per-environment
  • Persist these settings in source control or a configuration management system
  • Apply the correct settings whenever we deploy a new website version or provision a new environment
  • Apply updated settings to an existing deployment

Note: Azure’s capabilities and APIs change rapidly. This information is current as of December 2014. If you’re reading this in the distant future, you may want to check with other sources on current capabilities and best practices.

Storing your App Settings

There is no one “right” place to persist our application settings, and the best solution for you will likely depend on your choice of tools and your existing build and operations processes. In order to keep this solution simple and flexible, I’m going to piggy-back off some of the PowerShell scripts that are automatically added when you create new websites in Visual Studio 2013. If this isn’t the right approach for you, you should be able to cherry-pick useful things from this post and integrate them with other approaches.

In your new website project, you should see a PublishScripts folder with 3 files in it:

image

  • A .json file which contains basic configuration information for a deploying your website to a specific environment
  • AzureWebsitePublishModule.psm1, which has helper functions for Azure deployment
  • Publish-WebApplicationWebsite.ps1, which controls the deployment process and is designed to be customised.

Note that the “out of the box” Publish-WebApplicationWebsite.ps1 script will only deploy a website that has already been packaged into a Web Deploy Package (either using Visual Studio or as a part of an automated build). There are some notes towards the end of the script documentation which show how to modify the script to package the website as a part of the script. This is often a good thing to do, but isn’t required for anything described in this post.

Now if you open up the default JSON file, it will look something like this:

{
    "environmentSettings": {
        "webSite": {
            "name": "TomsWebApplication16",
            "location": "Australia East"
        },
        "databases": [
            {
                "connectionStringName": "",
                "databaseName": "",
                "serverName": "",
                "user": "",
                "password": "",
                "edition": "",
                "size": "",
                "collation": ""
            }
        ]
    }
}

So let’s extend this to include support for application settings:

{
    "environmentSettings": {
        "webSite": {
            "name": "TomsWebApplication16",
            "location": "Australia East",
            "appSettings": {
                "Foo": "Bar",
                "Baz": "Blah"
            }
        },
        "databases": [
            {
                "connectionStringName": "",
                "databaseName": "",
                "serverName": "",
                "user": "",
                "password": "",
                "edition": "",
                "size": "",
                "collation": ""
            }
        ]
    }
}

The values you put in this file should be the default or current values for a given deployment environment (Dev, Test, UAT, Prod, etc). You can (and should!) create a copy of this file for each environment, and store it somewhere safe such as source code control or a configuration management database.

Applying your App Settings

Now that we have our app settings defined and safely stored, we need a way to apply them to your Azure Website. Again, we’ll piggy-back off the PowerShell scripts provided by Visual Studio, but you could put this into an automated build process or custom scripts.

Here’s a little PowerShell function which is able to parse the above JSON, create a PowerShell array and pass it to the Set-AzureWebsite cmdlet (thanks to Rob Mawston for helping with this part). Paste this code as a new function within the Publish-WebApplicationWebsite.ps1 script.

function Set-WebsiteConfiguration
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateScript({Test-Path $_ -PathType Leaf})]
        [String]
        $ConfigurationFile
    )

    $configJson = Get-Content $ConfigurationFile -Raw | ConvertFrom-Json

    $siteName = $configJson.environmentSettings.webSite.name
    $hasAppSettings =  Test-Member -Object $configJson.environmentSettings.webSite -Member 'appSettings'

    $appSettingsHash=@{}
    if ($hasAppSettings)
    {
        $appSettings = $configJson.environmentSettings.webSite.appSettings
        $names = $appSettings | Get-Member -MemberType properties | Select-Object -ExpandProperty name
        $names | foreach `
            -Begin {$appSettingsHash=@{}} `
            -Process { $appSettingsHash.Add($_,$appSettings.$_) } `
            -End {$appSettingsHash}
    } 

    Set-AzureWebsite $siteName -AppSettings $appSettingsHash 
}

Next, we want to call this whenever we (re)deploy our website. To do this, scroll right to the bottom of Publish-WebApplicationWebsite.ps1 and add a call to our function right before the finally block:

    #Deploy Web Application package if $WebDeployPackage is specified by the user 
    if($WebDeployPackage)
    {
        Publish-AzureWebApplicationToWebsite `
            -Configuration $Config `
            -ConnectionString $newEnvironmentResult.ConnectionString `
            -WebDeployPackage $WebDeployPackage
    }

    #Update the application settings with values from the JSON file
    Set-WebsiteConfiguration $Configuration
}
finally

...

We’re now ready to deploy! As mentioned previously, the default version of the scripts requires that you have a ready-built Web Deploy Package. To create one, go into Visual Studio, right-click the web project and choose Publish. On the Connection page, choose Web Deploy Package as the publish method, and specify a folder. After the publish process is complete, you should find a .zip folder in the location you specified. Then you can publish your script from Azure PowerShell as follows:

.\Publish-WebApplicationWebSite.ps1 -Configuration .\Configurations\WebApplication16-WAWS-dev.json -WebDeployPackage .\WebApplication16.zip -verbose

Your website should now be successfully deployed to Azure and the app settings configured. Now the entire point of putting values into Azure application settings is that administrators may want to change those settings post-deployment. Again you could do this with the Azure portal, but then you risk the situation where the values checked into the JSON file do not match those on the live site. So a better approach is to always treat the JSON file as the source of truth. You can then run the Set-WebsiteConfiguration function on its own to update the settings without redeploying the entire website.

Conclusion

Regardless of your choice of tools, good DevOps means automating as many processes as possible to ensure regular activities are performed quickly, consistently and error-free. Storing your Azure Websites application settings in environment-specific files and applying them using scripts is one step towards this goal.

Comments (4)
  1. Betty says:

    is there an advantage of using this method over the standard webdeploy xml transforms?

  2. Web.config transforms are a good way of customising the web.config files for each environment, and can be used for any config section (not just AppSettings). However this process occurs at build/deploy time, so you need to redeploy the app to get new settings. The approach I've described in this article is specifically geared to making it easy for admins to change specific app settings without needing to redeploy or edit the site contents.

  3. Jarle Nygård says:

    This post misses a huge point; the config for production cannot be stored in source control! It must be protected.

  4. @Jarle: Glossed over perhaps, but not missed. Of course you are right, and the post says "source control our a configuration management database".

Comments are closed.

Skip to main content