Swimburger

Deploying Umbraco 9 to Azure App Service for Linux

Niels Swimberghe

Niels Swimberghe - - Umbraco

Follow me on Twitter, buy me a coffee

Umbraco, Azure, and Linux logo next to title: Deploying Umbraco 9 to Azure App Service for Linux

Umbraco 9 has been built on top of .NET 5 (.NET Core). This means you are not limited to hosting Umbraco on Windows anymore, but you can now also host Umbraco on macOS and many popular Linux distributions.
If you were hosting Umbraco on Azure App Service previously, you had to host it using App Service for Windows, but now you can also host it on App Service for Linux.

In this blog post, you will learn how to provision the infrastructure to host your Umbraco 9 instance using Azure SQL and Azure App Service for Linux. After provisioning the infrastructure, you will learn how to deploy your Umbraco 9 website to Azure App Service for Linux.

Prerequisites #

You will need these items to follow along:

Create Azure infrastructure for Umbraco 9 #

You need at least 2 pieces of infrastructure to host Umbraco: Microsoft SQL Server and a web server.
There are many services in Azure that can run SQL Server, and many services to run web servers. For this tutorial, you will use Azure SQL and Azure App Service for Linux.

You will use the Azure CLI to provision these resources in a PowerShell script. To keep things organized, start with initializing some variables that you'll use later on. Run the following PowerShell and adjust the variables as needed:

$ResourceBaseName = 'your-umbraco9-site';
# assuming everything will be in the same resource location, 
# but may not be possible if Azure services aren't available in your desired region.
$Location = 'eastus';
$GroupName = "$($ResourceBaseName)-rg";

$AppServicePlanName = "$($ResourceBaseName)-asp";
# Enter web app name (has to be globally unique, will be used as subdomain xxx.azurewebsites.net)
$AppServiceName = 'nswimberghe-umbraco9-app';

# Enter sql server name (has to be globally unique, will be used as subdomain xxx.database.windows.net)
$SqlServerName = 'nswimberghe-umbraco9-sqlserver';
$SqlDbName = "$($ResourceBaseName)-sqldb";
$SqlAdminUser = 'YourSqlAdminUser';
# Must be complex enough or error will be thrown, use uppercase, lowercase, special symbols, and numbers
$SqlAdminPwd = '[Your SQL Admin User Password]'; 

Here's what the variables will be used for:

  • ResourceBaseName: the prefix for multiple Azure resources
  • Location: the location of all the Azure resources. The Azure services may not be available in the Azure region you wish to use. In that case, use a supported region.
    You can use this Azure website to figure out in which region an Azure service is supported.
  • GroupName: the name of the Azure resource group.
  • AppServicePlanName: the name of the App Service Plan. The App Service Plan is the set of servers (1 or more) on which your App Services will be run.
  • AppServiceName: the name of your App Service. This name will be used to create a DNS entry like 'xxx.azurewebsites.net' where 'xxx' will be the name of your App Service. The name of your App Service has to be globally unique.
  • SqlServerName: the name of your SQL Server. This name will be used to create a DNS entry like 'xxx.database.windows.net' where 'xxx' will be the name of your SQL Server. The name of your SQL server has to be globally unique.
  • SqlDbName: the name of the database which will be created on your SQL Server. This is where the Umbraco CMS will store its content.
  • SqlAdminUser: the username of the SQL Server admin user.
  • SqlAdminPwd: the password of the SQL Server admin user.

The SQL Server admin credentials will be used to have the Umbraco CMS authenticate with the SQL server. It would be better to create a separate user and use alternatives methods to authenticate, but in this tutorial, you'll stick to using the admin credentials to keep things simple.

Create a new folder on your machine:

mkdir Umbraco9ToAzureAppServiceForLinux
cd Umbraco9ToAzureAppServiceForLinux

You will use this folder to store configuration and your new Umbraco 9 website.

Run the following command:

az config set --local `
    defaults.group=$GroupName `
    defaults.location=$Location `
    defaults.appserviceplan=$AppServicePlanName `
    defaults.web=$AppServiceName;

This will store some configuration at .azure/config which will look like this:

[defaults]
group = your-umbraco9-site-rg
location = eastus
appserviceplan = your-umbraco9-site-asp
web = nswimberghe-umbraco9-app

Certain Azure CLI commands will require you to pass this information in as arguments, but if you don't, the commands will look at this configuration and use that instead. You'll run some commands to deploy your Umbraco 9 website later which will rely on this configuration.

Next, create your Azure resource group:

az group create --location $Location --resource-group $GroupName;

Run this command to create an Azure SQL Server:

az sql server create `
    --location $Location `
    --resource-group $GroupName `
    --name $SqlServerName `
    --admin-user $SqlAdminUser `
    --admin-password $SqlAdminPwd;

Run the following command to create a database in your Azure SQL Server:

az sql db create `
    --server $SqlServerName `
    --name $SqlDbName;

Run this command to create a firewall rule which allows Azure services to have network access to your SQL Server. In the Azure Portal, there's a toggle to enable this, but in the CLI, you have to use start and stop IP address as 0.0.0.0 which Azure will interpret as the same thing.

# this allows Azure services to communicate with the Sql Server (there are more secure alternatives)
az sql server firewall-rule create `
    --resource-group $GroupName `
    --server $SqlServerName `
    --name AllowAzureServices `
    --start-ip-address 0.0.0.0 `
    --end-ip-address 0.0.0.0;

Now all Azure services will be able to reach your SQL Server. There are better ways to secure your SQL Server such as using a Virtual Network, but this is the simplest way to get started.

You can use the az sql db show-connection-string command to get an 'ado.net' compatible connection string. This connection string will have not have the actual username and password included, but instead have the value '<username>' as the username and the value '<password>' as the password.
Use the following Azure CLI command and use PowerShell to replace the dummy credentials with the actual admin credentials:

$ConnectionString = $(az sql db show-connection-string `
    --server $SqlServerName `
    --name $SqlDbName `
    --client ado.net `
    --auth-type SqlPassword) `
    -replace "<username>",$SqlAdminUser -replace "<password>",$SqlAdminPwd;

 

You now have created an Azure SQL Server with a database and stored the connection string to connect to the SQL server into the ConnectionString variable. It is time to provision the Azure App Service for Linux.

Run the following command to create the Azure App Service Plan:

az appservice plan create `
    --location $Location `
    --resource-group $GroupName `
    --name $AppServicePlanName `
    --is-linux;

By passing in the --is-linux argument, Azure will create an App Service plan based on Linux instead of Windows.

Run the following command to create the App Service:

az webapp create `
    --name $AppServiceName `
    --plan $AppServicePlanName `
    --runtime '"DOTNET|5.0"'; #'" and "' is used to escape the | character in PowerShell

The --runtime argument specifies that the App Service should use the .NET 5 runtime to host your application.

Lastly, your Umbraco 9 site will need to connect to the SQL server, so you need to configure the connection string on your App Service to do that. Run the following command to add the connection string to your App Service configuration:

az webapp config connection-string set `
    --resource-group $GroupName `
    --name $AppServiceName `
    --settings umbracoDbDSN=$ConnectionString `
    --connection-string-type SQLAzure;

Here are all the steps in a single PowerShell script:

$ResourceBaseName = 'umbraco9';
$Location = 'eastus';
$GroupName = "$($ResourceBaseName)-rg";

$AppServicePlanName = "$($ResourceBaseName)-asp";
Write-Host "Enter web app name (has to be globally unique, will be used as subdomain xxx.azurewebsites.net)";
$AppServiceName = Read-Host "(name has to be globally unique, can only contain lowercase characters, numbers, and dashes)";

Write-Host "Enter sql server name (has to be globally unique, will be used as subdomain xxx.database.windows.net)";
$SqlServerName = Read-Host "(name has to be globally unique, can only contain lowercase characters, numbers, and dashes)";
$SqlDbName = "$($ResourceBaseName)-sqldb";
$SqlAdminUser = "SqlAdmin";
$SqlAdminPwd = Read-Host -AsSecureString "Enter a password for Sql Server admin";

az config set --local `
    defaults.group=$GroupName `
    defaults.location=$Location `
    defaults.appserviceplan=$AppServicePlanName `
    defaults.web=$AppServiceName;

az group create --location $Location --resource-group $GroupName;

az sql server create `
    --location $Location `
    --resource-group $GroupName `
    --name $SqlServerName `
    --admin-user $SqlAdminUser `
    --admin-password $($SqlAdminPwd | ConvertFrom-SecureString -AsPlainText);

az sql db create `
    --server $SqlServerName `
    --name $SqlDbName;

# this allows Azure services to communicate with the Sql Server (there are more secure alternatives)
az sql server firewall-rule create `
    --resource-group $GroupName `
    --server $SqlServerName `
    --name AllowAzureServices `
    --start-ip-address 0.0.0.0 `
    --end-ip-address 0.0.0.0;

$ConnectionString = $(az sql db show-connection-string `
    --server $SqlServerName `
    --name $SqlDbName `
    --client ado.net `
    --auth-type SqlPassword) `
    -replace "<username>",$SqlAdminUser -replace "<password>",$($SqlAdminPwd | ConvertFrom-SecureString -AsPlainText) `
    | ConvertTo-SecureString -AsPlainText;

az appservice plan create `
    --location $Location `
    --resource-group $GroupName `
    --name $AppServicePlanName `
    --is-linux;

az webapp create `
    --name $AppServiceName `
    --plan $AppServicePlanName `
    --runtime '"DOTNET|5.0"'; #'" and "' is used to escape the | character in PowerShell

az webapp config connection-string set `
    --resource-group $GroupName `
    --name $AppServiceName `
    --settings umbracoDbDSN=$($ConnectionString | ConvertFrom-SecureString -AsPlainText) `
    --connection-string-type SQLAzure;

Instead of hardcoding some of the values, the script will prompt you for them. The script also uses SecureString to store the admin password and connection string instead of storing it as plain text.

Deploy Umbraco 9 to Azure App Service for Linux #

Run these commands to create a new Umbraco 9 website locally in case you don't have an Umbraco 9 website yet:

dotnet new -i Umbraco.Templates::*;
dotnet new umbraco;

Run this command to publish the Umbraco 9 site to the publish folder:

dotnet publish . -c Release -o publish;

Run the following PowerShell cmdlet to zip the publish folder:

Compress-Archive -Path publish/* -DestinationPath publish.zip;

Run this command to deploy the zip file to your App Service:

az webapp deploy `
    --clean true `
    --restart true `
    --src-path publish.zip `
    --type zip;

This command will

  • upload your zip file to Azure
  • delete any existing files in your App Service (--clean)
  • deploy the contents of the zip file to your App Service
  • restart your App Service

Normally, you need to also pass in the resource group and app service name, but in this case, it pulled that information from the .azure/config file.

You can use the following command to quickly browse to your website:

az webapp browse

By default, it'll take you to the website with the HTTP protocol instead of the HTTPS protocol. Make sure to switch to HTTPS, or even better, update your website to redirect all HTTP requests to HTTPS and add the HSTS security header.

To make the deployment more repeatable, you can use the following PowerShell script:

If (Test-Path -Path './publish') {
    Remove-Item -Path './publish' -Recurse;
}
If (Test-Path -Path './publish.zip') {
    Remove-Item -Path './publish.zip' -Recurse;
}

dotnet publish . -c Release -o publish;
Compress-Archive -Path publish/* -DestinationPath publish.zip;
az webapp deploy `
    --clean true `
    --restart true `
    --src-path publish.zip `
    --type zip;

This updated script makes sure any previous publish folder or publish.zip file is removed before running the commands to publish and deploy the website.

Summary #

Now that Umbraco 9 is built on top of .NET 5 (.NET Core), you can host Umbraco on macOS and many popular Linux distributions. This also means you can now use Azure App Service for Linux instead of for Windows. You learned how to create the Azure infrastructure for an Umbraco website using the Azure CLI and how to deploy your Umbraco site to Azure App Service for Linux.

Related Posts

Related Posts

.NET Bot on a scooter riding from Umbraco 8 to 9

Thoughts and tips on moving to Umbraco 9 from Umbraco 8

- Umbraco
.NET Core was a groundbreaking change to the .NET platform. It is blazing fast, open-source, and cross-platform across Windows, Linux, and macOS. With Umbraco 9, we finally get to enjoy all the new innovations from .NET Core. Read about my experience upgrading an Umbraco 8 website to Umbraco 9.
.NET Core logo + Dotnet bot wearing Red Hat

How to run ASP.NET Core Web Application as a service on Linux without reverse proxy, no NGINX or Apache

- .NET
This article walks us through running a ASP.NET Core web application on Linux (RHEL) using systemd. The end goal is to serve ASP.NET Core directly via the built-in Kestrel webserver over port 80/443.
Azure & .NET & Discord Logo

Creating a Discord Bot using .NET Core and hosting it on Azure App Services

- Azure
Discord is an online communication platform built specifically for gaming. Using .NET Core and Azure App Service WebJobs we can host a Discord bot that can listen and respond to voice and text input.
.NET Bot, Azure, and Discord are together in a Discord server

How to create a Discord Bot using the .NET worker template and host it on Azure Container Instances

- Azure
Learn how to develop a Discord bot using the .NET worker template, containerize it using Docker, push the container image to Azure Container Registry, and host it on Azure Container Instances.
.NET Core logo + Dotnet bot wearing Red Hat

How to run a .NET Core console app as a service using Systemd on Linux (RHEL)

- .NET
Let's learn how to run a .NET Core console application on systemd. After running a console app as a service, we'll upgrade to using the dotnet core worker service template designed for long running services/daemons. Lastly, we'll add the systemd package integration.