In this article we going to learn how to deploy a .NET API to Azure App Service using Azure DevOps pipeline so your deployments are automated and consistent every time.
Deploying manually from Visual Studio works fine when you're the only developer. But the moment you have a team, or you're deploying more than once a week, manual deployment becomes a problem. Someone forgets to build in Release mode, someone deploys from a feature branch instead of main, something breaks in production and nobody knows exactly what got deployed.
Azure DevOps pipelines fix all of this. Every push to your main branch triggers a build, runs tests, and deploys to Azure automatically. No manual steps, no human mistakes. Same process every single time.
This tutorial shows how to:
- Set up an Azure DevOps project and connect it to your code repo
- Create a build pipeline that compiles and publishes your .NET API
- Connect Azure DevOps to your Azure App Service using a service connection
- Create a release pipeline that deploys to Azure App Service automatically
- Add environment variables and connection strings during deployment
- Set up branch-based deployment so only main branch deploys to production
Why Azure DevOps Pipeline
You could also use GitHub Actions if your code is on GitHub. Both are solid options. But if you're already using Azure for hosting, Azure DevOps fits naturally — same Microsoft ecosystem, tight integration with Azure App Service, and the service connections are straightforward to set up.
Also for teams using Azure Boards for task tracking, Azure Repos for code, and Azure Artifacts for packages — having your pipelines in the same place just makes sense.
What You Need Before Starting
Make sure these are ready :
- An Azure account with an App Service already created. If you haven't done that yet, go through the basic App Service setup first.
- A .NET 6, 7, or 8 Web API project pushed to a repo — Azure Repos, GitHub, or Bitbucket all work with Azure DevOps pipelines.
- An Azure DevOps account. Free tier at dev.azure.com — includes 1800 free pipeline minutes per month which is plenty to start.
Step 1 : Create an Azure DevOps Project
Go to dev.azure.com and sign in. Click New Project. Give it a name, set visibility to Private, and click Create.
If your code is in Azure Repos, push your .NET project there. If it's on GitHub, you'll connect it during pipeline creation — Azure DevOps supports both.
Step 2 : Create a Service Connection to Azure
This is what allows Azure DevOps to deploy to your Azure subscription. Without this the pipeline can't talk to Azure.
In your Azure DevOps project, go to :
Project Settings → Service connections → New service connection
Select Azure Resource Manager and click Next.
Choose Service principal (automatic) — this is the easiest option. Azure DevOps creates a service principal in your Azure AD automatically.
Select your subscription, choose the Resource Group where your App Service lives, give the connection a name like azure-production, and click Save.
Azure DevOps will ask you to authorize it. Sign in with your Azure account when prompted. Once done, the service connection shows up in the list.
Step 3 : Create the Build Pipeline (YAML)
In Azure DevOps, go to Pipelines → New Pipeline.
Select where your code is — Azure Repos Git, GitHub, Bitbucket, etc. Select your repository.
Azure DevOps will suggest some templates. Select .NET or ASP.NET Core from the list. It'll generate a starter YAML file.
Replace the contents with this pipeline definition :
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
variables:
buildConfiguration: 'Release'
dotnetVersion: '8.0.x'
steps:
- task: UseDotNet@2
displayName: 'Install .NET SDK'
inputs:
version: $(dotnetVersion)
- task: DotNetCoreCLI@2
displayName: 'Restore NuGet packages'
inputs:
command: 'restore'
projects: '**/*.csproj'
- task: DotNetCoreCLI@2
displayName: 'Build project'
inputs:
command: 'build'
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration) --no-restore'
- task: DotNetCoreCLI@2
displayName: 'Run tests'
inputs:
command: 'test'
projects: '**/*Tests/*.csproj'
arguments: '--configuration $(buildConfiguration) --no-build'
- task: DotNetCoreCLI@2
displayName: 'Publish API'
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: true
- task: PublishBuildArtifacts@1
displayName: 'Upload artifact'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'
Let me explain what each section does.
trigger — pipeline runs only when code is pushed to main branch. Pushes to other branches don't trigger this pipeline.
pool — uses Microsoft-hosted Ubuntu agent. Free, no setup needed.
variables — set .NET version and build configuration in one place. Change dotnetVersion here if you're on .NET 6 or 7.
The steps run in order — install SDK, restore packages, build, run tests, publish, upload artifact. If tests fail, the pipeline stops and nothing gets deployed. That's the whole point.
The published artifact is a zip file uploaded to Azure DevOps as "drop". The release pipeline picks this up in the next step.
Click Save and Run. First run takes a couple of minutes. Watch the logs — each step shows its output so you can see exactly what's happening.
Step 4 : Create the Release Pipeline
Build pipeline creates the artifact. Release pipeline deploys it to Azure.
Go to Pipelines → Releases → New pipeline.
Click Add an artifact. Select Build as the source type. Select your project and the build pipeline you just created. Set Default version to Latest. Click Add.
Now click the lightning bolt icon on the artifact — this enables Continuous deployment trigger. Every time the build pipeline completes, the release pipeline runs automatically.
Click Add a stage on the right side. Select Deploy a Node.js app to Azure App Service — wait, wrong one. Select Deploy an ASP.NET app to Azure App Service or just start with an Empty job.
Click on the stage name and rename it to Production.
Click 1 job, 0 tasks to open the stage configuration.
Click the + button to add a task. Search for Azure App Service Deploy and add it.
Configure the task :
- Azure subscription — select the service connection you created in Step 2 (
azure-production) - App Service type — Web App on Windows (or Linux, match what you created)
- App Service name — select your App Service from the dropdown
- Package or folder — click the
...button and browse to the zip file in the drop artifact. It'll be something like$(System.DefaultWorkingDirectory)/_YourBuildPipeline/drop/*.zip
Click Save and then Create release → Create to trigger a manual release and test it.
Step 5 : Add Environment Variables During Deployment
Your API needs connection strings and config values in production. You can set these as pipeline variables and inject them into the App Service during deployment.
In the release pipeline, click Variables tab at the top.
Add your variables :
DB_CONNECTION_STRING = Server=your-server.database.windows.net;Database=yourdb;...
JWT_SECRET = your-secret-key
ASPNETCORE_ENVIRONMENT = Production
Mark sensitive values like connection strings and secrets as secret by clicking the padlock icon. Secret variables are masked in logs.
Now go back to the Azure App Service Deploy task. Scroll down to Application and Configuration Settings. In the App settings field, add :
-ASPNETCORE_ENVIRONMENT Production -JWT_SECRET $(JWT_SECRET)
For connection strings, use the Connection Strings field :
-DefaultConnection "$(DB_CONNECTION_STRING)" SQLServer
These get written to the App Service configuration during deployment, overriding whatever was set manually in the portal. Good habit — keep all config in the pipeline, not scattered across portal settings that no one can track.
Step 6 : Add a Staging Environment Before Production
Deploying straight to production is fine for small projects. But a better setup is to deploy to a staging slot first, test it, then swap to production.
In your release pipeline, add another stage before Production. Call it Staging.
In the Staging stage, configure the same Azure App Service Deploy task but point it to your staging slot :
- App Service name — same App Service
- Deploy to Slot — check this box
- Slot — staging
Add an Agentless job between Staging and Production stages. Add a Manual intervention task. This pauses the pipeline after staging deploy and waits for someone to approve before continuing to production.
Now the flow is :
- Push to main
- Build pipeline runs — compile, test, publish
- Release pipeline deploys to staging slot automatically
- Pipeline pauses — someone checks staging, clicks Approve
- Pipeline deploys to production slot
If something looks wrong on staging, click Reject instead. Production stays untouched.
Common Issues
Pipeline fails at Restore step
Usually a private NuGet feed issue. If you're using Azure Artifacts for packages, make sure the build agent has access. Add an Authenticate to NuGet feeds task before the restore step.
App Service Deploy task fails with 403
Your service connection doesn't have permission to deploy to the App Service. Go to Azure portal → your App Service → Access control (IAM) → add the service principal as Contributor.
App deploys but API returns 500
Build succeeded but runtime config is wrong. Check App Service logs in Azure portal immediately after deployment. Usually a missing connection string or wrong ASPNETCORE_ENVIRONMENT value.
Tests are skipped or not found
The test projects pattern **/*Tests/*.csproj needs to match your folder structure. If your test project is named differently, update the glob pattern to match.
Summary
You learned how to deploy a .NET API to Azure App Service using Azure DevOps pipeline. You covered :
- Creating a service connection between Azure DevOps and your Azure subscription
- Writing a YAML build pipeline that restores, builds, tests, and publishes your .NET API
- Creating a release pipeline with the Azure App Service Deploy task
- Enabling continuous deployment trigger so every merge to main deploys automatically
- Injecting environment variables and connection strings during deployment
- Adding a staging environment with manual approval gate before production
Once this is set up, your deployment process is completely automated. Push to main, pipeline runs, API is live. No manual steps, no missed configurations, no "works on my machine" problems.
I hope you like this article...
Happy coding! 🚀
Tags:
0 Comments