Build, test, and deploy .NET Core apps
Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019
Use an Azure Pipeline to automatically build, test, and deploy your .NET Core projects. This article shows you how to do the following tasks:
- Set up your build environment with self-hosted agents.
- Restore dependencies, build your project, and test with the .NET Core task (DotNetCoreCLI@2) or a script.
- Test your code and use the publish code coverage task to publish code coverage results.
- Package and deliver your build output to:
- your pipeline.
- a NuGet feed.
- a
.zip
file to deploy a web app to Azure.
- Set up your build environment with Microsoft-hosted or self-hosted agents.
- Restore dependencies, build your project, and test with the .NET Core task (DotNetCoreCLI@2) or a script.
- Test your code and use the publish code coverage task to publish code coverage results.
- Package and deliver your build output to:
- your pipeline.
- a NuGet feed.
- a
.zip
file to deploy a web app to Azure.
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 .
Open a terminal window.
Create a project directory and navigate to it.
Create a new .NET 8 webapp.
dotnet new webapp -f net8.0
From the same terminal session, run the application locally using the
dotnet run
command from your project directory.dotnet run
Once the application has started, press Ctrl-C to shut it down.
Create a git repo and connect it to GitHub
From the project directory, create a local git repository and commit the application code to the main branch.
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.
- In a browser, go to dev.azure.com and sign in.
- Select your organization.
- Create a new project by selecting New project or Create project if creating the first project in the organization.
- Enter a Project name.
- Select the Visibility for your project.
- Select Create.
- In a browser, go to your Azure DevOps Server.
- Select your collection.
- Create a new project by selecting New project or Create project if creating the first project in the collection.
- Enter a Project name.
- Select the Visibility for your project.
- Select Create.
- In a browser window, sign in to your Azure DevOps Server and select your collection.
- Select New project.
- Enter a project name.
- Optionally, enter a description.
- 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
Sign in to your Azure DevOps organization and go to your project.
Go to Pipelines, and then select New pipeline or Create pipeline if creating your first pipeline.
Do the steps of the wizard by first selecting GitHub as the location of your source code.
You might be redirected to GitHub to sign in. If so, enter your GitHub credentials.
When you see the list of repositories, select your repository.
You might be redirected to GitHub to install the Azure Pipelines app. If so, select Approve & install.
Configure your pipeline
When the Configure tab appears, select Show more and select the ASP.NET Core pipeline template from the list.
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
When you're ready, select Save and run.
Optionally, you can edit the commit message.
Commit the new azure-pipelines.yml file to your repository by selecting Save and run.
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.
- Go to your project and select Pipelines.
- Select Create pipeline or New pipeline if creating the first pipeline for this project.
Select your source
Select your source repository. For this example, use GitHub Enterprise Server.
- Enter the URL for your GitHub account. For example,
https://github.com/<username>
. - Enter your personal access token for your GitHub account.
- Enter a Service connection name. For example,
my-github
. - Select Create.
- Enter the URL for your GitHub account. For example,
Select your GitHub repository.
Configure your pipeline
On the Configure tab, select Show more and select the ASP.NET Core pipeline template from the list.
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
Select Save.
To commit the pipeline YAML file to your repository, edit the commit message as needed and select Save.
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.
Select Save and run.
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:
Go to the position in the YAML file where you want to insert the task.
Select the .NET Core from the task catalog.
Select the restore command from the Command dropdown list.
In the Path to project(s) field, enter the path to your
.csproj
files.Select Add.
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:
Go to the position in the YAML file where you want to insert the task.
Select the .NET Core from the task catalog.
Select the build command from the Command dropdown list.
In the Path to project(s) field, enter the path to your
.csproj
files.Select Add.
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:
Go to the position in the YAML file where you want to insert the task.
Select the Command Line from the task catalog.
Optionally, add a Display name.
Enter the
dotnet build
command with parameters. For example,dotnet build --configuration $(buildConfiguration)
.Enter the path to the
.csproj
file as the working directory.Select Add.
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:
Go to the position in the YAML file where you want to insert the task.
Select .NET Core from the task catalog.
Select the command you want to run.
Configure any options needed.
Select Add.
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:
- Add the .NET Core task and set the following properties:
- Command: custom.
- Path to projects: leave empty.
- Custom command: tool.
- Arguments:
install -g dotnetsay
.
- Command: custom.
- To run the tool, add a Command Line and set the following properties:
- Script:
dotnetsay
.
- Script:
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:
Add the .NET Core task to your build job and set the following properties:
- Command: test.
- Path to projects: Should refer to the test projects in your solution.
- Arguments:
--configuration $(BuildConfiguration) --collect "Code coverage"
.
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:
- Run
dotnet publish --output $(Build.ArtifactStagingDirectory)
on CLI or add the DotNetCoreCLI@2 task with the publish command. - Publish the artifact by using the Publish build artifact (PublishBuildArtifacts@1) task.
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?
Q: Where can I learn more about running tests in my solution?
A: Unit testing in .NET Core projects