1

I have a dotnet core app. I am building a pipeline in teamcity. and once the teamcity detects the change in the cource code, It downloads the source code runs dotnet restore dotnet build

and copies the content in the output folder to Azure app service.

I believe to run the app i need to run the command dotnet nameoftheprojectdll.dll

but how can I run this command on the app service. and I need to run this command as part of a build script.

wonderwall
  • 146
  • 1
  • 11

1 Answers1

5

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:

  1. dotnet restore
  2. dotnet publish --configuration Release
  3. 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:

  1. Run dotnet restore
  2. 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.
  3. 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.

Ryan Erdmann
  • 1,786
  • 10
  • 11
  • Thanks mate for the advise but It did not work. I had all the settings that you mentioned. My Program.cs is same as you mentioned, My web.config had the same entries that you mentioned and my project.json had the entries "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]. After getting the Publish folder and manually put the content of PUBLISH folder into wwroot folder of my webapp and restarted the webapp. now when i hit the URL I get 500 internal error. – wonderwall Dec 02 '16 at 05:59
  • There are a number of things that could cause an HTTP 500 response, so it's hard to speculate about what the real reason is. Can you verify that you can run your application locally with `dotnet run myapp.dll` in the publish output directory? – Ryan Erdmann Dec 02 '16 at 06:07
  • The app is running fine locally If I build using VS or If I build using dotnet build, dotnet publish and from the ouput directory running the donet nameofthedll.dll – wonderwall Dec 04 '16 at 22:20
  • I enabled the logs in web app and can see the following information.Ensure that the NTFS permissions for the web.config file are correct and allow access to the Web server's machine account.
  • Check the event logs to see if any additional information was logged.
  • Verify the permissions for the DLL.
  • Install the .NET Extensibility feature if the request is mapped to a managed handler.
  • – wonderwall Dec 05 '16 at 00:31
  • Is your `web.config` file located at `/site/wwwroot/web.config`? – Ryan Erdmann Dec 05 '16 at 00:33
  • Yes as do the all other files that I get after running dotnet publish. – wonderwall Dec 05 '16 at 02:39
  • Its working for me now. I had to copy everything from the output of the dotnet publish command and also from dotnet build command. When I only copied the content generated from the command dotnet publish then I was getting 500 internal error. And a restart of the web app as well. Is this how it should be or do i need t make changes to my project.json? – wonderwall Dec 05 '16 at 06:22
  • You shouldn't need to copy the output from `dotnet build` in order to make it work. Because you do, it sounds like there is some missing resource your app depends on that is not getting copied to the publish output directory (although if this is the case, it surprises me that it ran locally from the publish directory). Can you do a clean publish, and inspect the build vs. publish output to see if any files are missing? Speculating here, it's common to forget the `appsettings.json`. Add missing files/folders here: https://github.com/aspnet/live.asp.net/blob/dev/src/live.asp.net/project.json#L76 – Ryan Erdmann Dec 05 '16 at 07:18
  • all those files which were not included in the build options and publish options are not included in respective command. and the output of both is different as well. What does not make sense to me is that If publish does the build as well then why does it not use the build options and only use the publish options. How can I use publish option to make sure that some files and folders are copied in the output. I used the include but it is not giving the desired folders in the output folder. – wonderwall Dec 05 '16 at 23:25
  • While the `dotnet publish` command does build your code, it uses `publishOptions` to control what files are included in the output. Can you please post a copy of your `project.json`? (A gist would be best, if possible). – Ryan Erdmann Dec 06 '16 at 00:07
  • Just putting the build options and publish options here."buildOptions": { "emitEntryPoint": true, "preserveCompilationContext": true, "embed": [ "Views/**" ], "copyToOutput": [ "images\\", "js\\", "styles\\" ] }, "runtimeOptions": { "configProperties": { "System.GC.Server": true } }, "publishOptions": { "include": [ "wwwroot", "Views", "Areas/**/Views", "appsettings.json", "web.config" ] }, – wonderwall Dec 06 '16 at 01:00
  • @RyanErdmann How does the dotnet plugin for TeamCity that you mentioned improve in any way on simply running dotnet * using a command line task? Additionally, can you please complete your answer by showing the MSDeploy command that can be used to deploy a previously _published_ asp.net dotnet core app? This seems to be a major blind-spot with little or no documentation or examples. – JTW Jun 17 '17 at 22:07