.Net Web App Configuration with Parameters – Moving beyond web.config files

For any web application, you’ll typically want to configure your application for the different local, Dev, Test and Production environments. And you want all your building and deployment to be handled by your build server, which means anything you do needs to run using MsBuild, Powershell, command-line .exes, or whatever nice wrappers your build server provides.

Web.Config files

When you first start building .Net web applications, you probably use web.configs. And then when you need more than the out-of-the-box “debug” and “release” configurations, you resort to adding more build configurations and web.config transforms. And these are fine when you’re selecting a configuration to build and run locally, or publishing from Visual Studio, but this doesn’t scale beyond a quick experimental project or hackathon to an actual production site.

So if you want “test” and “production” configs, the temptation is to use web.config files and transforms, where local dev uses the “debug” configuration, and everything else uses an optimised “release” configuration. Actually adding another configuration involves first adding a build configuration, then adding the new web.config transforms. If you’ve ever done web publishing through Visual Studio, you’ll probably have spent a lot of time setting up build configurations for each environment (that should be a warning), then creating the web.config transforms so you can use them when publishing.

However, web.configs have a couple of issues:

  1. They hold different configurations for different purposes – your application “build” configuration for debug/release configurations, and your environment “deployment” configuration (local, test, production).
  2. Web.configs are designed to be used in a “configure, build, deploy” process, and for a “build once, deploy often” continuous delivery pipeline, we want to “build, deploy, configure”.

Whether your branching-and-deployment strategy is “single trunk” (build package once, deploy to each environment) or “branch-based” (rebuild from a known code snapshot/revision and configure before each deployment), it pays to start thinking of “build” and “deployment” configurations as separate.

You can cheat (I did at first) and run all your web.config transforms at build-time, bundle them all up into your release package, and ship the correct config at deployment time (by configuring your deployment, or by deploying a generic package followed by a specific config file). My original web.config approach is covered in this epic post.

Web.config files and the Continuous Deployment Pipeline

It makes sense to have developers configure your core application “build” configuration at build time, but you don’t necessarily want to restrict the “business preference” or environment-specific configurations to requiring a complete application rebuild as you make a code change to inject those changes into the start of the continuous deployment pipeline. Having configuration settings that can be changed outside of the commit, build, deploy process is really useful – you should still manage these configuration values in source control or other change management system.

Using SetParameter.xml Files

So instead of putting our environment config values in web.config files, we’re going to put them in a separate file – the SetParameters.xml file. This makes things cleaner, and is also necessary when using certain deployment tools that only work on whole packages not individual files (eg. Azure Powershell cmdlets).

We need a web app that we can build into a deployment package, and somewhere we can deploy to. I’m using a new Azure web app, but parameters files also work with IIS.

Creating the Deployment Package

First we need an application that can be built into a deployable web package – skip this section if you already have a project you want to deploy.

As a demo, I set up a new basic MVC web app (just add a single HomeController and Index view). I also create appSettings entries for “EnvironmentName” and “EnvironmentDescription”, so I have some values to set. I can read these from the ConfigurationManager (eg. ConfigurationManager.AppSettings[“EnvironmentName”]) and display on my view. The plan is that I have different parameter files for my dev, test and live environments, and when I deploy with the appropriate configuration, an on-screen message shows that the appropriate configuration has been applied. Web.config transform files already exist for the debug and release build configurations (we could add more), but we’re not going to use these for configuring our environments.

Now I’m going to publish my site to a deployment package, so I can later have a two-stage build process:

  1. Build into a deployment (zip) package
  2. Deploy to dev/test/live environment and apply appropriate configuration

So in Visual Studio, go to build->publish, select profile type “custom” and name it “DeploymentPackage”. Connection method is “Web Deploy Package”, select a location (eg. “obj/appName”) and site name. . Go to solution explorer, look in yourAppName/Properties/PublishProfiles and see the newly-created publishing profile
(you can now edit the publish profile and change these values if you want).

Build the application with

msbuild appName.csproj /t:Build /p:DeployOnBuild=True;PublishProfile=DeploymentPackage;ProfileTransformWebConfigEnabled=False;Configuration=Release

Or just run the publish from Visual Studio.

This builds your site (in the “Release” configuration), creates your deployment package zip and a couple of extra files – a manifest, .cmd, readm, and a SetParameters.xml file. The parameters file is really basic:

<?xml version="1.0" encoding="utf-8"?>
 <setParameter name="IIS Web Application Name" value="MyWebAppName" />

But we can expand this with our own settings.

Deploying the Web App

OK, so I’m going to deploy this package to an Azure site.

Using Powershell, the syntax is:

Publish-AzureWebsiteProject -Name appname -Package myapp.zip -SetParametersFile appname.SetParameters.xml

For MsDeploy, the syntax looks like this:

msdeploy.exe -source:package="appname.zip"
-verb:sync -setParam:name="IIS Web Application Name",value="appname" -disableLink:AppPoolExtension 
-disableLink:ContentExtension -disableLink:CertificateExtension -retryAttempts=2 -verbose 
-userAgent="VS12.0:PublishDialog:WTE12.3.51016.0" -setParamFile:"appname.SetParameters.xml"

When I run this, I find that this hasn’t actually done anything to override the settings with the values in the SetParameters file, because I didn’t set the parameter value. Also, I have to tell our app which parameters actually exist.

So go back to Visual Studio and add a “parameters.xml” file to the root of your web project. It should look like this:

<?xml version="1.0" encoding="utf-8" ?>
 <parameter name="EnvironmentName"
 description="blah blah blah"
 <parameterEntry kind="XmlFile" scope="Web.config"
 match="/configuration/appSettings/add[@key='EnvironmentName']/@value" />

This creates a named parameter with a default value, and specifies how this should affect the web.config at deployment time. Rebuild and re-package the application, and the generated “SetParameters.xml” file now has a “setParameter” entry for “EnvironmentName”. Awesome. You can go edit that file, change the value, and run
MsDeploy / Publish-AzureWebsiteProject again to deploy the changes (and only the changes get shipped). This is basically doing a web.config transform at deployment time. And of course, you can expand your parameters.xml file for more settings. Update parameters.xml, rebuild the package, and you’ll see new entries in SetParameters.xml.

If you only want to change a single setting, you can do that with parameters added to your MsDeploy call. In fact, you could have a load of settings in a parameters.xml file, and then apply those as a base while overriding selected settings at deploy time. This might prove useful if you have to deploy multiple copies of your app to production, yet tag each one differently (eg. for A/B testing, “developer” API deployments, etc). Anyway, just add this to the end of your MsDeploy call:


This is obviously a really simple example, just changing appSettings. You can also use parameterization to set connection strings and other configuration settings. This post details the parameters syntax: http://vishaljoshi.blogspot.co.uk/2010/07/web-deploy-parameterization-in-action.html

Configuring for Different Environments

So far, all we have is a single appname.SetParameters.xml file, and it gets generated at build time, so we don’t want to be editing that. What we actually need is a SetParameters.xml file for each environment, and we need to make sure their values survive rebuilding the project. So you can either create additional SetParameters.xml files within your project and include them at build time (basically the equivalent of web.config transforms), or hold your SetParameters.xml files somewhere else, and just consume them at deploy time.

Note that if you do just include the parameters.xml files in your code, it might seem like you’ve gained nothing over hacky web.config transforms – you’re still configuring at commit time and shipping all the possible configurations to use at deployment time. However, you have de-coupled the code configuation (debug/release build config) from the environment configuration, and removed the need to have all those near-duplicate build configurations cluttering up your solution just to enable web.config transforms.


So now we have a scripted process to

  1. Build our application into a deployment package, including a specification for how the application can be configured
  2. Deploy our application to our chosen environment, applying the necessary configuration at deployment time

This allows you to implement a continuous delivery pipeline with whatever build/deployment server you use, and have “build once, deploy repeatedly” behaviour at each dev/test/live stage of your pipeline.

Gotcha: Don’t Deploy All The Parameters!

You’ll want to have the various parameters available at deployment time – read on for notes on including them with your build artifact so you have a “deployable zip package plus a set of parameter config files” – but you don’t actually want them included inside the “site.zip” package, because then they’ll end up getting deployed, and you don’t want people being able to go to http://www.yourawesomesite.com/parameters.test.xml and seeing all your secrets.

So the answer is to omit them from the build, either using the file properties, to set “Build Action” to be “none” rather than “content”, and “Copy to output directory” to “do not copy” on individual files, or by using some scripting (eg. MsBuild within your publish profile) to decide what ends up in the deployment package.

It’s best not to rely on humans setting the build properties and ensure that your build process omits the parameters (or have a CI/post-build check inspecting the deployment package for contraband).

You can add the following entry to your “DeploymentPackage.pubxml” publish profile to omit them:


This is used by MsBuild when generating the package:

msbuild WebApp.csproj /p:DeployOnBuild=True;PublishProfile=DeploymentPackage.pubxml

When the deployment package is built this should exclude the matching parameter files.

Summary – what you should have is:

  1. Access to the appropriate “parameters-environment.xml” file at deployment time (either a release artifact that contains your site.zip and parameter files, or a separate repository of configuration files)
  2. The “parameters.xml” template in your “site.zip” deployment package
  3. Absolutely no “parameters-env.xml” files in the actual “site.zip” deployment package (expand the zip archive and navigate down to the “PackageTmp” folder to verify)

Bonus: Shipping Environment Configurations with the Deployment Package

One approach to including SetParameters files within the codebase might look like this.

Add a set of “parameters.dev.xml”, “parameters.test.xml”, “parameters.live.xml” files to your solution, clone them from the generated SetParameters.xml file eg:

<?xml version="1.0" encoding="utf-8"?>
 <setParameter name="IIS Web Application Name" value="MyAppName" />
 <setParameter name="EnvironmentName" value="Test" />
 <setParameter name="EnvironmentDescription" value="Testing" />

Now I need to handle building the package and copying of all the necessary release files into a “BuildArtifacts” folder. This could be achieved in different ways:

  • Set the parameters.*.xml properties to “copy to output directory=copy always”
  • Use an MsBuild or other scripted step to copy them into your final “artifacts” folder

For the MsBuild approach, I have a “WebPackage.msbuild” file in my solution with the following fragment:

<Target Name="BuildPackage">
 <Exec Command='$(MSBuildExe) $(WebApp)\$(WebApp).csproj /t:Build /p:VisualStudioVersion=11.0 
 <CallTarget Targets="_CopyPackageToDeploymentDirectory"/>
 <CallTarget Targets="_CopyParametersToDeploymentDirectory"/>

<Target Name="_CopyPackageToDeploymentDirectory">
 <PackageFiles Include="$(PackageAssemblyDir)\**"/>
 <Copy SourceFiles="@(PackageFiles)" DestinationFolder="$(BuildArtifactsDir)\$(WebApp)\%(RecursiveDir)"></Copy>

<Target Name="_CopyParametersToDeploymentDirectory">
 <ParameterFiles Include="$(WebApp)\parameters.*.xml"/>
 <Copy SourceFiles="@(ParameterFiles)" DestinationFolder="$(BuildArtifactsDir)\$(WebApp)"></Copy>

This is part of a “WebPackage.msbuild” script within my solution, as long as I have all the properties defined (in a PropertyGroup within the MsBuild script) I can just call

msbuild WebPackage.msbuild /t:BuildPackage

to generate a nice “BuildArtifacts” folder with everything I need to deploy my app.


Scripting Azure Activity and Authenticating with Publish Profiles

If you want to do anything with Azure, the tools Microsoft provide for humans – Visual Studio and the Azure Portal – are great. Unfortunately, if you want to automate any of your Azure activity – for example, deploying applications – then things get a bit tricky. Some Azure commands are available as simple command-line tools (eg. MsDeploy), but the preferred (and sometimes only) way is to use Powershell and the Azure cmdlets. And
with Powershell comes a few tricky authentication issues.

A warning: what I’m about to demonstrate is an approach, possibly not the best approach. I’m still trying to figure this stuff out, and this may not be the most secure method of doing things.

So the problem is this: My real-world deployment pipeline currently has automated Azure deployments via msdeploy.exe (all sorts of hassles and risks with the authentication credentials and the scripts are ugly, but it works). But as we want to use deployment slots for “zero-downtime deployments” (ship to staging slot, instantly switch slots to live), we need a smarter mechanism than manually swapping slots via the Portal. Swapping deployment slots is a one-liner in Powershell (“Set-AzureWebsite -Slot”), but as a proof of concept, I’m just going to get my script to connect to my Azure account and bring back some info. Then we can do some real damage and actually deploy code…

Setting Up Azure Powershell

So you’ll need to install Powershell and the Azure cmdlets. I had to upgrade to Powershell 4 to get this to work (also, I needed to re-install Azure Powershell after upgrading Powershell).

Fire up Powershell (you may also have an “Azure Powershell” shortcut available).

Check your Powershell version with this command:


Get Powershell from http://social.technet.microsoft.com/wiki/contents/articles/21016.how-to-install-windows-powershell-4-0.aspx

Get the web Platform Installer and use it to install Azure Powershell –

You can run a test Azure command, for example:


Doesn’t matter if you get an error about your credentials, at this point only worry if you’re getting instant errors about unrecognised commands because you haven’t installed everything.

The Manual Process

So you fire up an Azure Powershell console session, and start doing stuff. And of course, you have to supply authentication credentials. So do


and log in to your Microsoft account at the prompt. Once logged in, you can run some commands. To see your details:


To see all your webapps:


To “log out” – either:

Remove-AzureAccount -Name yourname@somewhere.com



So all of this is great if you’re a human, not so good if you’re a robot, because “login prompts”. We need a better way to authenticate.

Publish profiles

If you’ve used MsDeploy to ship to Azure through either Visual Studio or the msdeploy.exe, you’ll have used publish profiles. These are per-application files that Azure provide containing authentication details (go to the portal and download the publish profile for each web app). However, you can’t really use these with Azure cmdlets, they’re “per-application”, which is an issue if you want to do something like swap between staging and live slots (actually, two apps with two publish profiles).

Thankfully, there’s another “account” publish profile you can use. Go to your Powershell console, make sure you’re logged in, and do:


There’s probably also a link to download this file on the portal somewhere, but I’ve yet to see it. Anyway, you should get a simple XML file called “SubscriptionName-date-credentials.publishsettings”. Save this somewhere safe.

Now you can do

Import-AzurePublishSettingsFile [.publishsettings file path]

And this imports the certificate, essentially “logging you in” as the trusted deployment user specified in the publish settings. Go run some “Get-AzureSubscription” and “Get-AzureWebsite” calls.

Scripting Azure Activity

You just did Azure stuff without having to deal with login prompts. Great. Now you can script all that up.

Create a simple script (text file) called “Connect.ps1” that contains this:


#Connect to account
Import-AzurePublishSettingsFile -PublishSettingsFile $publishSettingsPath

#Show account status
echo "run Get-AzureSubscription"
echo "run Get-AzureAccount"

#List all websites
echo "run Get-AzureWebsite"

You can run this from a standard command prompt, with this call:

powershell -File Connect.ps1 C:\pathTo\myAzure.publishsettings

(The script takes a publish settings file path as an argument).

Adding Azure Powershell to an Automated Build Process

If you can script your Azure activity – and we just did – then you can add that to a build process. As a quick test, I dumped that Connect.ps1 script into an existing project and created a simple Team City build config to run it, so whenever I commit project changes to Git, I can have Team City connect to Azure.

All I had to do was create a Team City build step with the following configuration.

Runner type: Powershell
Powershell run mode: version 4
Script: file (with path to .ps1 relative to build agent checkout/working directory)
Script execution mode: external file
Script arguments: C:\pathTo\myAzure.publishsettings

Run this build and go look in the build logs, and you should see the script output, same as when you ran it from a command prompt.

For reference – Team City’s PowerShell arguments are [-Version, 4.0, -NonInteractive, -ExecutionPolicy, ByPass,
-File, \TeamCity\buildAgent\work\etc\PowershellScripts\Connect.ps1, \AzurePublishProfiles\my-


So we now have scripts accessing our Azure account and running commands without hitting login prompts. This allows for automated deployments to Azure, including all the activities that were previously only available when using the portal. However, there’s a few security issues that you’ll likely want to address.

Additional Notes

Security Issues and “logging out”

The recommended advice seems to be to remove all accounts before importing the publish settings file, to prevent confusion over which Azure account is used by the Powershell session. Given that you’re logging in with a text file instead of a password, it’s good security practice to delete the publish settings file once you’ve set up access. However, Azure sessions have a tendency to time-out, so you probably can’t just do a one-time download of this file to set up a build machine then delete it – you’re going to need access to this file whenever you want to run an automated deployment (hence my adding the import command to the powershell script). I’m still working on this one – I suspect that Azure Resource Manager is ultimately the way to go.

Outstanding security issues:

  1. Where to keep the publish settings file? For security, source control would be a bad move, as would a network share. Probably storing a copy on the (locked down) build machine is best, but for a distributed build setup (eg. Team City with multiple agents) your build configuration needs to look in a consistent place on each agent. Getting around importing a publish settings file would be the ultimate aim.
  2. How to “log out” at the end of the session? Remove-AzureAccount seems to want an account name you don’t have, but it can apparently be “smart” about choosing the correct account to remove. Remove-AzureSubscription would work. Also, these commands don’t “remove” anything from your Azure account, they just kill the cached authentication credentials for your Powershell session.
  3. Can I rename the publish settings file? Yes. No complaints when I did just that.
  4. I’m paranoid. How do I revoke and reset my publishing credentials? Um, TBC. You might be able to use the “Reset deployment credentials” option in the portal.

Links for more info

I borrowed various bits of info from various sources.

Microsoft’s Azure cmdlet reference: https://msdn.microsoft.com/library/azure/jj554330.aspx

Resetting deployment credentials (in any webapp “publish your app” section): https://blogs.msdn.microsoft.com/kaushal/2013/05/29/windows-azure-web-sites-reset-your-deployment-credentials-vs-reset-your-publish-profile-credentials/

Cached credentials and removing accounts: http://itproguru.com/expert/2015/03/how-to-remove-azure-accounts-cached-credentials-from-powershell-remove-azureaccount-for-all-accounts-step-by-step/

Azure Web App Deployment With MsDeploy and Powershell

For my first attempt at automating an Azure deployment process, I had some MsBuild wrap the calls to the MsDeploy.exe. This lets me have the build server (eg. Team City) run the MsBuild script and call a simple “DeployX” target for the relevant Dev/Test/Production deployment. The MsBuild script is responsible for supplying MsDeploy with the correct settings. Problem is, the MsDeploy call passes Azure credentials (for the web app, not a user) as clear-text command-line parameters.

If we have those publish settings stored in the MsBuild script, we have to copy those parameter values from the Azure publish profile into a script (the build script) that lives in source control. Not so secure, and it involves some tedious copy-and-pasting.

While your application should know “how to deploy itself”, a big problem with locking your publishing credentials into your deployment packages is what happens if those settings should change. If you have a continuous delivery pipeline (you should), and if code takes a few days to travel through the pipeline from check-in to deployment, any code in the pipeline with outdated publish settings is hard to deploy (you may need to just grab the deployment packages and modify them). It would be better if the credentials lived outside of the build pipeline. You could have your build server pass these in via parameters, but that only works if you’re deploying one or two apps (I’m currently working on a project that deploys a set of eight apps together, whether that’s the correct long-term strategy is another question).

So a first step would be, rather than storing the Azure credentials in a script, just include the publish profile files and read them at deployment time. Then you can move them out of your code (source control and deployment packages) and store them on disk or in a different repository – anywhere that every developer or build server agent machine can access – and read the appropriate profile at deployment time.

You could do this with a custom application, script or MsBuild target that reads and parses the publish profile Xml file. Or just use some Powershell, it has some built-in XML hacking cpabilities. Instead of knowing the publish settings for the MsDeploy call, our MsBuild script can run a Powershell script instead.

Note that this isn’t the only way to deploy with Powershell. In fact, there’s probably better ways to use Powershell to deploy directly as a trusted Azure user, rather than all the hassle of downloading publish profiles. All we’re doing is wrapping the MsDeploy calls that actually ship our code.

A Powershell Script That Deploys Webapps

So create a Powershell script like this (yes the funny quotes to escape characters are weird but necesssary):

#Usage: powershell .\deployWebApp.ps1 [pathToWebAppPackageFolder] [webAppPackageZipFile] [pathToPublishSettingsFile] [configuration]

#MsDeploy.exe location (if not in path)
[string]$msDeployExe="C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe"

#Get publish settings from file

#MsDeploy "white label" webapp package (additional configs to follow)
$msDeployPackageCommand = "`"$msDeployExe`" -source:package=`"$packageFolderPath\$packageZip`" 
-verb:sync -setParam:name=`"IIS Web Application Name`",value=`"$azureSite`" -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -retryAttempts=2 
-verbose -userAgent=`"VS12.0:PublishDialog:WTE12.3.51016.0`"";

Write-Output ("MsDeploy Command: " + $msDeployPackageCommand);
cmd.exe /c "$msDeployPackageCommand";

#MsDeploy config file "web.configuration.config" eg. "web.TEST.config"
$msDeployConfigCommand = "`"$msDeployExe`" -source:contentPath=`"$packageFolderPath\Configs\Web.$configuration.config`" 
-verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -retryAttempts=2 
-verbose -userAgent=`"VS12.0:PublishDialog:WTE12.3.51016.0`"";

Write-Output ("MsDeploy Command: " + $msDeployConfigCommand);
cmd.exe /c "$msDeployConfigCommand";

For the configs, I run web.config transforms on all my configs during the CI/packaging build stages, and ship all the possible configs with the deployment package. While the above example shows shipping full web packages and individual files, a better approach is to use parameter files for configuration, adding an argument like:


OK, try running the script with:

powershell powershellScripts\deployWebApp.ps1 BuildArtifacts\DeployableWebApp DeployableWebApp.zip C:\Development\AzurePublishProfiles\myappname.azurewebsites.net.PublishSettings Debug

This works, so we go and add this to our MsBuild script with this target:

<Target Name="PsDeployToDev">
 <Exec Command="$(PowerShellExe) powershellScripts\deployWebApp.ps1 $(PackageDeploymentPath)$(WebApp) $(WebApp).zip $(AzurePublishProfilesPath)\myappname.azurewebsites.net.PublishSettings Debug"/>

Run it with:

msbuild WebDeploy.msbuild /t:PsDeployToDev

…and it fails, because of an execution policy issue. See http://go.microsoft.com/fwlink/?LinkId=135170

Yes, within this command prompt, I have the rights to run a powershell script, but not to run a script that runs my powershell script. Go Microsoft! Let’s add a few parameters to our MsBuild Powershell call to handle the execution policy:

<Target Name="PsDeployToDev">
 <Exec Command="$(PowerShellExe) -NonInteractive -ExecutionPolicy Unrestricted -File powershellScripts\deployWebApp.ps1 $(PackageDeploymentPath)$(WebApp) $(WebApp).zip $(AzurePublishProfilesPath)\myappname.azurewebsites.net.PublishSettings Debug"/>

Success! You can test this by editing, re-building and re-deploying your web app.

I have this running as a Team City build step (MsBuild runner calls the appropriate target in my MsBuild script).

This is a starting point – a basic “ship a white-label package and a config file”, you’ll need to adapt it for deploying your applications.

Some next steps:

  • You’ll probably want to turn off verbose logging and outputting of the actual deployment command (with the publishing credentials arguments) once you’re happy things are working
  • You might need the “msdeploy.exe -useCheckSum” argument to prevent files getting redeployed after your build server does a clean checkout / rebuild