Build, test, and deploy .NET Core apps

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019 | TFS 2018

Use an Azure Pipeline to automatically build, test, and deploy your .NET Core projects. This article shows you how to do the following tasks:

Note

For help with .NET Framework projects, see Build ASP.NET apps with .NET Framework.

Prerequisites

  • A GitHub account where you can create a repository. Create one for free.
  • An Azure DevOps organization and project. Create one for free.
  • An ability to run pipelines on Microsoft-hosted agents. You can either purchase a parallel job or you can request a free tier.
  • A GitHub account where you can create a repository. Create one for free.
  • An Azure DevOps collection.
  • An ability to run pipelines on a self-hosted agent with Docker installed and running on the agent's host.

Create your first pipeline

Are you new to Azure Pipelines? If so, then we recommend you try the following section first.

Create a .NET project

If you don't have a .NET project to work with, create a new one on your local system. Start by installing the latest .NET 8.0 SDK .

  1. Open a terminal window.

  2. Create a project directory and navigate to it.

  3. Create a new .NET 8 webapp.

    dotnet new webapp -f net8.0
    
  4. From the same terminal session, run the application locally using the dotnet run command from your project directory.

    dotnet run
    
  5. Once the application has started, press Ctrl-C to shut it down.

Create a git repo and connect it to GitHub

  1. From the project directory, create a local git repository and commit the application code to the main branch.

  2. Connect your local Git repo to a GitHub repo.

Create a DevOps project

Sign-in to Azure Pipelines. After you sign in, your browser goes to https://dev.azure.com/my-organization-name and displays your Azure DevOps dashboard.

Within your selected organization, create a project. If you don't have any projects in your organization, you see a Create a project to get started screen. Otherwise, select the New Project button in the upper-right corner of the dashboard.

  1. In a browser window, sign in to your Azure DevOps Server and select your collection.
  2. Select New project.
  3. Enter a project name.
  4. Optionally, enter a description.
  5. Select Create.

Set up your build environment

Your builds run on self-hosted agents. Make sure that you have the necessary version of the .NET Core SDK and runtime installed on the agents. You can build your .NET Core projects by using the .NET Core SDK and runtime on Windows, Linux, macOS and Docker.

You can install a specific version of .NET SDK by adding the UseDotNet@2 task in your pipeline YAML file or add the task to your pipeline using the classic editor.

Example YAML snippet:

steps:
- task: UseDotNet@2
  inputs:
    version: '8.x'

Your builds run on Microsoft-hosted agents. You can build your .NET Core projects by using the .NET Core SDK and runtime on Windows, Linux, and macOS.

Alternatively, you can use a self-hosted agent. With a self-hosted agent, you can use preview or private SDKs not officially supported by Azure DevOps Services and run incremental builds.

Create your pipeline

You can use the YAML pipeline editor or the classic editor to create your pipeline. To use the classic editor, select Use the classic editor.

Create a new pipeline and select your source
  1. Sign in to your Azure DevOps organization and go to your project.

  2. Go to Pipelines, and then select New pipeline or Create pipeline if creating your first pipeline.

  3. Do the steps of the wizard by first selecting GitHub as the location of your source code.

  4. You might be redirected to GitHub to sign in. If so, enter your GitHub credentials.

  5. When you see the list of repositories, select your repository.

  6. You might be redirected to GitHub to install the Azure Pipelines app. If so, select Approve & install.

Configure your pipeline
  1. When the Configure tab appears, select Show more and select the ASP.NET Core pipeline template from the list.

  2. Examine your new pipeline to see what the YAML does.

You can customize the YAML file for your requirements. For example, you can specify the agent pool or add a task to install different .NET SDK.

Save and run your pipeline
  1. When you're ready, select Save and run.

    Save and run button in a new YAML pipeline

  2. Optionally, you can edit the commit message.

  3. Commit the new azure-pipelines.yml file to your repository by selecting Save and run.

  4. To watch your pipeline in action, select the job in the Jobs section.

Create and run your pipeline

You can create a pipeline by using the YAML pipeline editor or the classic editor.

  1. Go to your project and select Pipelines.
  2. Select Create pipeline or New pipeline if creating the first pipeline for this project.
Select your source
  1. Select your source repository. For this example, use GitHub Enterprise Server.

    1. Enter the URL for your GitHub account. For example, https://github.com/<username>.
    2. Enter your personal access token for your GitHub account.
    3. Enter a Service connection name. For example, my-github.
    4. Select Create.
  2. Select your GitHub repository.

Configure your pipeline
  1. On the Configure tab, select Show more and select the ASP.NET Core pipeline template from the list.

  2. Examine your new pipeline to see what the YAML does.

You can customize the YAML file for your requirements. For example, you can add tasks to install a .NET SDK or to test and publish your project.

Save and run your pipeline
  1. Select Save.

    Screenshot showing the Save and run button in a new YAML pipeline.

  2. To commit the pipeline YAML file to your repository, edit the commit message as needed and select Save.

  3. Select Run to run your pipeline.

To see the build logs as your pipeline runs, select the build number at the top of the page.

  1. Select Save and run.

    Screenshot showing the Save and run button in a new YAML pipeline.

  2. To commit the new azure-pipelines.yml file to your repository, edit the commit message as needed and select Save and run.

To watch your pipeline in action, select the job in the Jobs section.

You now have a working pipeline that's ready for you to customize! Read further to learn some of the common ways to customize your pipeline.

Build environment

Azure Pipelines uses self-hosted agents to build your .NET Core projects. Make sure that you have the necessary version of the .NET Core SDK and runtime installed on the agents. You can build your .NET Core projects by using the .NET Core SDK and runtime on Windows, Linux, macOS and Docker.

For example, to select a pool and agent capabilities in the pipeline YAML file:

You can select the agent pool and agent for your build job. Agents are specified based on their capabilities.

pool:
  name: myPrivateAgents
  demands:
  - agent.os -equals Darwin
  - anotherCapability -equals somethingElse

You can install a specific version of .NET SDK by adding the UseDotNet@2 task in your pipeline. Keep in mind that for agents that run on physical systems, installing SDKs and tools through your pipeline alters the build environment on the agent's host.

To install a newer SDK, set performMultiLevelLookup to true in the following snippet:

steps:
- task: UseDotNet@2
  displayName: 'Install .NET Core SDK'
  inputs:
    version: 8.x
    performMultiLevelLookup: true
    includePreviewVersions: true # Required for preview versions

You can use Azure Pipelines to build your .NET Core projects on Windows, Linux, or macOS without the need to set up infrastructure.

For example, Ubuntu is set here in the pipeline YAML file.

pool:
  vmImage: 'ubuntu-latest' 

See Microsoft-hosted agents for a complete list of images and further configuration examples.

The Microsoft-hosted agents in Azure Pipelines include several preinstalled versions of supported .NET Core SDKs. Microsoft-hosted agents don't include some of the older versions of the .NET Core SDK. They also don't typically include prerelease versions. If you need these versions of the SDK on Microsoft-hosted agents, install them using the UseDotNet@2 task.

For example, to install 5.0.x SDK, add the following snippet:

steps:
- task: UseDotNet@2
  inputs:
    version: '5.x'

Windows agents already include a .NET Core runtime. To install a newer SDK, set performMultiLevelLookup to true in the following snippet:

steps:
- task: UseDotNet@2
  displayName: 'Install .NET Core SDK'
  inputs:
    version: 8.x
    performMultiLevelLookup: true
    includePreviewVersions: true # Required for preview versions

Tip

To save the cost of running the tool installer, you can set up a Linux, macOS, or Windows self-hosted agent. You can also use self-hosted agents to save additional time if you have a large repository or you run incremental builds. A self-hosted agent can also help you in using the preview or private SDKs that aren't officially supported by Azure DevOps or are only available on your corporate or on-premises environments.

Restore dependencies

NuGet is a popular way to depend on code that you don't build. You can download NuGet packages and project-specific tools that are specified in the project file by running the dotnet restore command either through the .NET Core task or directly in a script in your pipeline. For more information, see .NET Core task (DotNetCoreCLI@2).

You can download NuGet packages from Azure Artifacts, NuGet.org, or some other external or internal NuGet repository. The .NET Core task is especially useful to restore packages from authenticated NuGet feeds. If your feed is in the same project as your pipeline, you don't need to authenticate.

This pipeline uses an Azure Artifact feed for dotnet restore in the DotNetCoreCLI@2 task.

trigger:
- main

pool:
  vmImage: 'windows-latest'

steps:
- task: UseDotNet@2
  displayName: 'Install .NET Core SDK'
  inputs:
    version: 8.x
    performMultiLevelLookup: true
    includePreviewVersions: true # Required for preview versions

variables:
  buildConfiguration: 'Release'

steps:
- task: DotNetCoreCLI@2
  inputs:
    command: 'restore'
    feedsToUse: 'select'
    vstsFeed: 'my-vsts-feed' # A series of numbers and letters

- task: DotNetCoreCLI@2
  inputs:
    command: 'build'
    arguments: '--configuration $(buildConfiguration)'
  displayName: 'dotnet build $(buildConfiguration)'

The dotnet restore command uses the NuGet.exe packaged with the .NET Core SDK and can only restore packages specified in the .NET Core project .csproj files.

If you also have a Microsoft .NET Framework project in your solution or use package.json to specify your dependencies, use the NuGet task to restore those dependencies.

- task: NuGetCommand@2
  inputs:
    command: 'restore'
    restoreSolution: '**/*.sln'
    feedsToUse: 'select'

In .NET Core SDK version 2.0 and newer, packages are restored automatically when running commands such as dotnet build. However, you would still need to use the .NET Core task to restore packages if you use an authenticated feed.

Your builds can fail because of connection issues when you restore packages from NuGet.org. You can use Azure Artifacts with upstream sources to cache the packages. The credentials of the pipeline are automatically used when it connects to Azure Artifacts. These credentials are typically derived from the Project Collection Build Service account. To learn more about using Azure Artifacts to cache your NuGet packages, see Connect to Azure Artifact feeds.

To specify a NuGet repository, put the URL in a NuGet.config file in your repository. If your feed is authenticated, manage its credentials by creating a NuGet service connection in the Services tab under Project Settings.

When you use Microsoft-hosted agents, you get a new machine every time you run a build, which restores the packages with each run. Restoration can take a significant amount of time. To mitigate, you can either use Azure Artifacts or a self-hosted agent with the benefit of using the package cache.

For more information about NuGet service connections, see publish to NuGet feeds.

Restore packages from an external feed

Do the following to restore packages from an external feed.

You can add the restore command to your pipeline using the YAML pipeline editor by directly inserting the following snippet into your azure-pipelines.yml file or using the task assistant to add the .NET Core task.

# do this before your build tasks
steps:
- task: DotNetCoreCLI@2
  displayName: Restore
  inputs:
    command: restore
    projects: '**/*.csproj'
    feedsToUse: config
    nugetConfigPath: NuGet.config    # Relative to root of the repository
    externalFeedCredentials: <Name of the NuGet service connection>

Replace the <placeholder> with your service connection name.

To use the task assistant:

To add a build task using the task assistant, do the following steps:

  1. Go to the position in the YAML file where you want to insert the task.

  2. Select the .NET Core from the task catalog.

  3. Select the restore command from the Command dropdown list.

  4. In the Path to project(s) field, enter the path to your .csproj files.

  5. Select Add.

  6. Select Save to commit the change.

Note

Make sure the custom feed is specified in your NuGet.config file and that credentials are specified in the NuGet service connection.

Build your project

Build your .NET Core projects by running the dotnet build command. You can add the command to your pipeline as a command line script or by using the .NET Core task.

.NET Core build using the .NET Core task

YAML example to build using the DotNetCoreCLI@2 task:

steps:
- task: DotNetCoreCLI@2
  displayName: Build
  inputs:
    command: build
    projects: '**/*.csproj'
    arguments: '--configuration $(buildConfiguration)' # Update this to match your needs

You can add a build task using the YAML pipeline editor by directly editing the file or adding the .NET Core task using the task assistant.

To add a build task using the task assistant, do the following steps:

  1. Go to the position in the YAML file where you want to insert the task.

  2. Select the .NET Core from the task catalog.

  3. Select the build command from the Command dropdown list.

  4. In the Path to project(s) field, enter the path to your .csproj files.

  5. Select Add.

  6. Select Save to commit the change.

.NET Core build using command line script

YAML example to build using dotnet build as a script:

steps:
- script: dotnet build --configuration $(buildConfiguration)
  displayName: 'dotnet build $(buildConfiguration)'

You can add a build task using the YAML pipeline editor by directly editing the file or adding the Command Line task.

Use the following steps to add the Command Line task:

  1. Go to the position in the YAML file where you want to insert the task.

  2. Select the Command Line from the task catalog.

  3. Optionally, add a Display name.

  4. Enter the dotnet build command with parameters. For example, dotnet build --configuration $(buildConfiguration).

  5. Enter the path to the .csproj file as the working directory.

  6. Select Add.

  7. Select Save to commit the change.

Add .NET SDK commands to your pipeline

You can add .NET SDK commands to your project as a script or using the .NET Core task. The .NET Core task (DotNetCoreCLI@2) task allows you to easily add dotnet CLI commands to your pipeline. You can add .NET Core tasks by editing your YAML file or using the classic editor.

Add a .NET CLI command using the .NET Core task

To add a .NET Core CLI command using the YAML pipeline editor, do the following steps:

  1. Go to the position in the YAML file where you want to insert the task.

  2. Select .NET Core from the task catalog.

  3. Select the command you want to run.

  4. Configure any options needed.

  5. Select Add.

  6. Select Save to commit the change.

Add a .NET Core CLI command using a script

You can add .NET Core CLI commands as a script in your azure-pipelines.yml file.

Example:


steps:
# ...
- script: dotnet test <test-project> 

Install a tool

To install a .NET Core global tool like dotnetsay in your build running on Windows, take the following steps:

  1. Add the .NET Core task and set the following properties:
    • Command: custom.
      • Path to projects: leave empty.
    • Custom command: tool.
    • Arguments: install -g dotnetsay.
  2. To run the tool, add a Command Line and set the following properties:
    • Script: dotnetsay.

Run your tests

When you have test projects in your repository, you can use the .NET Core task to run unit tests by using testing frameworks like MSTest, xUnit, and NUnit. The test project must reference Microsoft.NET.Test.SDK version 15.8.0 or higher. Test results are automatically published to the service. These results are available to you in the build summary and can be used for troubleshooting failed tests and test-timing analysis.

You can add a test task to your pipeline using the DotNetCoreCLI@2 task or add the following snippet to your azure-pipelines.yml file:

steps:
# ...
# do this after other tasks such as building
- task: DotNetCoreCLI@2
  inputs:
    command: test
    projects: '**/*Tests/*.csproj'
    arguments: '--configuration $(buildConfiguration)'

When using the .NET Core task editor, set Command to test and Path to projects should refer to the test projects in your solution.

Alternatively, you can run the dotnet test command with a specific logger and then use the Publish Test Results task:

steps:
# ...
# do this after your tests have run
- script: dotnet test <test-project> --logger trx
- task: PublishTestResults@2
  condition: succeededOrFailed()
  inputs:
    testRunner: VSTest
    testResultsFiles: '**/*.trx'

Collect code coverage

When you're building on the Windows platform, code coverage metrics can be collected by using the built-in coverage data collector. The test project must reference Microsoft.NET.Test.SDK version 15.8.0 or higher.

When you use the .NET Core task to run tests, coverage data is automatically published to the server. The .coverage file can be downloaded from the build summary for viewing in Visual Studio.

Add the following snippet to your azure-pipelines.yml file:

steps:
# ...
# do this after other tasks such as building
- task: DotNetCoreCLI@2
  inputs:
    command: test
    projects: '**/*Tests/*.csproj'
    arguments: '--configuration $(buildConfiguration) --collect "Code coverage"'

To add the .NET Core task through the task editor:

  1. Add the .NET Core task to your build job and set the following properties:

    1. Command: test.
    2. Path to projects: Should refer to the test projects in your solution.
    3. Arguments: --configuration $(BuildConfiguration) --collect "Code coverage".
  2. Ensure that the Publish test results option remains selected.

If you choose to run the dotnet test command, specify the test results logger and coverage options. Then use the Publish Test Results task:

steps:
# ...
# do this after your tests have run
- script: dotnet test <test-project> --logger trx --collect "Code coverage"
- task: PublishTestResults@2
  inputs:
    testRunner: VSTest
    testResultsFiles: '**/*.trx'

Collect code coverage metrics with Coverlet

If you're building on Linux or macOS, you can use Coverlet or a similar tool to collect code coverage metrics.

You can publish code coverage results to the server with the Publish Code Coverage Results (PublishCodeCoverageResults@1) task. The coverage tool must be configured to generate results in Cobertura or JaCoCo coverage format.

To run tests and publish code coverage with Coverlet, do the following tasks:

  • Add a reference to the coverlet.collector NuGet package.

  • Add the following snippet to your azure-pipelines.yml file:

    - task: UseDotNet@2
      inputs:
        version: '8.x'
        includePreviewVersions: true # Required for preview versions
    
    - task: DotNetCoreCLI@2
      displayName: 'dotnet build'
      inputs:
        command: 'build'
        configuration: $(buildConfiguration)
    
    - task: DotNetCoreCLI@2
      displayName: 'dotnet test'
      inputs:
        command: 'test'
        arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura'
        publishTestResults: true
        projects: 'MyTestLibrary' # update with your test project directory
    
    - task: PublishCodeCoverageResults@1
      displayName: 'Publish code coverage report'
      inputs:
        codeCoverageTool: 'Cobertura'
        summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'
    

Package and deliver your code

You can publish your build artifacts by:

  • Publishing to Azure Pipelines.
  • Publishing packages to Azure Artifacts.
  • Creating a NuGet package and publish to your NuGet feed.
  • Creating a .zip archive to deploy your web app.

Publish artifacts to Azure Pipelines

To publish the output of your .NET build to your pipeline, do the following tasks:

  • Run dotnet publish --output $(Build.ArtifactStagingDirectory) on the .NET CLI or add the DotNetCoreCLI@2 task with the publish command.
  • Publish the artifact by using the Publish Pipeline Artifact task.

Add the following snippet to your azure-pipelines.yml file:

steps:

- task: DotNetCoreCLI@2
  inputs:
    command: publish
    publishWebProjects: True
    arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
    zipAfterPublish: True

# this code takes all the files in $(Build.ArtifactStagingDirectory) and uploads them as an artifact of your build.
- task: PublishPipelineArtifact@1
  inputs:
    targetPath: '$(Build.ArtifactStagingDirectory)' 
    artifactName: 'myWebsite'

Note

The DotNetCoreCLI@2 task has a publishWebProjects input that is set to true by default. This task publishes all web projects in your repo by default. You can find more help and information in the open source task on GitHub.

To copy more files to the build directory before publishing, use the Copy Files (CopyFile@2) task.

To publish the output of your .NET build to your pipeline, do the following tasks:

Add the following snippet to your azure-pipelines.yml file to publish your build artifacts as a .zip file:

steps:

- task: DotNetCoreCLI@2
  inputs:
    command: publish
    publishWebProjects: True
    arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
    zipAfterPublish: True

# this code takes all the files in $(Build.ArtifactStagingDirectory) and uploads them as an artifact of your build.
- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'drop'

For more information, see Publish and download build artifacts.

Publish to a NuGet feed

To create a NuGet package and publish it to your NuGet feed, add the following snippet:

steps:
# ...
# do this near the end of your pipeline in most cases
- script: dotnet pack /p:PackageVersion=$(version)  # define version variable elsewhere in your pipeline
- task: NuGetAuthenticate@1
  inputs:
    nuGetServiceConnections: '<Name of the NuGet service connection>'
- task: NuGetCommand@2
  inputs:
    command: push
    nuGetFeedType: external
    publishFeedCredentials: '<Name of the NuGet service connection>'
    versioningScheme: byEnvVar
    versionEnvVar: version

Note

The NuGetAuthenticate@1 task doesn't support NuGet API key authentication. If you're using a NuGet API key, use the NuGetCommand@2 task with the command input set to push with the --api-key argument. For example, dotnet nuget push --api-key $(NuGetApiKey).

For more information about versioning and publishing NuGet packages, see publish to NuGet feeds.

Publish a NuGet package to Azure Artifacts

You can publish your NuGet packages to your Azure Artifacts feed by using the NuGetCommand@2 to push to your Azure Artifact feed. For example, see Publish NuGet packages with Azure Pipelines.

Deploy a web app

To create a .zip file archive that's ready to publish to a web app, add the following snippet:

steps:
# ...
# do this after you've built your app, near the end of your pipeline in most cases
# for example, you do this before you deploy to an Azure web app on Windows
- task: DotNetCoreCLI@2
  inputs:
    command: publish
    publishWebProjects: True
    arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
    zipAfterPublish: True

To publish this archive to a web app, see Azure Web Apps deployment.

Build an image and push to container registry

You can also build an image for your app and push it to a container registry.

Publish symbols

You can use the PublishSymbols@2 task to publish symbols to an Azure Artifacts symbol server or a file share.

For example, to publish symbols to a file share, add the following snippet to your azure-pipelines.yml file:

- task: PublishSymbols@2
  inputs:
    SymbolsFolder: '$(Build.SourcesDirectory)'
    SearchPattern: '**/bin/**/*.pdb'
    IndexSources: true
    PublishSymbols: true
    SymbolServerType: 'FileShare' 
    SymbolsPath: '\\server\shareName'

When using the classic editor, select Index sources publish symbols from the task catalog to add to your pipeline.

For more information, see Publish symbols.

Troubleshoot

If you can build your project on your development machine, but you're having trouble building it on Azure Pipelines, explore the following potential causes and corrective actions:

  • Prerelease versions of the .NET Core SDK aren't installed on Microsoft-hosted agents. After a new version of the .NET Core SDK is released, it can take a few weeks to roll out to all the Azure Pipelines data centers. You don't have to wait for this rollout to complete. You can use the Use .NET Core task to install the .NET Core SDK version you want on Microsoft-hosted agents.
  • Check the .NET Core SDK versions and runtime on your development machine and make sure they match the agent. You can include a command-line script dotnet --version in your pipeline to print the version of the .NET Core SDK. Either use the .NET Core Tool Installer to deploy the same version on the agent, or update your projects and development machine to the newer version of the .NET Core SDK.

  • You might be using some logic in the Visual Studio IDE that isn't encoded in your pipeline. Azure Pipelines runs each of the commands you specify in the tasks one after the other in a new process. Examine the logs from the pipelines build to see the exact commands that ran as part of the build. Repeat the same commands in the same order on your development machine to locate the problem.

  • If you have a mixed solution that includes some .NET Core projects and some .NET Framework projects, you should also use the NuGet task to restore packages specified in packages.config files. Add the MSBuild or Visual Studio Build task to build the .NET Framework projects.

  • Your builds might fail intermittently while restoring packages: either NuGet.org is having issues or there are networking problems between the Azure data center and NuGet.org. You can explore whether using Azure Artifacts with NuGet.org as an upstream source improves the reliability of your builds, as it's not in our control.

  • Occasionally, a when new version of the .NET Core SDK or Visual Studio is rolled out, your build might break. For example, a newer version or feature of the NuGet tool is shipped with the SDK could break your build. To isolate this issue, use the .NET Core Tool Installer task to specify the version of the .NET Core SDK used in your build.

FAQ

Q: Where can I learn more about Azure Artifacts?

A: Package Management in Azure Artifacts

Q: Where can I learn more about .NET Core commands?

A: .NET Core CLI tools

Q: Where can I learn more about running tests in my solution?

A: Unit testing in .NET Core projects

Q: Where can I learn more about tasks?

A: Build and release tasks