Introduction
As you might know I am big fan of automation. Whenever I run into repetitive 🔁 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 ⚔️ 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) and the frontend application (e.g. ASP.NET MVC in combination with React), Azure SQL database, Azure Search and Azure Storage for hosting media 🖼️ 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 (Microsoft MVP) was pretty persuasive in his article: Stop using ARM templates! Use the Azure CLI instead 😉
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 👀 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 🧔 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
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.
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.
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.
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.
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 🏠 our Web Apps we create the App Service Plan first.
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.
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.
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 🏥 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.
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.
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 💯.
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.
az webapp config appsettings set --resource-group $RESOURCEGROUP_NAME -n $WEBAPP_NAME --settings #Name=Value
Create Azure Web App deployment slot
I love ❤️ to use deployment slots to minimize downtime. Let's add it with the following script.
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.
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 🔍 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.
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 🖼️.
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:
This will take you to an interface to execute the Azure CLI scripts. Alternativaly you could navigate to 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
And that's all to it. Have fun with your next rollout 🥳
Top comments (0)