Rollout your website in minutes with the Azure CLI
Tags: azure, cli, automation, devops
Published: 2020-08-20 15:28:56

## Introduction
As you might know I am big fan of automation. Whenever I run into repetitive :repeat: tasks I challenge myself to find good solutions and define best practices to improve the quality that we want to provide to our clients.

In my quest :crossed_swords: I was looking for ways to prevent the repeatedly, often manual creation of the necessary Azure resources to host our digital web platforms. The majority of these sites consist of the same resources, including a number of Azure Web Apps for the CMS (e.g. [Kentico Xperience](https://www.xperience.io)) and the frontend application (e.g. [ASP.NET MVC](https://www.asp.net) in combination with [React](https://reactjs.org)), Azure SQL database, Azure Search and Azure Storage for hosting media :framed_picture: files. In order to automate this process I came across two flavors: ARM templates and Azure CLI. But which one prevailed?

## ARM Templates versus Azure CLI
While both options have their advantages and disadvantages I ended up picking Azure CLI. I guess my former colleague [Pascal Naber](https://twitter.com/pascalnaber) ([Microsoft MVP](https://mvp.microsoft.com/)) was pretty persuasive in his article: [Stop using ARM templates! Use the Azure CLI instead](https://pascalnaber.wordpress.com/2018/11/11/stop-using-arm-templates-use-the-azure-cli-instead/) :wink:

In my opinion the great thing about Azure CLI is that it's a very clear and imperative approach for creating the necessary Azure resources. From my experience it is easier to learn compared to the ARM template approach where you could get lost :eyes: in the json structure. The biggest benefit of ARM templates on the contrary is it's support for incremental updates to the Azure resources.

I created my first Azure CLI script almost two years ago and the API has matured :bearded_person: a lot since then. In this post I will share various Azure CLI scripts that I have written to generate all Azure resources needed to host a professional website including CMS.

## Azure CLI scripts
The first thing we have to do is define configuration variables. This is especially useful because it only requires us to specify a number of project specific settings before we run the script. You can find an example of this below:

### Configure variables
```bash
PROJECT_ABBREVIATION=#specify abbreviation for project
PROJECT_ENVIRONMENT=#specify environment name e.g. TEST or PROD
LOCATION=#specify location e.g. westeurope
SERVICEPLAN_SKU=#specify service plan SKU e.g. S1 or S2
SEARCH_SKU=#specify search SKU e.g. BASIC or S1
SQL_ADMIN_PASSWORD=#specify sql password
SQL_EDITION=#specify sql edition e.g. Standard
SQL_COLLATION=Latin1_General_CI_AS
SQL_MAXSIZE=268435456000
SQL_SERVICEOBJECTIVE=#specify sql objective e.g. S1, S2
RESOURCEGROUP_NAME=$PROJECT_ABBREVIATION-RG-$PROJECT_DOMAIN-$PROJECT_ENVIRONMENT
SQL_SERVER_NAME=${PROJECT_ABBREVIATION,,}-dbs-web-${PROJECT_ENVIRONMENT,,}
SQL_ADMIN_USERNAME=$PROJECT_ABBREVIATION-$PROJECT_ENVIRONMENT-DB-ADMIN
SQL_DATABASE_NAME=$PROJECT_ABBREVIATION-DB-WEB-$PROJECT_ENVIRONMENT
SERVICEPLAN_NAME=$PROJECT_ABBREVIATION-ASP-WEB-$PROJECT_ENVIRONMENT
WEBAPP_NAME=$PROJECT_ABBREVIATION-APP-WEB-$PROJECT_ENVIRONMENT
STORAGEACCOUNT_NAME=${PROJECT_ABBREVIATION,,}saweb${PROJECT_ENVIRONMENT,,}
SEARCH_NAME=${PROJECT_ABBREVIATION,,}-as-web-${PROJECT_ENVIRONMENT,,}
```

In the first part of the script we build up the names of the resources. This is based on a certain naming convention to prevent, or reduce the chance, that the Azure resources already exist. It also helps in identifying the type of resource e.g. RG for Resource Group, APP for Web App and ASP for App Service Plan.

*Tip: You can find the variables for locations, editions or SKU in the [Azure CLI documentation](https://docs.microsoft.com/en-us/cli/azure/reference-index?view=azure-cli-latest).*

Here is a list of resource abbreviations that I like to use:
- RG: Resource Group
- ASP: App Service Plan
- APP: Web App / App Service
- AI: Application Insights
- DBS: Database Server (lowercase)
- DB: SQL Database
- SA: Storage Account (lowercase)
- AS: Azure Search (lowercase)
- SLOT: Deployment Slot 
- RC: Azure Redis Cache 

Now it is time to create the resources.

### Create the Azure Resource Group
The script below creates the resource group that like the name says groups all Azure resources in the location specified using the location variable specified in the previous script.

```bash
az group create --name $RESOURCEGROUP_NAME --location $LOCATION
```

### Create the Azure SQL Server
The next thing we're going to make is the SQL Server which is a bit more challenging because we also need to specify a firewall rule. The firewall rule below allows access from Azure services. In my setup I would like to do this because the Web Apps will access the SQL Database via a Connection String.

```bash
az sql server create --name $SQL_SERVER_NAME --location $LOCATION --resource-group $RESOURCEGROUP_NAME --admin-user $SQL_ADMIN_USERNAME --admin-password $SQL_ADMIN_PASSWORD
az sql server firewall-rule create --resource-group $RESOURCEGROUP_NAME --server $SQL_SERVER_NAME -n AllowAllWindowsAzureIps --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0
```

### Create the Azure SQL Database
What struck me was that you need to specify the edition separately from the service objective. I expected this to be a single parameter.

```bash
az sql db create --resource-group $RESOURCEGROUP_NAME --server $SQL_SERVER_NAME --name $SQL_DATABASE_NAME --edition $SQL_EDITION --collation $SQL_COLLATION --max-size $SQL_MAXSIZE --service-objective $SQL_SERVICEOBJECTIVE
```

### Create the Azure App Service Plan
To house :house: our Web Apps we create the App Service Plan first.
```bash
az appservice plan create --name $SERVICEPLAN_NAME --resource-group $RESOURCEGROUP_NAME --sku $SERVICEPLAN_SKU
```

### Create the Azure Web App
Then we can create the Azure Web Apps. The script below creates a single Web App. If you want to host multiple Web Apps in addition to the frontend application like a Content Management System, then I prefer to introduce an extra variable in the name e.g. WEB or CMS.
```bash
az webapp create --name $WEBAPP_NAME --resource-group $RESOURCEGROUP_NAME --plan $SERVICEPLAN_NAME
```

### Update the Connection String
To enable access from Web App to the SQL Database we need to specify the Connection String. I prefer to do this via Azure CLI because I can reuse the username and password variables that I used earlier in the script to create the SQL Server.
```bash
SQL_CONNECTIONSTRING="Data Source=tcp:'$(az sql server show -g $RESOURCEGROUP_NAME -n $SQL_SERVER_NAME --query fullyQualifiedDomainName -o tsv)',1433;Initial Catalog='$SQL_DATABASE_NAME';User Id='$SQL_ADMIN_USERNAME';Password='$SQL_ADMIN_PASSWORD';"
az webapp config connection-string set --resource-group $RESOURCEGROUP_NAME --name $WEBAPP_NAME --connection-string-type SQLServer --settings CMSConnectionString="$SQL_CONNECTIONSTRING"
```

### Create and hookup Application Insights
A powerful tool to measure the health :hospital: of the application is Application Insights. The script below creates the Application Insights resource and connects the required Instrumentation Key as Application Setting in the Web App.
```bash
az resource create --resource-group $RESOURCEGROUP_NAME --name $WEBAPP_NAME --resource-type Microsoft.Insights/components --location $LOCATION --properties '{"Application_Type":"web"}'
INSTRUMENTATIONKEY=$(az resource show -g $RESOURCEGROUP_NAME -n $WEBAPP_NAME --resource-type "Microsoft.Insights/components" --query properties.InstrumentationKey)
az webapp config appsettings set --resource-group $RESOURCEGROUP_NAME --name $WEBAPP_NAME --settings APPINSIGHTS_INSTRUMENTATIONKEY=$INSTRUMENTATIONKEY
```

### Update Azure Web App SSL settings
SSL is the standard nowadays and we would like to activate this by default using the script below.
```bash
az webapp update -g $RESOURCEGROUP_NAME -n $WEBAPP_NAME --https-only true
```

### Disable Azure Web App FTP state
In addition, we deactivate the FTP server on the Web App because the rollout takes place via Azure DevOps pipelines :100:.
```bash
az webapp config set --ftps-state Disabled --resource-group $RESOURCEGROUP_NAME -n $WEBAPP_NAME
```

### Optional: update Azure Web App application settings
The script below van be used if there is a need to automatically rollout Application Settings for the Web Apps. 
```bash
az webapp config appsettings set --resource-group $RESOURCEGROUP_NAME -n $WEBAPP_NAME --settings #Name=Value
```

### Create Azure Web App deployment slot
I love :heart: to use deployment slots to minimize downtime. Let's add it with the following script.
```bash
az webapp deployment slot create --name $WEBAPP_NAME --resource-group $RESOURCEGROUP_NAME --slot SLOT --configuration-source $WEBAPP_NAME
```

### Update SSL settings for deployment slot
Again we activate SSL as a standard option but this time for the deployment slot.
```bash
az webapp update -g $RESOURCEGROUP_NAME -n $WEBAPP_NAME --https-only true -s SLOT
```

### Create Azure Search
I am a big fan of Azure Search :mag: and enjoy using this resource in our projects. Create it via the script below. To create and update the search indexes I prefer to roll them out via code using Azure Functions. 

*Note: The Azure Function can be created using the az functionapp create command similar as creating the Web App.*
```bash
az search service create --name $SEARCH_NAME --resource-group $RESOURCEGROUP_NAME --sku $SEARCH_SKU
```

### Create Azure Storage Accounts
The last resource is the storage account for the storage of media files :framed_picture:.
```bash
az storage account create --name $STORAGEACCOUNT_NAME --resource-group $RESOURCEGROUP_NAME
```

## But how do you run these kickass scripts?
One of the best things about Azure Portal is that it comes with all these built in tools that help you in your daily work. In the navigation bar in the top you will find the door to the Azure Cloud Shell:

![Azure Cloud Shell](https://dev-to-uploads.s3.amazonaws.com/i/7vclwmhymvsqeelxrkes.png)

This will take you to an interface to execute the Azure CLI scripts. Alternativaly you could navigate to [shell.azure.com](shell.azure.com).

*Tip: When you fire Azure Cloud Shell for the first time you will need to create a storage account. For more info about the setup check out the [Azure Cloud Shell quickstart guide](https://docs.microsoft.com/en-us/azure/cloud-shell/quickstart)* 

And that's all to it. Have fun with your next rollout :partying_face: