DEV Community

Cover image for Deploying an ASP.NET Core Web API to Azure with App Service and Azure SQL Database
Promise
Promise

Posted on

Deploying an ASP.NET Core Web API to Azure with App Service and Azure SQL Database

Introduction

A common habit among developers is working on multiple personal projects, confirming they work and moving on to the next. However, as the technology landscape evolves, so does the expectation. Local development is no longer enough.

The question developers now need to ask is "How can I get my projects running in the real world?"

Recognising this and asking that question is where growth begins. A project working fine on your machine does not guarantee it will behave the same way in production. In addition, the recent development in the technology space has highlighted the need for developers to gain exposure to the world of cloud development in order to broaden their skillset.

This article focuses on Microsoft Azure and walks you through deploying a real-world ASP.NET Core Web API using two core resources, Azure App Service and Azure SQL Database.

We will be working with Eventify, a full-featured event management backend service I built, so every step here reflects decisions made on the deployment of the project.

By the end of this detailed guide, you will have:

  • Your ASP.NET Core Web API running live on Azure App Service
  • A production database hosted on Azure SQL Database
  • Application secrets configured securely on Azure
  • An automated deployed pipeline via GitHub Actions

Prerequisites

  • An active Azure account
  • .NET SDK installed
  • Visual Studio
  • Azure CLI installed (installation guide)
  • Basic familiarity with ASP.NET Core and Entity Framework Core

Overview of the project behind this tutorial

This guide is backed by a real project: Eventify, an event management backend service I built and deployed to Azure. It covers venue bookings,ticketing, payments, authentication and more. However, I will not be going deep into how Eventify works, but whatever API you are deploying, the steps highlighted in this guide apply.

Provisioning Azure Resources

With your project ready for deployment, the first step is to set up the cloud infrastructure it will run on. We will provision two core Azure resources:

  • Azure SQL Database: for the data layer
  • Azure App Service: to host the API

Overview of the Azure Portal

Head over to Azure. The portal is your central workspace for creating and managing everything in Azure. For this tutorial, the key area you will be working with is the Create a resource button. This is what will be used to spin up any new Azure service.

Azure dashboard

It is important to understand the concept of Resource and Resource Group early.

  • Resource: Azure resources are basic building block of Azure. Anything you create, provision, deploy, etc. in Azure is a resource.
  • Resource Group: This is a container for resources. They exist to help manage resources together. Grouping your resources together make it easier to manage, monitor and when the time comes, clean them all up together.

Create a resource group before anything else:

  1. In the portal, search for resource groups in the top search bar or among the listed Azure services on the dashboard
  2. Click Create
  3. Give it a meaningful name (e.g my-api-rg) and choose a region close to your users N.B Depending on your Azure subscription, you may be restricted from creating resources in certain regions
  4. Click Review + Create, then Create

This newly created resource group will be used for creating everything that follows.

Creating the Azure SQL Database

Azure SQL Database is a fully managed relational database service. Microsoft handles backups, patching and availability, so you can focus on your schema. The following are steps involved in setting it up.

Step 1. Create a SQL Server

Just like our usual databases that reside in a server, Azure SQL Database lives inside a logical SQL Server, so we create the server first

  1. In the portal search bar, type SQL Server and select it

Search for SQL Server

Click Create
Create a new Azure SQL Server

2.Fill in the following:

  • Resource group - select the group you just created

  • Server name - this must be globally unique (e.g my-api-sql-server). Azure appends .database.windows.net to this automatically

  • Region- match the region you chose for your resource group

  • Authentication method: select Use SQL authentication

  • Server admin login: choose a username (e.g sql-server-admin)

  • Password: choose a strong password and note it down, you will need it shortly

  • Click Review + Create then Create

Step 2 - Create the database

Once the server has been provisioned

  1. Navigate to the newly created SQL server
  2. From the Overview section, click Create database
  3. You can fill in the following details:
  • Database name: e.g my-api-db

  • Compute + storage: for a development or early production deployment, the Basic or General Purpose — Serverless tier is a cost-effective choice. Serverless pauses automatically during inactivity and you only pay for what you use

  • Click Review + Create, then Create

N.B: The serverless tier is ideal for projects like Eventify at an early stage as it keeps costs low while there is no significant load. We can scale up later without any changes to our application code

Once the database has been provisioned, grab your connection string now while you're here as you'll need it later when configuring the App Service:

  1. Navigate to your newly created SQL Database resource
  2. In the left-hand menu, select Settings → Connection strings
  3. Copy the ADO.NET (SQL Authentication) string and paste it somewhere safe, a notepad or notes app is fine for now
    Getting connection string

  4. Note the {your_password} placeholder in the string, you'll replace this with the admin password you set when creating the SQL Server

Step 3 - Configure Firewall Rules

By default, Azure SQL blocks all incoming connections including your own machines, so you need to explicitly allow access at the SQL Server level. These rules apply across all databases on that server. To do this, follow the following steps

  1. Navigate to your SQL server resource N.B: You can easily locate this under the Recent section on Azure's home page
  2. Under Security, select Networking
  3. Under Firewall rules, click Add your client IPv4 address. This allows your current machine to connect, which you'll need for running migrations later
  4. Ensure to check Allow Azure services and resources to access this server. This allows your App Service to reach the database once deployed
  5. Click Save

Configuring Firewall rules

Creating the Azure App Service

Azure App service is a fully managed platform for hosting web applications, APIs and mobile backends. It is a PaaS (Platform as a Service), meaning you do not manage the underlying infrastructure (server etc), you just deploy your code and Azure handles the rest.

Step 1 - Create the App Service Plan

  1. Search for App Services in the portal and click Create. A dropdown will appear with three options:
  • Web App: This creates just the App Service resource on its own. This is what we want since we have already provisioned our database separately

  • Web App + Database: provisions both an App Service and a database together in one flow. This is useful for greenfield projects, but since we already have our Azure SQL database set up already, we don't need it here

  • WordPress on App Service: A pre-configured setup specifically for Word Press sites

Select Web App

2.On the creation form, fill in the following:

  • Resource group: select your resource group

  • Name: this becomes part of your public URL (e.g my-api -> my-api.azurewebsites.net), so it must be globally unqiue. Leave the Secure unique default hostname toggle on, it is a security improvement that prevents hostname guessing

  • Publish: select code

  • Runtime stack: select .NET 8(or whichever version your project targets)

  • Operating System: Linux or Windows both support ASP.NET Core. Linux tends to be slightly cheaper at the same tier

  • Region: match the region you chose for your other resources

3.Under Pricing plans, select an existing App Service Plan or let Azure create a new one. For the pricing tier, B1 (Basic) is the minimum recommended for a real deployment. The Free tier (F1) puts your app to sleep during inactivity, which is a poor experience for an API
4.Under Zone redundancy, leave this as Disabled for now. Zone redundancy runs multiple instances of your app across availability zones for high availability, which is useful at scale, but unnecessary overhead at this stage
5.You'll notice tabs at the bottom of the form "Next: Database and others". Since we've already provisioned our database separately, you can skip straight to select Review + Create
6.Click Create

Allow the resource to be provisioned and after getting the notification that the deployment is complete, navigate to your Web App. You should see a Default domain link on the Overview section confirming the App Service is live and ready to receive a deployment.

At this point, you now have all the Azure infrastructure in place. Next, we will wire your API up to those resources by connecting the database, securing the app secrets and preparing the application for its first deployment

Configuring the Application for deployment to Azure

With the infrastructure now provisioned, we need to connect our application to it. This section covers securing the application secrets in Azure and ensuring database schema gets applied before requests hits the API endpoints.

Overview of how Azure App Settings work

Your ASP.NET Core applications read configurations from appsettings.json at runtime. When the application is deployed to Azure App Service, any values you add under Configuration -> App settings in the portal will immediately override their correspoinding values in appsettings.json at runtime without touching your codebase.
What this means is that your appsettings.json can now safely hold local development values and Azure will swap them out with production values at runtime. The benefit of this is that your app secrets never need to be committed to source control.

N.B: For nested configuration keys, Azure flattens them by using a double underscore "__" as the separator. For example:

// A value structured like this in appsettings.json:
{
   "JwtSettings": {
       "SecretKey": "your-secret-key"
   }
}

// Would be entered in Azure app settings as:
JwtSettings__SecretKey
Enter fullscreen mode Exit fullscreen mode

To add your app secrets in the created Azure web-app resource:

  1. Navigate to Environment Variables tab under Settings section.
  2. Our focus will be on the App Settings tab (next to Connection string).

Setting app secrets

Add each of your secrets there, using the double underscore separator for nested key.
Your project will have its own set of secrets, anything sensitive that lives in appsettings.json or appsettings.Development.json locally should be moved to Azure App Settings in Production.

The Deployment slot setting checkbox controls whether this value stays pinned to the current slot if you ever use Azure's deployment slot feature (e.g. staging and production slots). For settings that should be environment-specific like your database connection string, JWT secret, or API keys, check this box. That way, if you swap a staging slot into production, each slot keeps its own version of those secrets rather than swapping them over too. If you're not using deployment slots at all right now, leaving it unchecked is fine but it's a good habit to check it for any sensitive or environment-specific setting.

Setting project app secrets

  1. Once you have added all your settings, click Apply, then Confirm. Azure will restart your App Service to pick up the new configuration.

Setting the database connection string

Next, we will set the database connection string

  1. Navigate to the left-hand menu of the created App service resource, select Settings -> Environment Variables
  2. Click on the Connection string tabs, then click Add Adding database connection string
  3. Fill in the following information in the pop-up modal:
  • Name: this must match the key in your appsettings.json file. For example, if your connection string is defined as this:
{
   "ConnectionStrings": {
      "DefaultConnection": "..."
  }
}
Enter fullscreen mode Exit fullscreen mode

Then Name should be DefaultConnection

Adding new app secret

  • Value: Paste in the ADO.NET connection string you copied earlier when the database was provisioned. Remember to replace the {your_password} placeholder with your SQL Server admin password
  • Type: select SQL Server

4.Click Apply

Now that we have set the app secrets and database connection string, we will move on to the next section.

Applying Database Migration

There are a couple of ways we can apply EF Core migrations against Azure SQL. However for simplicity, I will suggest applying migrations programmatically at app startup.

Copy the code below to your project Program.cs before app.Run().

using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    await db.Database.MigrateAsync();
}
Enter fullscreen mode Exit fullscreen mode

_N.B: Replace AppDbContext with the name of your own DbContext class if it differs.

On first startup, the application connects to Azure SQL and applies any pending migrations automatically. On subsequent startup, if it finds nothing pending, it moves on.

Another alternative is running migrations manually from your terminal before deploying:

dotnet ef database update --connection "your-azure-sql-connection-string"
Enter fullscreen mode Exit fullscreen mode

Either approach works. Whichever you choose, ensure your local machine's IP is in the SQL Server firewall rules (which we set up in the previous section) before attempting to run migrations from your terminal.

With migrations covered in addition to the necessary configurations done earlier, we will do the first deployment and verify everything is running end to end.

Deploying to Azure App Service

There are different ways to deploy an ASP.NET Core Web API to Azure App Service. This section first examines the straightforward manual option using Visual Studio's publish option. However, if you prefer to skip manual deployment and set an automated pipeline from the start, you can skip to the next section where we cover that in full using GitHub Actions (an approach I personally used for Eventify)

Option A: Deployment via Visual Studio

Visual Studio's publish wizard is the quickest way to get a one-off deployment without any deployment pipeline setup. It is a very good option especially for a first deployment or for just verifying things work quickly.
To use this approach, follow the steps highlighted below:

  1. Open your project in Visual Studio and right-click the project in Solution Explorer
  2. Select Publish, Publish option on Visual Studio
  3. Select New Profile
  4. On the Publish target screen that appears, select Azure and click Next
  5. Select Azure App Service (Windows) or Azure App Service (Linux) depending on the OS you chose when creating the App Service resource, then click Next
  6. Sign in with your Azure Account if prompted. Visual Studio will list all your existing App service resources. Select the one you created earlier (e.g my-api) and click Next
  7. On the API Management step, select Skip this step and click Next
  8. On the Deployment type step, select Publish (generates pubxml file), then click Finish
  9. Click Publish

Visual Studio will build your project in Release configuration, package it, and push it to Azure. You'll see the output log in the publish window.

Verifying the deployment

It is important to confirm that everything is working correctly end to end and not just that the app started but that the app is also communicating with the database and all configurations defined are picked up. To test via Swagger UI, navigate to https://your-app-name.azurewebsites.net/swagger in your browser. If swagger loads and your endpoints are listed, the API is running. Try to hit any of the endpoints, preferably a simple GET endpoint that reads database to confirm both the API and the database connection are working.

NOTE: If you have Swagger disabled in production as shown below if the code is present in your Program.cs file, you can remove the condition then redeploy the application or use a REST client like Postman instead to test the API.

Verifying deployment

Checking logs if something goes wrong

If the deployment succeeded but the API isn't responding as expected, the first place to look is the application logs. In the Azure Portal, navigate to your App Service and select Log stream from the left-hand menu. This streams live logs from your running application and is usually the fastest way to spot a misconfigured secret, a failed migration, or a missing connection string.

Drawback with manual deployment

While the Visual Studio publish wizard is convenient for a quick deploy, every deployment requires manual action. There is no automated testing to ensure the application builds correctly before the push goes out. For project you are actively developing, a CI/CD pipeline is a better long-term approach to.

Automating Deployment with GitHub Actions

Manual deployments are useful for quick deployment but they are not suitable in every situation. Every code changes requires a manual redeployment and there is no guarantee tests passed before the code went out. A CI/CD pipeline solve this as every push to the main project branch automatically triggers a build, runs your project tests and deploys to Azure if everything passes.
This section discusses setting up a GitHub Actions pipeline for your ASP.NET Core Web API .

Getting the Azure Publish Profile

Setting up the GitHub Actions workflow can be done in various ways but we will be focusing on the approach that starts from Azure platform.
We will be doing so via a publish profile which is a file Azure generates that contains everything needed to authenticate a deployment. To get this, follow the steps below:

  1. In the Azure portal, navigate to the App Service resource created
  2. On the overview page, click Download publish profile. This will download an xml file to your machine

Download publish profile

  1. Open the downloaded file in a text editor and copy its entire contents
  2. Go to your project's GitHub repository. Navigate to Settings -> Security -> Secrets & Variables -> Actions
  3. Click New repository secret button and create a secret with the following
  4. Name: AZURE_WEBAPP_PUBLISH_PROFILE
  5. Value: paste the full contents of the publish profile file downloaded
  6. Click Add secret button

NOTE: Your publish profile is a sensitive entity so it should be safely stored. Anyone with its contents can deploy to your App Service so ensure to never commit it to your repository

The Workflow File

GitHub Action workflows reside in your project repository under .github/workflows/.
Create a file there e.g deploy.yml. Paste the code below in the file:

name: Build, Test and Deploy

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    permissions:
      contents: read

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Restore dependencies
        run: dotnet restore

      - name: Build
        run: dotnet build --configuration Release --no-restore

      - name: Run tests
        run: dotnet test --configuration Release --no-build --verbosity normal

      - name: Publish
        run: dotnet publish --configuration Release --output ./publish

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v4
        with:
          name: dotnet-app
          path: ./publish

  deploy:
    runs-on: ubuntu-latest
    needs: build-and-test

    permissions:
      contents: read

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v4
        with:
          name: dotnet-app

      - name: Deploy to Azure App Service
        uses: azure/webapps-deploy@v3
        with:
          app-name: 'your-app-service-name'
          publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
          package: .
Enter fullscreen mode Exit fullscreen mode

Replace your-app-service-name with the name of your App Service resource created.

Walking through the workflow

The pipeline is split into two jobs build-and-test and deploy.

  • build-and-test runs first on every push to main. It restores dependencies, builds the project in Release configuration, runs the test suite, and if everything passes, publishes the application and uploads the output as a build artifact. If any test fails, the job fails and the deploy job never runs so your broken code never reaches Azure.

  • deploy only runs if build-and-test passes, enforced by the needs: build-and-test directive. Rather than rebuilding from scratch, it downloads the artifact produced by the previous job and hands it directly to the azure/webapps-deploy action, which uses your publish profile secret to authenticate and push the exact same build that passed tests to Azure App Service.

Triggering a deployment

With the workflow file committed and pushed to your repository, the pipeline is live. Every subsequent push to main will automatically trigger it.
You can monitor runs in real time by navigating to the Actions tab in your project GitHub repository. Each run shows the status of every step, green checkmarks across the board means your code built, tested, and deployed successfully. If something fails, clicking into the failed step shows the full log output, which is usually enough to diagnose the problem quickly.
For the very first run, watch the deploy job logs closely, this is when your application boots for the first time against Azure SQL, applies any pending migrations, and starts serving requests. If anything is misconfigured such as a missing secret or a wrong connection string, it will surface here.

Wrapping up

If you've followed along to this point, you have a fully deployed ASP.NET Core Web API running on Azure, connected to a managed database, configured securely without a single secret in your codebase, and shipping new code automatically every time you push to your main branch. This guide covered the fundamentals, but there's plenty of room to further scale what has been built.

The official Microsoft Azure documentation is an excellent resource to continue from here. It is well maintained and covers more topics and concepts related to this guide.

I hope this guide took some of the mystery out of deploying to Azure. If you have any questions, run into any issues following along, or spot something worth correcting, feel free to drop a comment below. I would love to hear how it goes for your own project.

Top comments (0)