Building And Deploying Front End Assets In .NET With TeamCity And Octopus Deploy

In the "Deploying An ASP.NET Core Site: You're Still Doing It Wrong" series I defined why we want to strive for automation that surrounds our deployment process, how automation helps bring predictability and reliability to our deployments, and how we might go about setting up a Continuous Integration environment to achieve automation. In the fifth part of the series, I loosely touched on the idea of adding your front-end build to the process but did not go into many details. In this post, I will dive a bit deeper into how to build your front-end assets for both ASP.NET and ASP.NET Core projects using TeamCity and how to appropriately pack those assets and publish them to Octopus Deploy, making them ready for your deployment.

One Build Not To Rule Them All

Typically, we would not want to push our built asset to our git repositories. It adds both bloat and confusing to your change history, and it should be left to your build server to produce everything that is needed to deploy your application. With that in mind, you generally would want to add all you front-end tooling assets (typings, gulpfile.js, bower.json, package.json, temp files, etc...) and your front-end final build assets, of which are continuously regenerated during your development phase, to your .gitignore file.

There are a ton of options for us to use in regards to front-end tooling. Personally and professionally, I have gravitated towards a combination of npm, bower, and gulp. A typical example of my front-end build steps might look like this.

  1. Install npm modules
  2. Install bower modules
  3. Run build steps by way of gulp

That being said, the example in this post will be based on this build process. If you happen to use other tooling, the follow will still be of value to you as it will highlight steps you will need to take regardless and some issues you might come across during your setup.

Command Line For The Win

The vast majority of front-end tooling happens directly on the command-line or is abstracted on top of a command-line. Though there are "runner type" plug-ins for TeamCity that attempt to make the process of building your front-end assets easier, in my experience they are nothing more than interfaces that bring a familiar lexicon to TeamCity. Instead of installing these plug-ins, I recommend interacting with your tooling using the TeamCity command-line runner in order to gain parity between your development process and build steps.


On the box that TeamCity lives on, you will need to ensure Node and by way of, npm, are installed. I would recommend running the following Chocolatey command to do so.

choco install nodejs.install

Head over to your Build Step(s) in TeamCity, add a new step and select the Command Line type. The command-line runner will allow you to execute command-line commands against a directory of your choice, just like you would do during your development process. In this step, I am simply telling TeamCity to execute the following command.

npm install
Npm Build


Justifiably you might think that all we need to do to run a bower process would be to add a new command line build step that executes the following.

bower install

Unfortunately, that is not the case. If you do, you will end up with an error like so.

Bower Error

In most cases, TeamCity is installed with a Service Account running its services. This means you have a few options on how you want to handle TeamCity not being able to recognize the bower command.

  1. Apply a user account that has access to a global (-g) install of bower to the TeamCity services.
  2. Add a global path environment variable.
  3. Dynamically set the path after installing bower inside TeamCity.
  4. Target the bower executable with an absolute path.

I chose option 4.

Bower build


Again, create a new build step with a command line runner. Just like last time, the expectation would be to run the following command.


Much like last time, we again end up with an error.

Bower build

Since I know TeamCity already has access to node, this time around I am going to target my gulp build by proxy using a node script. Inside my package.json file, I added the following script commands.

"scripts": {
  "gulp-public": "gulp public",
  "gulp-admin": "gulp admin"

This will allow you to create a command line build step like the following.

Bower build

And that is that. At this point, TeamCity should be producing a deployable version of front-end assets.

Get These Assets Out Of My Project

During development, we are rapidly iterating over the process of building our front-end assets. We remove files and add files to the final output at a rapid pace. Because of this, we do not want to include any of these assets in our project. More importantly, we don't want to have to remember to include new files in our project and instead should rely on TeamCity to supply them in its final build package. That being said, we want to make sure we explicitly exclude these files from our project. This holds true be it an ASP.NET or ASP.NET Core project.


Exclude csporoj

Solution Explorer

Exclude Solution Explorer

Packing Before Deployment

Now that we have TeamCity building and producing our assets, we need a way to get those assets over to Octopus Deploy. This process will differ depending on if you project is of the type of ASP.NET or ASP.NET Core.


In my ASP.NET projects, I use octo-pack to pack up everything that is defined in my project file before passing it along to Octopus Deploy. If you recall from above, we explicitly removed our front-end build from our project. Meaning, when octo-pack is executed, even though we have TeamCity building our front-end assets, it will not include them in its final package.

To fix this, we need to introduce the concept of a .nuspec file.

A .nuspec file is an XML manifest that contains package metadata. This is used both to build the package and to provide information to consumers. The manifest is always included in a package. .nuspec reference

Inside your .nuspec file, you want to explicitly include files and folders you want octo-pack to package before publishing.

<?xml version="1.0"?>
<package xmlns="">
    <description>Your.Project Build</description>
  <file src="wwwroot\app.css*" target="wwwroot" /> 

If you don't explicitly define includes in your .nuspec file, octo-pack will look to your project file for information on what to pack into its final outputted packaged. If you do add explicit definitions targeting what to include, octo-pack will override you project file definition and only include what is in your .nuspec file. In our case, we want to include everything that is in the project file in addition to the file and folders we have added to our .nuspec file (front-end assets). To accomplish this, we will need to add a command line flag to our octo-pack operation in TeamCity.

Octo Pack

With a nuspec file in place and the appropriate command-line parameters being set, octo-pack will now pack up the entire definition of files located in your project file, in addition to any amendments you made in your nuspec file.


The process when dealing with .NET Core, for me, is reversed. Instead of including files and folder using a .nuspec file, or for that matter including a .nuspec file at all, I pack the entire dotnet publish build and exclude what I don't want packed in the Octopus Push build step configuration .

Octo Pack


As demonstrated, even with the few issues I identified, the process is fairly straight forward when you approach it from the command-line instead of an abstraction on top of the command-line. By building our assets in TeamCity, we are adding more automation to our deployment process and by way of, reliability and predictability.

For more information on how to setup a contentious integration pipeline using TeamCity and Octopus deploy, please be sure to visit my five-part series titled Deploying An ASP.NET Core Site: You're Still Doing It Wrong. Specifically, you can look at Part 5 for some supplemental information about building front-end assets. As always, if you have any comments or questions, please feel free to leave a comment below.