Testing Your Unity Configuration when you Build or Deploy .Net Apps

Dependency injection is great, but it does come with a risk that errors with the container configuration don’t show up until runtime (eg. forgetting to specify which implementation should be used for a given interface). In an MVC app, this can mean broken controllers, fatal errors with service classes, and parts of your site/application being unavailable. So a safeguard against this is to check your IOC configuration, either at deployment time, or even better, at build time. Then you can fail the build or back out of a deployment if the various services or controllers can’t be resolved.

To solve this problem on a recent project, I wrote some custom code to do a “get me one of everything” test. So if I have a service requiring other services/repositories, or a controller that uses those services, I know about any missing Unity configuration entries. This post covers .Net, MVC and Unity, but the same approaches should work for other IOC setups.

I’ve listed a couple of different approaches to Unity configuration testing in the following code snippets – applying a simple strategy to a large production codebase threw up a few issues, and prompted some experimentation and refactoring.

Basic Approach

First, I try and resolve everything I can find registered in the Unity container. Then I check that I can get an instance of each controller. This should ensure that all the constructor-injection is tested. Any errors with Unity get collected up and reported (and hopefully other errors are left to a different test). If the checks return no errors we’re good, otherwise we need to go fix the configuration. There are limits to this approach – I’m still trying to figure out a way to deal with individual calls to directly resolve implementations from the container – but checking the listed container contents and all the controllers cuts down on a lot of potential problems.

Running a Unity Check

Once you have a basic Unity checker class setup, you have options for running it. I have an API controller run this, so I can poll it from a command-line app/script on deployment. Our build pipeline does a “deploy to a development environment and run some tests” following a successful CI build, so we know soon after a code commit whether we have a shippable build. Getting the tests running at the local/CI build stage (eg. via unit tests) is even better.

Example Code

The first thing I need is a simple app with Unity setup (your production codebase will no doubt require some tweaks), and some very basic/stupid test interfaces/implementations. And then I can add some Unity configuration. I just grabbed the Unity.Mvc Nuget package, used the out-of-the-box UnityConfig.cs, and edited it to set up a load of config entries eg.

container.RegisterType();

I then made sure I had service implementations and controllers constructed with dependency injection so they get whatever’s configured in Unity as the implementation, eg.

public StuffController(IDoStuffService doStuffService) { ... }

I start by building a basic Unity checking class like this:


public class UnityConfigurationChecker
{
    public static IList GetUnityErrors()
    {
        List errors = new List();
        var container = UnityConfig.GetConfiguredContainer();

        foreach (ContainerRegistration registration in container.Registrations)
        {
            try
            {
                Type registrationType = registration.RegisteredType;
                var instance = container.Resolve(registrationType);
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
            }
        }

        //Note: going to add extra checks for controllers in here!

        return errors;
    }
}

I can then call it to get a list of (hopefully zero) errors with

var messages = UnityConfigurationChecker.GetUnityErrors();

This class isn’t intended to be hit by production code or user action, it’s just a helpful utility that tells us all the configuration errors we currently have. No messages is good, otherwise we need to investigate the issues (so I just catch all the exceptions and make a note of them).

Running Unity Tests

You could start with a basic controller/action that just runs the Unity checks and reports any errors to a view. Expand this with an API method and a script that runs during your build process to call the API and get a list of (hopefully zero) issues.

Now the fun begins as we start commenting-out Unity entries and watch those errors eg. “The current type, UnityChecker.Services.IDoStuffService, is an interface and cannot be constructed. Are you missing a type mapping?”

So far this only tests the registered container entries. It will only pick up issues with registered types that require other registered types.

Assume you have this setup of three services, only one of which requires constructor injection:

container.RegisterType();
container.RegisterType();
container.RegisterType();

public DoStuffService() { ... }
public DoThingsService() { ... }
public DoStuffAndThingsService(IDoStuffService doStuffService, IDoThingsService doThingsService) { ... }

If you forgot to register either the DoStuffService or DoThingsService, the DoStuffAndThingsService can’t be resolved.

Testing Unity With Unit Tests

So this gives you a Unity-checking utility that you can use on a running web application, but that means you have to wait until deployment time to check your config. Better than a fatal runtime exception days after deployment, but checking this during the CI process (or even before check-in) would be awesome. So let’s try building container-checking into a unit test.

Add a unit test project to the solution (I’m using MsTest, but the NUnit approach should be the same) and create a simple unit test class:


[TestClass]
public class UnityConfigurationCheckerTest
{
    [TestMethod]
    public void GetUnityErrors_ExpectZeroErrors()
    {
        var messages = UnityConfigurationChecker.GetUnityErrors();
        Assert.AreEqual(0, messages.Count);
    }
}

Verifying Controllers (Version 1)

So far, this example only checks the container registrations, not the controllers. We can amend our method with extra code that also checks the controllers:


    //Need reference to the main MVC web application assembly
    //May need to check for typeof Controller and ApiController
    //Note: Prevent false-positives of abstract controllers
    var assembly= typeof(UnityConfig).Assembly;
    var controllerTypes = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller)));

    foreach (var controllerType in controllerTypes)
    {
        if (!controllerType.IsAbstract)
        {
            try
            {
                var controllerInstance = container.Resolve(controllerType);
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
            }
        }
    }
    return errors;

Note that searching the correct assembly for controllers is vital. Amend as necessary if you want to use the Unity checker with multiple different web application assemblies.

If the controller constructor expects something that’s not registered in the container, we’ll now get an error about it. Actually, I did get an error regarding one of the controllers in the demo MVC solution I used as a starting point, so some care may need to be taken about what constitutes an actual error in your configuration…

Better Exception Checking

Treating all the exceptions you hit as “fatal” is unhelpful, we want to distinguish between container registration errors and other runtime issues (eg. a service fails because of some other database or configuration issue). Be mindful of the testing context you run the Unity checks in. This is especially important if you’re going to use container checking as a build-breaking condition.

An improvement is to catch “System.InvalidOperationException” and “Microsoft.Practices.Unity.ResolutionFailedException”, and then log the exception type and message. For controllers that throw ResolutionFailedException, if there’s an InnerException then use that instead.

As a test, you could make sure your container registers everything you need, then deliberately throw an exception in a service/controller constructor – differentiate between being unable to construct the controller (missing Unity configuration entry) and a subsequent runtime exception.

Deciding on the correct set of exceptions to catch / ignore is still a judgement call and might not catch every scenario of “IOC configuration vs other runtime error” problem you run into…

Verifying Controllers (Version 2)

At this point, it’s maybe worth re-thinking the controller checks. While we’re making sure we can get controller instances, we’re not too worried about whether we can construct an instance of a controller so much as whether the controller has access to everything it needs to be constructed. So what if, instead of exercising Unity by constructing an instance of the controller, we just check that everything injected into the controller’s constructor is available in Unity?

So my refactored UnityConfigurationChecker class now contains this:


public class UnityConfigurationChecker
{
    private static void ResolveImplementationFromIoc(IUnityContainer container, Type registrationType)
    {
        try
        {
            var instance = container.Resolve(registrationType);
        }
        catch (InvalidOperationException e)
        {
            throw e;
        }
        catch (ResolutionFailedException e)
        {
            throw e;
        }
        catch (Exception)
        {
            //Ignore
        }
    }

    public static IList GetUnityErrors()
    {
        List errors = new List();

        var container = UnityConfig.GetConfiguredContainer();


        foreach (ContainerRegistration registration in container.Registrations)
        {
            Type registrationType = registration.RegisteredType;
            try
            {
                ResolveImplementationFromIoc(container, registrationType);
            }
            catch (Exception e)
            {
                errors.Add(e.GetType() + " - " + e.Message);
            }
        }
        //Need reference to the main MVC web application assembly
        //May need to check for typeof Controller and ApiController
        var assembly = typeof(UnityConfig).Assembly;
        var controllerTypes = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller)));

        foreach (var controllerType in controllerTypes)
        {
            if (!controllerType.IsAbstract)
            {
                var constructors = controllerType.GetConstructors();
                foreach (var constructor in constructors)
                {
                    System.Reflection.ParameterInfo[] parameters = constructor.GetParameters();
                    foreach (var parameter in parameters)
                    {
                        var parameterType = parameter.ParameterType;
                        if (parameterType.IsInterface)
                        {
                            try
                            {
                                ResolveImplementationFromIoc(container, parameterType);
                            }
                            catch (Exception e)
                            {
                                errors.Add("Registration error with controller " + controllerType.FullName + ". " + e.GetType() + " - " + e.Message);
                            }
                        }
                    }
                }
            }
        }

        return errors;
    }
}

And then a quick test – get the controller to deliberately fail eg. by throwing a new NotImplementedException in the constructor – and as long as the Unity configuration is fine, the Unity checks will pass, because you’re not actually constructing an instance of the controller. However, throw an exception in one of the concrete implementations you resolve from the container, and you will get an error.

Check again that this is all working by editing the UnityConfig.cs and hiding a few necessary registrations.

Gotchas and Other Issues

I encountered a few issues when applying this strategy to an existing codebase. The Unity configuration works fine in its natural web application context, but running in an unfamiliar unit testing context causes issues. Because you’re actually running the Unity registration and creating instances of those registered types during your tests, you may encounter errors in the setup before the test even runs and starts testing your newly-configured container.

Firstly, I had a few types that wouldn’t resolve themselves because they fell over at construction time looking for non-existent configuration settings. A few extra “appSettings” entries in the unit test project’s “app.config” file fixed that.

Next, I had our old friend the “Could not load file or assembly” exception. Because the unit test project is running the same code as your target application, it’s going to need to reference and load the same assemblies. Good luck debugging that one.

I was running the Unity configuration in application startup with a “UnityConfig.RegisterComponents()” call to run all my “RegisterType” calls. I also had the unit test run that as a setup step to mimic a clean application startup.

You can wrap a try-catch around your setup call, or just step-through in the debugger to find the problem registration, but it might take some effort to get your container to work correctly for your unit tests.

An Alternative Approach Using Reflection (Version 3)

There’s an alternate approach worth considering at this point. The aim of this exercise is not to actually resolve and construct any concrete instances (we won’t be using them). That’s just one way to test the container (it’s probably the best way). All we actually care about is one thing: if you have a service/controller with a concrete instance of another service injected at run-time, will it work? Or will the container say “I don’t know how to resolve that” and let the app fall over?

I already used reflection to inspect my controller constructor parameters, I can do the same trick for my service classes.

So here’s code for an alternative approach. I also took the opportunity to wrap any returned errors in a class that can easily show errors in the test runner just by overriding ToString().


public class UnityConfigurationChecker
{
    private static Type GetMappedRegisteredConcreteType(IUnityContainer container, Type registrationType)
    {
        if (container.IsRegistered(registrationType))
        {
            //Don't resolve, just ask the container what it might resolve to
            ContainerRegistration registration = container.Registrations.FirstOrDefault(r => r.RegisteredType == registrationType);
            if (registration != null)
            {
                return registration.MappedToType;
            }
        }
        throw new ArgumentException("Could not map to concrete type for " + registrationType);
    }

    private static bool InspectConcreteTypeEnsureConstructorParametersValid(IUnityContainer container, Type concreteType)
    {
        //Don't care about abstract controllers / services
        if (!concreteType.IsAbstract)
        {
            //Look for constructor parameters and resolve all injected interface implementations
            var constructors = concreteType.GetConstructors();
            foreach (var constructor in constructors)
            {
                System.Reflection.ParameterInfo[] parameters = constructor.GetParameters();
                foreach (var parameter in parameters)
                {
                    var parameterType = parameter.ParameterType;
                    if (parameterType.IsInterface)
                    {
                        try
                        {
                            GetMappedRegisteredConcreteType(container, parameterType);
                        }
                        catch (Exception e)
                        {
                            throw new ArgumentException("Registration error with type " + concreteType.FullName + ". " + e.GetType() + " - " + e.Message);
                        }
                    }
                }
            }
        }
        return true;
    }

    public static UnityConfigurationCheckSummary GetUnityErrorsContainerConfiguration()
    {
        UnityConfigurationCheckSummary summary = new UnityConfigurationCheckSummary();
        var container = UnityConfig.GetConfiguredContainer();

        foreach (ContainerRegistration registration in container.Registrations)
        {
            Type registrationType = registration.RegisteredType;
            var concreteType = registration.MappedToType;
            try
            {
                InspectConcreteTypeEnsureConstructorParametersValid(container, concreteType);
            }
            catch (Exception e)
            {
                summary.Errors.Add(e.Message);
            }
        }
        return summary;
    }

    public static UnityConfigurationCheckSummary GetUnityErrorsControllerConfiguration()
    {
        UnityConfigurationCheckSummary summary = new UnityConfigurationCheckSummary();
        var container = UnityConfig.GetConfiguredContainer();

        var assembly = typeof(UnityConfig).Assembly;
        var controllerTypes = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller)));

        foreach (var controllerType in controllerTypes)
        {
            try
            {
                InspectConcreteTypeEnsureConstructorParametersValid(container, controllerType);
            }
            catch (Exception e)
            {
                summary.Errors.Add(e.Message);
            }
        }
        return summary;
    }
}


public class UnityConfigurationCheckSummary
{
    public IList Errors { get; set; }
    public int Count { get { return Errors.Count(); } }

    public UnityConfigurationCheckSummary()
    {
        Errors = new List();
    }

    public override string ToString()
    {
        if (Errors.Any())
        {
            return string.Join(", ", Errors);
        }
        return string.Empty;
    }
}

Here’s the unit test code, checking that we get no errors, but giving a nice summary if we do. You might need to add a “Setup” method to reset/recreate your container for each test.


[TestClass]
public class UnityConfigurationCheckerTest
{
    [TestMethod]
    public void GetUnityErrorsContainerConfiguration_ExpectZeroErrors()
    {
        var summary = UnityConfigurationChecker.GetUnityErrorsContainerConfiguration();
        Assert.AreEqual(string.Empty, summary.ToString());
        Assert.AreEqual(0, summary.Count);
    }

    [TestMethod]
    public void GetUnityErrorsControllerConfiguration_ExpectZeroErrors()
    {
        var summary = UnityConfigurationChecker.GetUnityErrorsControllerConfiguration();
        Assert.AreEqual(string.Empty, summary.ToString());
        Assert.AreEqual(0, summary.Count);
    }
}

Summary – Verifying Unity Configuration

With some simple code like this, every time you add a service or change a controller, your Unity configuration should be tested for correctness with a regular unit test run. And you can always put the tests behind an API method and poll after a deployment to be sure that your configuration is correct.

Versioning .Net Applications With Team City and Git/SVN/TFS

Some notes on setting up version numbering for .Net applications and having your build server manage everything (I’m using Team City). I’ll cover some differences between centralised (TFS, SVN) and decentralised (Git) version control. These notes are based on a couple of “single-branch, build-once-and-redeploy” continuous delivery pipelines I set up for projects in TFS and Git.

Why Version?

We’ve all seen software shipped from developer machines, hoped we shipped the code we actually tested, and had to troubleshoot live issues where all we know about the code is it’s “probably whatever Bob had on his laptop three weeks ago”. Want the easy life? Bake in a version number that:

  1. Confirms the code was built and released through an approved CI/CD process – the presence of a default “1.0.0.0” version indicates worrying “laptop” deployments *
  2. Confirms the production code matches the tested code (and hasn’t had any bonus unpredictability merged in)
  3. Tells you where to find the exact code revision in source control
  4. Tells you which build job created it
  5. Gives you a changing version number to display to users that says “new stuff”

Display that version somewhere in your code and have it available on your build server.

* OK, you could cheat and fake a version number, but the aim is to make things so easy that doing things properly is the only option you consider.

What Version Number Format Should I Use?

This one is up to you. .Net projects allow a basic four-segment numbering system.

A recommended format is: “<major version>.<minor version>.<build number>.<revision>”.

Major/minor components are typically your “marketing version”, but you might want to use them to signal build time (eg. calendar date or sprint number). They probably won’t change often, and updates may be manually controlled. You definitely want to have build and revision/changeset numbers automatically applied so you can trace where the code came from. If your source control doesn’t use sequential version numbers (eg. Git) then you need to make a few adjustments, typically by using the revision checksum/snapshot instead of a sequential code revision.

Displaying the Version Number In Your Application

Your application should display the version number somewhere – on a help/about dialog, footer, via an “info” API call, etc.

Within your code, each project has an “AssemblyInfo.cs” file under “properties”. This can contain versioning attributes for:

  • AssemblyVersion
  • AssemblyFileVersion
  • AssemblyInformationalVersion

Version and file-version components consist of four numbers (you might be able to squeeze letters into the file version, but you’ll get a warning). The informational version allows more freedom, so you could hide a checksum, like a Git revision (yes, I really did ship an application with version number “1.3.29.300bcd5309dacf3897fc41ba11dff56409b136db”).

These numbers are baked into the compiled code (inspect the properties of a DLL file).

Your default AssemblyInfo.cs entries look like this:

[assembly: AssemblyVersion(“1.0.0.0”)]
[assembly: AssemblyFileVersion(“1.0.0.0”)]

You can manually edit these and rebuild, but much better to get your build process to apply them automatically. To display, just use:

System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()

For the AssemblyInformationalVersion, use

Attribute.GetCustomAttributes(Assembly.GetExecutingAssembly(), 
typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute[]

And then display the first item in the array.

If you’re using Git, the AssemblyInformationalVersion is the place to store your revision.

Build Pipelines for Continuous Delivery / Continuous Deployment

I’m assuming you have some sort of pipeline whereby code passes through an initial CI (build on code change) stage to Test (deployment) to Live (deployment). You can control the flow of this pipeline in a couple of different ways:

  • Single branch / “trunk-based” deployment – everyone commits from local workspace (or their own development branches) to a single main branch, and you build once (at the Continuous Integration stage) then redeploy the same package/artifact at subsequent Test/Live stages
  • Branch-based – you merge code from Dev to Test to Live branches, and rebuild at each stage before deploying

You can use either approach, but always “ship what you test”. Because centralised version control systems (eg. SVN and TFS) assign a new sequential revision number when you merge code to a different branch, it’s difficult to track code changes across branches, so I wouldn’t recommend using a branch-based approach for SVN or TFS unless your process involves full continuous deployment at every step and you trust to full automated test coverage. With Git’s decentralised model, you have a checksum built into to the revision, so you can tell whether the merge introduced any changes, and a branch-based continuous delivery approach becomes viable.

 

Versioning in Team City

Each build in Team City is assigned a build number. You edit this in the configuration’s General Settings panel, just enter a value for “Build number format”. Typically this is built from a number of properties, the default being the “%build.counter%” (Team City’s internal count of builds for that configuration, also editable on the General Settings panel).

While the build counter is managed by Team City and the revision tracks back to source control, other build numbers can be applied. So to manage “marketing” versions for your product:

  1. Edit Project->Parameters
  2. Create two “configuration” parameters – “Project.Major.version” and “Project.Minor.version”

You could set up parameters for an individual build if you want, but project-wide parameters make sense. These values are now available to use in components of other build numbers as “%Project.Major.version%” and “%Project.Minor.version%”.

Some clarification – there’s a few different build numbers you’ll have to manage here:

  • The Team City build counter – increments each time Team City runs a specific build configuration job eg. “build code for project X” (this is a single number)
  • The Team City build number – applied to each build run, displayed on Team City dashboard and build reports (this might be a four-digit version number)
  • The version number(s) baked into the code at build time

Team City makes setting version numbers really easy, using the “Assembly Info Patcher” build feature – use this for every build configuration that is actually building code (CI, or any branch-based “re-build and re-deploy” stage). Just edit a build configuration and go to Build Features->Assembly Info Patcher. Set the required “Assembly version”, “Assembly file version” and “Assembly informational version” values to edit the
corresponding attributes in the AssemblyInfo.cs files.

Build Number Format (TFS)

For the initial CI build configuration use a build number format of

%Project.Major.version%.%Project.Minor.Version%.%build.vcs.number%.%build.counter%

I just set this as the version and file version in the assembly info patcher dialog, and ignore the informational version.

If you’re taking the single-branch/build-once-and-redeploy approach, then for a chained/dependent build configuration (eg. Deployments to Test/Live) set the build number of subsequent builds in the chain to be

%dep.CIProjectName.build.number%

You can use this approach to assign the same build number to the same code built/deployed by different build configurations – I do this to track a release candidate from the initial CI stage (build and package) through the various Test and Live deployment pipeline stages.

Build Number Format (Git)

For Git, I set the build number format to

%Project.Major.version%.%Project.Minor.Version%.%build.counter%

This means I only get 3-digit version numbers displayed on Team City, and no direct reference to the code.

I then set the assembly info patch values to be (on Build Features->Assembly Info Patcher)

Version and file version:

%Project.Major.version%.%Project.Minor.Version%.%build.counter%.0

Informational version:

%Project.Major.version%.%Project.Minor.Version%.%build.counter%.%build.vcs.number%

Passing Team City Version to Build Scripts

A quick note: Team City sets the current build number (the full 4-digit one) in a “BUILD_NUMBER” environment variable. So you could have an MsBuild script in your project use this if you need it (eg. to name files, or append to release notes and emails).

Example MsBuild usage:

<Message Text="Build number=$(BUILD_NUMBER)"/>

 

Summary

So you can use your build server to generate meaningful version numbers, track the same code as it works through the various chained builds of your build pipeline from initial commit to production deployment, and bake the appropriate version into your code. With a version number available, you should have confidence that the production release is known and tested software, and not some mystery release you can’t recreate locally.

 

Choosing What Gets Deployed When Publishing A .Net Web Application

When you use MsBuild to package up a web application for publishing, you typically rely on the publishing profile, to manage either a “build and publish” or “build a package for later deployment” process (a .zip package is created either way). By default, everything in your project may be copied to the output and included in the deployment package .zip file, unless you make adjustments.

You already have some control over whether files get copied into the output and packaged up, using file properties in the solution explorer, as set by the “Build Action” (set to “none” rather than “content”) and “copy to output directory” properties. However, it may be easier to just use some MsBuild to avoid packaging several files (or whole folders) based on a wildcard pattern.

Example MsBuild syntax for generating the deployment package would be:

msbuild WebApp\WebApp.csproj /t:Build /p:VisualStudioVersion=11.0;DeployOnBuild=True;PublishProfile="DeploymentPackage.pubxml";ProfileTransformWebConfigEnabled=False;Configuration=Release;

 

Including Extra Files In The Package

I previously covered ways to include extra files that your project doesn’t know about:

https://ajmburns.wordpress.com/2016/04/10/including-additional-generated-files-when-publishing-a-net-web-application/

Removing Files From The Package

If you wanted your deployment to omit selected files or folders, you can get the publish profile to that as well. I have to do this to hide deployment parameter config files and selected custom scripts that are part of my solution, but should not be deployed.

To test, add the excludes to the main “PropertyGroup” element of your publish profile, then build the package and check the generated package .zip file.

Example – excluding a list of custom scripts:

<ExcludeFilesFromDeployment>
 compile-this.bat;compile-that.bat
</ExcludeFilesFromDeployment>

Example – excluding a wildcard list of configs:

<ExcludeFilesFromDeployment>
 parameters*.xml
</ExcludeFilesFromDeployment>

Example – excluding a folder:

<ExcludeFoldersFromDeployment>
 myConfigs
</ExcludeFoldersFromDeployment>

MsBuild Version Gotchas – Better Building with MsBuild scripts and Build Servers

I recently ran into issues when upgrading an old codebase from the original setup (Visual Studio 2013 and the MsBuild tools v4.0) to use Visual Studio 2015, building through Team City on a new machine that only had VS2015. The MsBuild scripts I had been using for building (mostly creating the deployment package) and actual deployment (calling MsDeploy) needed a few tweaks. Mostly, I had taken and adapted a boilerplate MsBuild script from an earlier project, left in some stupid hacks that worked well until they didn’t, and found my scripts needed a few tweaks to work with the current tools.

My build process was to use Team City to compile, package, and later deploy (via MsDeploy) a web app to Azure, using MsBuild scripts to manage the actual solution build/package/deploy specifics.

For the purposes of this post, Team City happens to be my build server of choice. If the detail of your build process is stored and versioned with the code (and not the build server configs) then switching build servers should be easy.

So this post is some note-to-future-self gotchas for the next time a tools upgrade breaks the build…

MsBuild.exe path and version

First issue was to check the location of the MsBuild.exe itself. As this wasn’t already in my path, I had been specifying it explicitly as a command prompt setup for locally debugging build scripts, using a “commandPrompt.bat” with the following line

@%comspec% /k "SET PATH=C:\Windows\Microsoft.NET\Framework\v4.0.30319;

So first issue is to fix the path and use the latest version – “C:\Program Files (x86)\MSBuild\14.0\Bin”.

Setting the local path to MsBuild isn’t strictly required – normally I’d build locally through Visual Studio, but I like being able to test the build scripts locally without having to push them to the build server, and wrapping the build specifics in an MsBuild script lets me do this. The important thing is that I want Visual Studio and Team City build to compile the code with the same MsBuild engine so there’s no surprises.

Whenever I run the MsBuild script eg. with a

msbuild WebPackage.msbuild /t:BuildPackage

I get the MsBuild engine version reported, but to help debug my scripts, I tend to put some kind of “Info” target in to report paths and settings, eg:

<Target Name="Info">
 <Message Text="MsBuildExtensionsPath=$(MsBuildExtensionsPath)" />
 <Message Text="MSBuildToolsVersion=$(MSBuildToolsVersion)"/>
 <Message Text="VisualStudioVersion=$(VisualStudioVersion)"/>
 <Message Text="VSToolsPath=$(VSToolsPath)"/>
</Target>

Now I can run:

msbuild WebPackage.msbuild /t:Info

If the latest MsBuild is on the path, I should get latest version reported (v14.0).

Also, in my .csproj file I found references in a PropertyGroup to “VisualStudioVersion” and “VSToolsPath” settings.

<PropertyGroup>
 <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
 <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

Your build scripts shouldn’t really be tying themselves to specific tools versions, better to set these values and pass them in to any running MsBuild process.

MsBuild Scripts Calling MsBuild

Noticed another problem – I had my MsBuild script calling the MsBuild.exe directly:

<Exec Command="$(MSBuildExe) $(SolutionName).sln /t:clean"/>

…and because I couldn’t guarantee MsBuild.exe in the path, I cheated and had my MsBuild script setting the MsBuild.exe path to a default if it wasn’t overridden:

<MSBuildExe Condition=" '$(MSBuildExe)'=='' ">C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe</MSBuildExe>

So checking the output, I was using the old MsBuild engine version. Oops. I could probably have allowed the .exe location to be specified either on the path, as an argument to the script, or as a default in the script, but that’s a hassle. Luckily, to free myself from nasty tools path dependencies, I can rewrite my MsBuild calls as:

<MSBuild Projects="$(SolutionName).sln" Targets="clean"/>

The general format is:

<MSBuild Projects="filename" Targets="target" Properties="prop1=val1;prop2=val2"/>

This should mean that whatever MsBuild engine you use to kick off the script is the one that gets used throughout. I have had issues with a few quotes around property values (solution: don’t quote property values), but it should be possible to express all MsBuild calls using the MsBuild task.

Importing MsBuild Tasks

Ok, for some reason, probably relating to some hacky web.config transforming I had done on previous projects (I have done terrible things to get applications to deploy and workaround the “configure-build-deploy” workflow of web.configs), I had the following using statement in my MsBuild script:

<UsingTask TaskName="TransformXml" AssemblyFile="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>

This was actually referencing non-existent DLL

C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll

Which should be

C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll

There’s another issue here – this will almost certainly require Visual Studio installed on the build server, or a cut down install eg. http://www.nuget.org/packages/MSBuild.Microsoft.VisualStudio.Web.targets/

Turns out, I actually no longer needed to use this task for building this project and I can just omit this line. But there will be times when your build script needs access to those extra tasks. If you can, try to avoid your build script knowing exactly where on the build server to look for stuff.

Team City Settings

So finally, I had to go into my Team City build configuration for all my build steps that used MsBuild and upgrade the MsBuild tools versions to latest. And check you’re using the same version throughout the build (I ran into problems compiling and packaging the same web app with different MsBuild versions in different Team City build steps).

Summary

Things to watch for:

  • Wrong MsBuild version on path – to debug, create an “Info” target in your build script and echo property values to console / build server log
  • Specifying the MsBuild version in your script – use the “MsBuild” task instead of direct MsBuild.exe calls to prevent specifying the .exe location on the path or in your script
  • References to a specific Visual Studio / tools version that may not be installed – eg. common MsBuild targets
  • Copying-and-pasting a previous build script and not cleaning out the unused stuff when you find a better way of doing things

Also – limit the use of MsBuild, try keep it simple, use it for building, packaging, including/excluding/copying files to be deployed. Handle deployment and other complex tasks through Powershell.

Including Additional / Generated Files When Publishing a .Net Web Application

When you publish a .Net web app, you typically set up a publish profile, either to do an immediate build-and-publish, or to publish to a package for later deployment (eg. single-trunk / build-once-deploy-many-times scenarios).

So for building a package, the MsBuild syntax would look like:

msbuild WebApp\WebApp.csproj /t:Build /p:VisualStudioVersion=11.0;DeployOnBuild=True;PublishProfile="DeploymentPackage.pubxml";ProfileTransformWebConfigEnabled=False;Configuration=Release;

You just use MsBuild and ask your project to build itself. The good bit is that your project knows what it contains, and therefore what needs deployed. The bad bit is that your project knows what it contains, and therefore what needs deployed… Any files generated by an external tool, or any other files you didn’t explicitly tell Visual Studio (listed as content in the .csproj file) are excluded. Thankfully, you can include extra stuff into your deployment package with a few lines of MsBuild commands. While you can probably hack the .csproj, the easiest way I’ve found is just to plug some MsBuild directly into the publish profile.

This location does make sense – you’re specifying what gets published after all. Note that if you have several publish profiles (maybe you have branch-based deployment and per-environment build-and-publish) then things get more complicated. I’m going to stick with the simple case – a single publish profile for creating a generic deployment package that we can then deploy (with appropriate configuration) to any target environment.

I recently had to solve this problem for handling some generated CSS, but you can use it for any non-solution files you want to include as part of the web application deployment package zip.

A quick warning – this does involve editing a generated file (the .pubxml file that you probably only create once).

Tweaking The Publish Profile

So assume I have some tools generating CSS (in “/webproj/content/styles”) and extra text files (in “/webproj/content/documentation”). I don’t even need to set up tools to generate these files – I can actually just simulate by manually adding some dummy files. I’m assuming that the files get generated within the project directory (because you want to deploy them to IIS or Azure when you deploy your site).

Anyway, create the extra files within the project directory, don’t tell Visual Studio or your project about them, and then build the deployment package. You’ll see they get omitted. We can fix that.

Go edit your publish profile, and add these lines inside the “PropertyGroup” element – we need to add a “CopyAllFilesToSingleFolderForMsdeployDependsOn” property.

 <CopyAllFilesToSingleFolderForMsdeployDependsOn>
 IncludeCustomFilesInPackage;
 $(CopyAllFilesToSingleFolderForMsdeployDependsOn);
 </CopyAllFilesToSingleFolderForMsdeployDependsOn>
 </PropertyGroup>

This goes off and calls a target called “IncludeCustomFilesInPackage” that we’ll create in a minute. The name of the “CopyAllFilesToSingleFolderForMsdeployDependsOn” property is important (MsBuild will look for it), the name of the custom “IncludeCustomFilesInPackage” target can be whatever we want (change the names and see what happens).

There is also a “CopyAllFilesToSingleFolderForPackageDependsOn” property, but this seems to get ignored even when building in package mode. For completeness, it would look like this:

<CopyAllFilesToSingleFolderForPackageDependsOn>
 IncludeCustomFilesInPackage;
 $(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>

But you don’t seem to need it – just add the “CopyAllFilesToSingleFolderForMsdeployDependsOn”.

Now we can go define our “IncludeCustomFilesInPackage” target, so add this to the .pubxml file, inside the “Project” element:

<Target Name="IncludeCustomFilesInPackage">
 <Message Text="Collecting custom files..."/>
 <ItemGroup>
  <CustomFiles Include="Content\styles\**\*" />
  <CustomFiles Include="Content\documentation\**\*" />
  <FilesForPackagingFromProject Include="%(CustomFiles.Identity)">
   <DestinationRelativePath>%(CustomFiles.Identity)</DestinationRelativePath>
  </FilesForPackagingFromProject>
 </ItemGroup>
 <Message Text="Add to package %(FilesForPackagingFromProject.Identity)"/>
</Target>

Add as many entries as you need into the ItemGroup. I added a couple of “message” calls to output progress.

So when you run your package build and inspect the final .zip package that gets created (check in the “obj” directory eg. “projectName/obj/projectName/projectName.zip”) then it should contain all those extra files that your .csproj didn’t know about.

Troubleshooting

When you’re setting this up, you might run into problems getting the paths correct. You can always add in a load of Message calls, and redirect the output of your MsBuild run to a text file.

You can add the following in your custom target for debugging purposes:

<ItemGroup>
 <GeneratedIncludeFiles Include="Content\documentation\**\*" />
</ItemGroup>
<Message Text="Generated files to include: %(GeneratedIncludeFiles.Identity)"/>

A note about “DestinationRelativePath” – I have seen this specified as “%(RecursiveDir)%(Filename)%(Extension)” instead of using the “CustomFiles” item group, but I had trouble getting this to actually include the custom files.

.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"?>
<parameters>
 <setParameter name="IIS Web Application Name" value="MyWebAppName" />
</parameters>

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"
-dest:auto,ComputerName="https://appname.scm.azurewebsites.net/msdeploy.axd?site=appname",UserName="$user",Password="extractFromPublishProfile",IncludeAcls="False",AuthType="Basic" 
-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" ?>
<parameters>
 <parameter name="EnvironmentName"
 description="blah blah blah"
 defaultValue="notsure">
 <parameterEntry kind="XmlFile" scope="Web.config"
 match="/configuration/appSettings/add[@key='EnvironmentName']/@value" />
 </parameter>
</parameters>

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:

-setParam:SettingName=value

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.

Summary

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:

<ExcludeFilesFromDeployment>
 parameters*.xml
</ExcludeFilesFromDeployment>

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"?>
<parameters>
 <setParameter name="IIS Web Application Name" value="MyAppName" />
 <setParameter name="EnvironmentName" value="Test" />
 <setParameter name="EnvironmentDescription" value="Testing" />
</parameters>

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 
/p:DeployOnBuild=True;PublishProfile="$(PublishProfilePackage)";ProfileTransformWebConfigEnabled=False;Configuration=$(Configuration)'/>
 <CallTarget Targets="_CopyPackageToDeploymentDirectory"/>
 <CallTarget Targets="_CopyParametersToDeploymentDirectory"/>
</Target>

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

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

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.

Pre-Transforming Web.Configs For Deployment

When your approach to automated deployment is to build a deployment package, then later deploy that to successive test/staging/live environments, you need to manage configurations. Unfortunately, Microsoft’s web.config approach is designed to work only as part of a configure-build-deploy process. For configuring at deployment time, you need a better approach (parameters files). Or a hack… There are better ways to manage configurations, but you might not have the ability to move away from web.configs.

A quick note: I originally included this section as part of a larger post on MsDeploy, but really this is a separate subject.

Transforming Web.Configs at Build Time

I’m going to manually add a target to my MsBuild file to transform my web.configs. I don’t know at build time where my deployment will end up, so I just include all of the possible configs.

We need a solution with an MsBuild file, and the necessary build configurations and web.config transform files setup. When building the deployment package, we also transform all the possible configs (and include in the deployment artifact). When deploying, we MsDeploy the entire site (from zip package), then do an additional MsDeploy of a single web.config file.

Now add a “_TransformWebConfigs” target and a reference to the “TransformXml” task that I’m going to borrow.

Right at the top of your build file, inside the Project element and before the PropertyGroup, add this:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>

For now I’ll just go process each configuration to create all possible web.configs in our package, transformed and ready to deploy. I’ve named the files “web.configurationName.config”, you could store them in “ConfigurationName\web.config” instead.

<Target Name="_TransformWebConfigs">
<TransformXml Source="$(WebApp)\Web.config" Transform="$(WebApp)\Web.Debug.config" Destination="$(PackageAssemblyDir)\Web.Debug.config" StackTrace="true" />
<TransformXml Source="$(WebApp)\Web.config" Transform="$(WebApp)\Web.Test.config" Destination="$(PackageAssemblyDir)\Web.Test.config" StackTrace="true" />
<TransformXml Source="$(WebApp)\Web.config" Transform="$(WebApp)\Web.Release.config" Destination="$(PackageAssemblyDir)\Web.Release.config" StackTrace="true" />
</Target>

And to call this, extend your “BuildPackage”, between building the package and copying to the deployment directory, add this:

<CallTarget Targets="_TransformWebConfigs" />

Now we add a “_DeployWebConfig” target that applies the correct config based on the $(Configuration) property (call MsBuild script with “/t:DeployPackage /p:Configuration=Test”). This is almost like the previous MsDeploy command, except:

    1. For the source, it really doesn’t like relative paths, so I grab the current working directory
    2. I’m explicitly setting the destination, because we’re renaming the file as we deploy
    3. You don’t set the “IIS Web Application Name”, MsDeploy complains if you do
<Target Name="_DeployWebConfig">
<Exec Command='$(MsDeployExe) -source:contentPath="$(WorkingDir)$(PackageDeploymentDir)\Web.$(Configuration).config" -dest:contentPath="$(AzureSite)\web.config",ComputerName="https://$(AzureUrl)/msdeploy.axd?site=$(AzureSite)",UserName="$(AzureUsername)",Password="$(AzurePassword)",IncludeAcls="False",AuthType="Basic" -verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -retryAttempts=2 -verbose -userAgent="VS12.0:PublishDialog:WTE12.3.51016.0"'/>
</Target>

And you just insert the call to deploy the config at the end of your “DeployPackage” target:

<CallTarget Targets="_DeployWebConfig"/>

Note that your “web.X.config” gets renamed to just “web.config” on the target server during the deployment.