ASP.NET Core, DevOps, DevTools, MVC

MSBuild… what? I just right-click on Publish

What is MSBuild? I had no idea. It has the word “build” in it, it must build something. Oh, I also remember that in the early stages of .NET Core there was a lot of discussion about project.json and the all DNX thing…. Then they decided to keep this MSBuild thingy. In the end we are all using Visual Studio and the only thing I needed to know is how to set up a Publish definition, right-click on publish and voila! My app was ready to ship. Untill…

While doing some DevOps work, I wanted to build the app only once but transform the config file multiple times based on the environment. So I started digging into this MSBuild. What is it? Here is Microsoft definition: MSBuild is the Microsoft Build Engine, a platform for building applications.
It really does a lot for our apps but all its work is hidden behind the “greatest GUI” of all time: Visual Studio.

I was going into this discovery with the idea that MSBuild would work like some of the other build process. Run a command with some flags and the build is done. Instead, the story is slightly different. It is a bit like the Angular CLI with schematics. You use schematics to define custom actions or re-define existing actions. In MSBuild you use a build file (xml format) to define the sequence of actions you need to do. That is what the .csproj are. Just build definitions. When you run MSBuild and point to a .sln file, it knows how to go through each .csproj and run the build processes as needed. Let’s see how it works and play with it.

Where is MSbuild in our machines?

The MSBuild command comes with Windows installation here: C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe based on the .NET Framework. But this version is kind of bare bone, it lucks a bunch of extensions you might need for application specific builds (like we.config transformation).

Visual Studio installation brings in a more complete version of it here: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe (actual location may vary based on your VS version and installation settings). This version includes a bunch of extensions saved in these folders:

First a bit of configuration:

Let’s start by making sure MSBuild work. Open PowerShell (or your command line of choice) and just run the MSBuild that comes with Windows with the -version flag:

As you can see all is good, MSBuild responded with its version. Now let’s see the version available with Visual Studio install:

Much newer version (obviously).

We will be using this version for the exercises below.

Hello World

This example comes from here here.

Go to your working directory of choice and create a new file named I use VS Code but you can use your editor of choice. The content of this new file is:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0"  xmlns="">
    <Target Name="HelloWorld">
        <Message Text="Hello"></Message>
        <Message Text="World"></Message>

MSBuild has 2 main concepts in executing instructions:

  • Target
  • Task

The first is a set of instructions/command to complete a larger unit of work. A task is the smallest unit of work, usually just one instruction. All instructions are wrapped in a Project tag. A target is called via a flag on the MSBuild invocation /t:TargetName (see below). In this case we just print on the console “Hello” and “World”. Let’s do it.

In PowerShell run this command:

& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" /t:HelloWorld


How about flow control and variables?

But of course they are possible. A new variable is just a custom xml tag and condition can be build in a PropertyGroup tag. Create another build definition file and call it with this content:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0"  xmlns="">
    <PropertyGroup Condition="'$(Name)' == ''">
        <OutMsg>Please let us know your name!</OutMsg>
    <PropertyGroup Condition="'$(Name)' != ''">
        <OutMsg>Welcome $(Name)!</OutMsg>
    <Target Name="Condition">
            <Message Text="$(OutMsg)"></Message>

Then run it with:

& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" /t:Condition /p:Name=""

And then assign your first name to Name:

& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" /t:Condition /p:Name="Emanuele"

You know already the result:

How about that web.config transformation?

In this case, we will use a task (small unit of work) and it is a preexisting task that comes with the Visual Studio installation. So we must import such task. Here is the build definition:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0"  xmlns="">
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Staging|AnyCPU'">
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
    <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
    <Target Name="TransformWebConfig">
        <TransformXml Source="Configuration/Web.config"

I called this definition

We then need the we.config files to be transformed. I created 3 files inside a folder called Configuration:

  • web.config
  • web.Staging.config
  • web.Release.config

Here is the xml in each file. Nothing new just regular web.config with environment transformations.

<!-- web.config -->
<?xml version="1.0" encoding="utf-8"?>
        <add name="entities" connectionString="Debug" />

<!-- web.Staging.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="">
        <add name="entities" connectionString="Staging" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />

<!-- web.Release.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="">
        <add name="entities" connectionString="Release" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />

Let’s now run this build definition with this command:

& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" /t:TransformWebConfig /p:Platform=AnyCPU /p:Configuration=Staging

As expected a new transformed web.config file is created in the working directory:

With this expected content:

<?xml version="1.0" encoding="utf-8"?>
        <add name="entities" connectionString="Staging" />

You can run the Release version of the above command, just change the Configuration property value.

Well, I started this little journey a bit worry about the complexity of MSBuild, but once I understood a couple of the basic concepts, it wasn’t that hard to use. Keep in mind that you can do a lot with MSBuild, from copying files around, to run node, npm or other command line scripts.

I would love to hear some interesting use of MSBuild you have done. Feel free to leave a comment below.


Test APIs bypassing CORS

I’ve been struggling for a couple of days to get this app I am working on to POST on to its back-end.  After tons of tries, many Stackoverflow reads and ajax call property reset, I got to the working solution, and I want to share it the community so that the next person doesn’t have to waste time.

Here is plain and simple, add this flag to the Chrome call (if you use Chrome):

--user-data-dir="C:/Chrome dev session" --disable-web-security

Explanation: CORS (Cross-Origin Resource Sharing) is a rule by which a call to an API from an app no residing on the same domain is rejected.  Unless the API server is set to accept calls from any app.  This rule is mainly enforced by the browser, without going too deep into the details of how this works, the best way to avoid this while developing , is to disable web security in the browser.

In doing so, it turns out that that the most recent Chrome versions do not accept the “–disable-web-security” flag by itself, you must also set the user data directory.  Therefore, create a directory of your choice and ad this flag along with the previous one: –user-data-dir=”my-data-directory”.

For some reason running this

“Chrome.exe --user-data-dir="C:/Chrome dev session" --disable-web-security”

from the command line, it doesn’t work as well as creating a shortcut with this command in its target.  Here are the steps:

  • Create a user data directory of your choice
  • Create a shortcut on your desktop pointing to the Chrome.exe app
  • Right-click on it, select the Shortcut tab and on the Target field, add this to the end of the existing field: –user-data-dir=”C:/Chrome dev session” –disable-web-security.  The Target field should look something like this:
     "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --user-data-dir="C:/Chrome dev session" --disable-web-security

One last and important thing: make sure to close all Chrome instances before clicking on the newly created shortcut to open Chrome with disabled web security.

I hope this helps.