Build an ASP.NET Core and SQL Database app in Azure App Service on Linux

Note

This article deploys an app to App Service on Linux. To deploy to App Service on Windows, see Build a .NET Core and SQL Database app in Azure App Service.

App Service on Linux provides a highly scalable, self-patching web hosting service using the Linux operating system. This tutorial shows how to create a .NET Core app and connect it to a SQL Database. When you're done, you'll have a .NET Core MVC app running in App Service on Linux.

app running in App Service on Linux

In this tutorial, you learn how to:

  • Create a SQL Database in Azure
  • Connect a .NET Core app to SQL Database
  • Deploy the app to Azure
  • Update the data model and redeploy the app
  • Stream diagnostic logs from Azure
  • Manage the app in the Azure portal

If you don't have an Azure subscription, create a free account before you begin.

Prerequisites

To complete this tutorial:

Create local .NET Core app

In this step, you set up the local .NET Core project.

Clone the sample application

In the terminal window, cd to a working directory.

Run the following commands to clone the sample repository and change to its root.

git clone https://github.com/azure-samples/dotnetcore-sqldb-tutorial
cd dotnetcore-sqldb-tutorial

The sample project contains a basic CRUD (create-read-update-delete) app using Entity Framework Core.

Run the application

Run the following commands to install the required packages, run database migrations, and start the application.

dotnet restore
dotnet ef database update
dotnet run

Navigate to http://localhost:5000 in a browser. Select the Create New link and create a couple to-do items.

connects successfully to SQL Database

To stop .NET Core at any time, press Ctrl+C in the terminal.

Use Azure Cloud Shell

Azure hosts Azure Cloud Shell, an interactive shell environment that you can use through your browser. Cloud Shell lets you use either bash or PowerShell to work with Azure services. You can use the Cloud Shell pre-installed commands to run the code in this article without having to install anything on your local environment.

To launch Azure Cloud Shell:

Option Example/Link
Select Try It in the upper-right corner of a code block. Selecting Try It doesn't automatically copy the code to Cloud Shell. Example of Try It for Azure Cloud Shell
Go to https://shell.azure.com or select the Launch Cloud Shell button to open Cloud Shell in your browser. Launch Cloud Shell in a new window
Select the Cloud Shell button on the top-right menu bar in the Azure portal. Cloud Shell button in the Azure portal

To run the code in this article in Azure Cloud Shell:

  1. Launch Cloud Shell.

  2. Select the Copy button on a code block to copy the code.

  3. Paste the code into the Cloud Shell session with Ctrl+Shift+V on Windows and Linux, or Cmd+Shift+V on macOS.

  4. Press Enter to run the code.

Create production SQL Database

In this step, you create a SQL Database in Azure. When your app is deployed to Azure, it uses this cloud database.

For SQL Database, this tutorial uses Azure SQL Database.

Create a resource group

A resource group is a logical container into which Azure resources like web apps, databases, and storage accounts are deployed and managed. For example, you can choose to delete the entire resource group in one simple step later.

In the Cloud Shell, create a resource group with the az group create command. The following example creates a resource group named myResourceGroup in the West Europe location. To see all supported locations for App Service on Linux in Basic tier, run the az appservice list-locations --sku B1 --linux-workers-enabled command.

az group create --name myResourceGroup --location "West Europe"

You generally create your resource group and the resources in a region near you.

When the command finishes, a JSON output shows you the resource group properties.

Create a SQL Database logical server

In the Cloud Shell, create a SQL Database logical server with the az sql server create command.

Replace the <server-name> placeholder with a unique SQL Database name. This name is used as the part of the SQL Database endpoint, <server-name>.database.windows.net, so the name needs to be unique across all logical servers in Azure. The name must contain only lowercase letters, numbers, and the hyphen (-) character, and must be between 3 and 50 characters long. Also, replace <db-username> and <db-password> with a username and password of your choice.

az sql server create --name <server-name> --resource-group myResourceGroup --location "West Europe" --admin-user <db-username> --admin-password <db-password>

When the SQL Database logical server is created, the Azure CLI shows information similar to the following example:

{
  "administratorLogin": "sqladmin",
  "administratorLoginPassword": null,
  "fullyQualifiedDomainName": "<server-name>.database.windows.net",
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Sql/servers/<server-name>",
  "identity": null,
  "kind": "v12.0",
  "location": "westeurope",
  "name": "<server-name>",
  "resourceGroup": "myResourceGroup",
  "state": "Ready",
  "tags": null,
  "type": "Microsoft.Sql/servers",
  "version": "12.0"
}

Configure a server firewall rule

Create an Azure SQL Database server-level firewall rule using the az sql server firewall create command. When both starting IP and end IP are set to 0.0.0.0, the firewall is only opened for other Azure resources.

az sql server firewall-rule create --resource-group myResourceGroup --server <server-name> --name AllowAzureIps --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0

Create a database

Create a database with an S0 performance level in the server using the az sql db create command.

az sql db create --resource-group myResourceGroup --server <server-name> --name coreDB --service-objective S0

Create connection string

Replace the following string with the <server-name>, <db-username>, and <db-password> you used earlier.

Server=tcp:<server-name>.database.windows.net,1433;Database=coreDB;User ID=<db-username>;Password=<db-password>;Encrypt=true;Connection Timeout=30;

This is the connection string for your .NET Core app. Copy it for use later.

Deploy app to Azure

In this step, you deploy your SQL Database-connected .NET Core application to App Service on Linux.

Configure local git deployment

FTP and local Git can deploy to an Azure web app by using a deployment user. Once you configure your deployment user, you can use it for all your Azure deployments. Your account-level deployment username and password are different from your Azure subscription credentials.

To configure the deployment user, run the az webapp deployment user set command in Azure Cloud Shell. Replace <username> and <password> with a deployment user username and password.

  • The username must be unique within Azure, and for local Git pushes, must not contain the ‘@’ symbol.
  • The password must be at least eight characters long, with two of the following three elements: letters, numbers, and symbols.
az webapp deployment user set --user-name <username> --password <password>

The JSON output shows the password as null. If you get a 'Conflict'. Details: 409 error, change the username. If you get a 'Bad Request'. Details: 400 error, use a stronger password.

Record your username and password to use to deploy your web apps.

Create an App Service plan

In the Cloud Shell, create an App Service plan in the resource group with the az appservice plan create command.

The following example creates an App Service plan named myAppServicePlan in the Basic pricing tier (--sku B1) and in a Linux container (--is-linux).

az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --sku B1 --is-linux

When the App Service plan has been created, the Azure CLI shows information similar to the following example:

{ 
  "adminSiteName": null,
  "appServicePlanName": "myAppServicePlan",
  "geoRegion": "West Europe",
  "hostingEnvironmentProfile": null,
  "id": "/subscriptions/0000-0000/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/myAppServicePlan",
  "kind": "linux",
  "location": "West Europe",
  "maximumNumberOfWorkers": 1,
  "name": "myAppServicePlan",
  < JSON data removed for brevity. >
  "targetWorkerSizeId": 0,
  "type": "Microsoft.Web/serverfarms",
  "workerTierName": null
} 

Create a web app

Create a web app in the myAppServicePlan App Service plan.

In the Cloud Shell, you can use the az webapp create command. In the following example, replace <app-name> with a globally unique app name (valid characters are a-z, 0-9, and -). The runtime is set to DOTNETCORE|2.2. To see all supported runtimes, run az webapp list-runtimes --linux.

# Bash
az webapp create --resource-group myResourceGroup --plan myAppServicePlan --name <app-name> --runtime "DOTNETCORE|2.2" --deployment-local-git
# PowerShell
az --% webapp create --resource-group myResourceGroup --plan myAppServicePlan --name <app-name> --runtime "DOTNETCORE|2.2" --deployment-local-git

When the web app has been created, the Azure CLI shows output similar to the following example:

Local git is configured with url of 'https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git'
{
  "availabilityState": "Normal",
  "clientAffinityEnabled": true,
  "clientCertEnabled": false,
  "cloningInfo": null,
  "containerSize": 0,
  "dailyMemoryTimeQuota": 0,
  "defaultHostName": "<app-name>.azurewebsites.net",
  "deploymentLocalGitUrl": "https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git",
  "enabled": true,
  < JSON data removed for brevity. >
}

You’ve created an empty web app in a Linux container, with git deployment enabled.

Note

The URL of the Git remote is shown in the deploymentLocalGitUrl property, with the format https://<username>@<app-name>.scm.azurewebsites.net/<app-name>.git. Save this URL as you need it later.

Configure connection string

To set connection strings for your Azure app, use the az webapp config appsettings set command in the Cloud Shell. In the following command, replace <app-name>, as well as the <connection-string> parameter with the connection string you created earlier.

az webapp config connection-string set --resource-group myResourceGroup --name <app name> --settings MyDbConnection='<connection-string>' --connection-string-type SQLServer

In ASP.NET Core, you can use this named connection string (MyDbConnection) using the standard pattern, like any connection string specified in appsettings.json. In this case, MyDbConnection is also defined in your appsettings.json. When running in App Service, the connection string defined in App Service takes precedence over the connection string defined in your appsettings.json. The code uses the appsettings.json value during local development, and the same code uses the App Service value when deployed.

To see how the connection string is referenced in your code, see Connect to SQL Database in production.

Configure environment variable

Next, set ASPNETCORE_ENVIRONMENT app setting to Production. This setting lets you know whether you're running in Azure, because you use SQLite for your local development environment and SQL Database for your Azure environment.

The following example configures a ASPNETCORE_ENVIRONMENT app setting in your Azure app. Replace the <app-name> placeholder.

az webapp config appsettings set --name <app-name> --resource-group myResourceGroup --settings ASPNETCORE_ENVIRONMENT="Production"

To see how the environment variable is referenced in your code, see Connect to SQL Database in production.

Connect to SQL Database in production

In your local repository, open Startup.cs and find the following code:

services.AddDbContext<MyDatabaseContext>(options =>
        options.UseSqlite("Data Source=localdatabase.db"));

Replace it with the following code, which uses the environment variables that you configured earlier.

// Use SQL Database if in Azure, otherwise, use SQLite
if(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
    services.AddDbContext<MyDatabaseContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("MyDbConnection")));
else
    services.AddDbContext<MyDatabaseContext>(options =>
            options.UseSqlite("Data Source=MvcMovie.db"));

// Automatically perform database migration
services.BuildServiceProvider().GetService<MyDatabaseContext>().Database.Migrate();

If this code detects that it's running in production (which indicates the Azure environment), then it uses the connection string you configured to connect to the SQL Database. For information on how app settings are accessed in App Service, see Access environment variables.

The Database.Migrate() call helps you when it's run in Azure, because it automatically creates the databases that your .NET Core app needs, based on its migration configuration.

Save your changes, then commit it into your Git repository.

git add .
git commit -m "connect to SQLDB in Azure"

Push to Azure from Git

Back in the local terminal window, add an Azure remote to your local Git repository. Replace <deploymentLocalGitUrl-from-create-step> with the URL of the Git remote that you saved from Create a web app.

git remote add azure <deploymentLocalGitUrl-from-create-step>

Push to the Azure remote to deploy your app with the following command. When prompted for credentials by Git Credential Manager, make sure that you enter the credentials you created in Configure a deployment user, not the credentials you use to sign in to the Azure portal.

git push azure master

This command may take a few minutes to run. While running, it displays information similar to the following example:

Counting objects: 98, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (92/92), done.
Writing objects: 100% (98/98), 524.98 KiB | 5.58 MiB/s, done.
Total 98 (delta 8), reused 0 (delta 0)
remote: Updating branch 'master'.
remote: .
remote: Updating submodules.
remote: Preparing deployment for commit id '0c497633b8'.
remote: Generating deployment script.
remote: Project file path: ./DotNetCoreSqlDb.csproj
remote: Generated deployment script files
remote: Running deployment command...
remote: Handling ASP.NET Core Web Application deployment.
remote: .
remote: .
remote: .
remote: Finished successfully.
remote: Running post deployment command(s)...
remote: Deployment successful.
remote: App container will begin restart within 10 seconds.
To https://<app-name>.scm.azurewebsites.net/<app-name>.git
 * [new branch]      master -> master

Browse to the Azure app

Browse to the deployed app using your web browser.

http://<app-name>.azurewebsites.net

Add a few to-do items.

app running in App Service on Linux

Congratulations! You're running a data-driven .NET Core app in App Service on Linux.

Update locally and redeploy

In this step, you make a change to your database schema and publish it to Azure.

Update your data model

Open Models\Todo.cs in the code editor. Add the following property to the ToDo class:

public bool Done { get; set; }

Run Code First Migrations locally

Run a few commands to make updates to your local database.

dotnet ef migrations add AddProperty

Update the local database:

dotnet ef database update

Use the new property

Make some changes in your code to use the Done property. For simplicity in this tutorial, you're only going to change the Index and Create views to see the property in action.

Open Controllers\TodosController.cs.

Find the Create() method and add Done to the list of properties in the Bind attribute. When you're done, your Create() method signature looks like the following code:

public async Task<IActionResult> Create([Bind("ID,Description,CreatedDate,Done")] Todo todo)

Open Views\Todos\Create.cshtml.

In the Razor code, you should see a <div class="form-group"> element for Description, and then another <div class="form-group"> element for CreatedDate. Immediately following these two elements, add another <div class="form-group"> element for Done:

<div class="form-group">
    <label asp-for="Done" class="col-md-2 control-label"></label>
    <div class="col-md-10">
        <input asp-for="Done" class="form-control" />
        <span asp-validation-for="Done" class="text-danger"></span>
    </div>
</div>

Open Views\Todos\Index.cshtml.

Search for the empty <th></th> element. Just above this element, add the following Razor code:

<th>
    @Html.DisplayNameFor(model => model.Done)
</th>

Find the <td> element that contains the asp-action tag helpers. Just above this element, add the following Razor code:

<td>
    @Html.DisplayFor(modelItem => item.Done)
</td>

That's all you need to see the changes in the Index and Create views.

Test your changes locally

Run the app locally.

dotnet run

In your browser, navigate to http://localhost:5000/. You can now add a to-do item and check Done. Then it should show up in your homepage as a completed item. Remember that the Edit view doesn't show the Done field, because you didn't change the Edit view.

Publish changes to Azure

git add .
git commit -m "added done field"
git push azure master

Once the git push is complete, navigate to your Azure app and try out the new functionality.

Azure app after Code First Migration

All your existing to-do items are still displayed. When you republish your .NET Core app, existing data in your SQL Database isn't lost. Also, Entity Framework Core Migrations only changes the data schema and leaves your existing data intact.

Stream diagnostic logs

The sample project already follows the guidance at ASP.NET Core Logging in Azure with two configuration changes:

  • Includes a reference to Microsoft.Extensions.Logging.AzureAppServices in DotNetCoreSqlDb.csproj.
  • Calls loggerFactory.AddAzureWebAppDiagnostics() in Startup.cs.

Note

The project's log level is set to Information in appsettings.json.

You can access the console logs generated from inside the container. First, turn on container logging by running the following command in the Cloud Shell:

az webapp log config --name <app-name> --resource-group myResourceGroup --docker-container-logging filesystem

Once container logging is turned on, run the following command to see the log stream:

az webapp log tail --name <app-name> --resource-group myResourceGroup

If you don't see console logs immediately, check again in 30 seconds.

Note

You can also inspect the log files from the browser at https://<app-name>.scm.azurewebsites.net/api/logs/docker.

To stop log streaming at any time, type Ctrl+C.

For more information on customizing the ASP.NET Core logs, see Logging in ASP.NET Core.

Manage your Azure app

Go to the Azure portal to see the app you created.

From the left menu, click App Services, then click the name of your Azure app.

Portal navigation to Azure app

By default, the portal shows your app's Overview page. This page gives you a view of how your app is doing. Here, you can also perform basic management tasks like browse, stop, start, restart, and delete. The tabs on the left side of the page show the different configuration pages you can open.

App Service page in Azure portal

Clean up resources

In the preceding steps, you created Azure resources in a resource group. If you don't expect to need these resources in the future, delete the resource group by running the following command in the Cloud Shell:

az group delete --name myResourceGroup

This command may take a minute to run.

Next steps

What you learned:

  • Create a SQL Database in Azure
  • Connect a .NET Core app to SQL Database
  • Deploy the app to Azure
  • Update the data model and redeploy the app
  • Stream logs from Azure to your terminal
  • Manage the app in the Azure portal

Advance to the next tutorial to learn how to map a custom DNS name to your app.

Or, check out other resources: