The Short Answer
You need to use the dotnet publish
command to package your application for IIS deployment, and copy that output (not the output from dotnet build
) to the host. Your build infrastructure in TeamCity will never be responsible for hosting and/or running your application, so you do not need dotnet run *.dll
anywhere in your build scripts. Rather, when your application is properly published to Azure Web Apps, IIS will read the web.config
and automatically host your application (likely using the ASP.NET Core Module).
For the remainder of this answer, I'm going to assume that your application is an ASP.NET Core application running on .NET Core.
ASP.NET Core Hosting Model
Before we discuss how to properly publish an ASP.NET Core application, it's important to understand at a high level how your application is hosted. ASP.NET Core, unlike classic ASP.NET, is "self hosted." What this means is that your web application is just a simple .NET Core Console Application, and can be started like any other .NET Core application with dotnet run webapp.dll
. When your application runs, it starts up a web server, called Kestrel, in process.
In your main method in Program.cs
, you probably have something like this:
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseKestrel()
.UseStartup<Startup>()
.Build();
host.Run();
Here you can see that your app is starting up the Kestrel web server to listen to incoming requests and process them in your app. When you dotnet run webapp.dll
, you're starting up your app (and Kestrel) directly.
However, Kestrel is not intended to be a public-facing web server. When you run your app in production, you should run it behind a production-ready web server like IIS. Azure Web Apps is simply using IIS to host your application. When an incoming request reaches your app in Azure Web Apps, IIS receives the request and proxies it to Kestrel, which then passes it off to your application for processing.
The ASP.NET Core Module, a native IIS module, is the component in IIS that is responsible for starting your application, keeping it alive (and restarting it if it crashes), and proxying requests to Kestrel. So, when running your app in Azure Web Apps, the dotnet run
command is never used. Rather, the ASP.NET Core Module is used to run your application.
The ASP.NET Core Module is configured in your web.config
file (and by the call to UseIISIntegration
above):
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
So, in order to run your application in IIS (like in Azure Web Apps), you must have a properly configured web.config
file alongside the application package you deploy. More on that in a bit.
Publishing an ASP.NET Core Application
Before automating your build system in TeamCity, it's worthwhile to validate all the steps in your deployment process. Let's assume your application is in C:/MyApp/src/WebApp/
.
First, as you noted, you need to restore all the dependencies for your application.
C:/MyApp/src/WebApp> dotnet restore
Second, and optionally, you can build your application to make sure there are no compilation errors.
C:/MyApp/src/WebApp> dotnet build
As I mentioned, that last step is actually optional; the next step, dotnet publish
, will also build for you, so it's unnecessary to use the build step in your automation.
Third, you need to publish your application. This was the crucial step you were missing in preparing your application. The dotnet publish
command packs your application and all its dependencies into a folder that is suitable for deployment.
C:/MyApp/src/WebApp> dotnet publish
This will package your application for deployment, putting the output in a folder like C:\MyApp\src\WebApp\bin\Debug\netcoreapp1.1\publish
. When doing this in your production build automation, you want to use dotnet publish --configuration Release
to build in Release, not Debug, mode.
The output in this /publish
folder is what you need to deploy to Azure Web Apps. There are many options for doing so, including FTP, Xcopy, or Web Deploy.
As a final note, there's actually one more step involved in preparing your app for IIS. The publish-iis
tool should be used to configure the ASP.NET Core Module. Using this tool ensures that your web.config
is properly configured for IIS. If you created your app from a template, you'll probably see that it has already been configured in your project.json
to automatically run after every dotnet publish
:
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
In summary, there are only really three things you need to do to publish your app:
dotnet restore
dotnet publish --configuration Release
- Copy publish output to Azure Web Apps
Building a TeamCity Pipeline
It sounds like you already have experience working with TeamCity, so I won't delve too deeply in how to do set up the necessary build steps. At a high level, though, here are the steps you'd need the build configuration:
- Run
dotnet restore
- Run
dotnet publish
. I like to manually specify the output directory, like so: dotnet publish --configuration Release --output %teamcity.build.checkoutDir%\.publish
. This makes it really easy to add .publish
as an artifact path on my build configuration so TeamCity keeps a permanent record of the output that was deployed.
- Deploy the published output to Azure Web Apps. I'm guessing you're using FTP deployment, so you can use Team City's built-in FTP upload step.
A few closing thoughts about using TeamCity:
While all the .NET Core SDK commands (dotnet *
) can be simply run with a Command Line build step, I use and highly recommend the TeamCity .NET Core Plugin: https://github.com/JetBrains/teamcity-dotnet-plugin This plugin, developed by JetBrains themselves, makes it super easy to run the dotnet
commands against your app.
If you are using a file upload method (FTP/Xcopy) to deploy your app, I'd urge you to consider using Web Deploy. When you use Web Deploy (msdeploy.exe), the engine will do a diff and only deploy the files that have changed. This significantly speeds up your deployment process. It will also automatically create an app_offline.htm
file for you while the deployment is happening, so IIS never serves requests while you're in the process of copying files to Azure.
Finally, if you have the option, do your continuous deployments to an Azure Web Apps Staging Slot, instead of directly to your production website. This will let you test your application before real users do, and you can warm up your site before swapping it in to production.
Resources
I highly recommend reading through the ASP.NET docs (https://learn.microsoft.com/en-us/aspnet/core/); there's a lot of great stuff there.