Today we will cover how to deploy an Azure SQL Server in Azure using ARM and how to and how to import a Database using a BACPAC file.
NOTE: This article was tested and written for a Linux Host running Ubuntu 18.04 with Azure CLI installed.
The steps for today's article are below.
Deploy a new Resource Group
Create the ARM Template File
Deploy the ARM Template
Retrieve the Credentials of the SQL Admin Account
Create a Firewall Rule for your Public IP
Create a Storage Account to use to Import SQL Databases
Upload a Database BACPAC File
Import the Database into the Azure SQL Server
Things to Consider
Conclusion
SPONSOR: Need to stop and start your development VMs on a schedule? The Azure Resource Scheduler let's you schedule up to 10 Azure VMs for FREE! Learn more HERE
Using Azure CLI, run the following command to create a new Resource Group.
az group create \
--name 100days-azsql \
--location westeurope
You should get back the following output:
{
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/100days-azsql",
"location": "westeurope",
"managedBy": null,
"name": "100days-azsql",
"properties": {
"provisioningState": "Succeeded"
},
"tags": null,
"type": "Microsoft.Resources/resourceGroups"
}
Below is the ARM Template File that we will be using to deploy the Azure SQL Server and a Database.
Copy the contents below into a file called azuredeploy.json.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"skuName": {
"type": "string",
"defaultValue": "B1",
"allowedValues": [
"F1",
"D1",
"B1",
"B2",
"B3",
"S1",
"S2",
"S3",
"P1",
"P2",
"P3",
"P4"
],
"metadata": {
"description": "Describes plan's pricing tier and instance size. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
}
},
"skuCapacity": {
"type": "int",
"defaultValue": 1,
"minValue": 1,
"maxValue": 3,
"metadata": {
"description": "Describes plan's instance count"
}
},
"sqlAdministratorLogin": {
"type": "string",
"defaultValue": "sqladmdays",
"metadata": {
"description": "The administrator username of the SQL Server."
}
},
"sqlAdministratorLoginPassword": {
"type": "securestring",
"defaultValue": "[guid(resourceGroup().id, deployment().name)]",
"metadata": {
"description": "The administrator password of the SQL Server."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {
"sqlServerName": "[concat('100dayssqlsrv-', substring(uniqueString(resourceGroup().id), 0, 3))]",
"hostingPlanName": "[concat('sqlsite-hostingplan-', substring(uniqueString(resourceGroup().id),0, 3))]",
"webSiteName": "[concat('sqlsite-', substring(uniqueString(resourceGroup().id),0, 3))]",
"databaseName": "wide-world-imports-std",
"databaseEdition": "Standard",
"databaseCollation": "SQL_Latin1_General_CP1_CI_AS",
"databaseServiceObjectiveName": "S2"
},
"resources": [
{
"name": "[variables('sqlServerName')]",
"type": "Microsoft.Sql/servers",
"apiVersion": "2014-04-01",
"location": "[parameters('location')]",
"tags": {
"displayName": "SqlServer"
},
"properties": {
"administratorLogin": "[parameters('sqlAdministratorLogin')]",
"administratorLoginPassword": "[parameters('sqlAdministratorLoginPassword')]",
"version": "12.0"
},
"resources": [
{
"name": "[variables('databaseName')]",
"type": "databases",
"apiVersion": "2015-01-01",
"location": "[parameters('location')]",
"tags": {
"displayName": "Database"
},
"properties": {
"edition": "[variables('databaseEdition')]",
"collation": "[variables('databaseCollation')]",
"requestedServiceObjectiveName": "[variables('databaseServiceObjectiveName')]"
},
"dependsOn": [
"[variables('sqlServerName')]"
]
},
{
"name": "AllowAllMicrosoftAzureIps",
"type": "firewallrules",
"apiVersion": "2014-04-01",
"location": "[parameters('location')]",
"properties": {
"endIpAddress": "0.0.0.0",
"startIpAddress": "0.0.0.0"
},
"dependsOn": [
"[variables('sqlServerName')]"
]
}
]
},
{
"apiVersion": "2018-02-01",
"name": "[variables('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[parameters('location')]",
"tags": {
"displayName": "HostingPlan"
},
"sku": {
"name": "[parameters('skuName')]",
"capacity": "[parameters('skuCapacity')]"
},
"properties": {
"name": "[variables('hostingPlanName')]"
}
},
{
"apiVersion": "2018-02-01",
"name": "[variables('webSiteName')]",
"type": "Microsoft.Web/sites",
"location": "[parameters('location')]",
"dependsOn": [
"[variables('hostingPlanName')]"
],
"tags": {
"[concat('hidden-related:', resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName')))]": "empty",
"displayName": "Website"
},
"properties": {
"name": "[variables('webSiteName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]"
},
"resources": [
{
"apiVersion": "2018-02-01",
"type": "config",
"name": "connectionstrings",
"dependsOn": [
"[variables('webSiteName')]"
],
"properties": {
"DefaultConnection": {
"value": "[concat('Data Source=tcp:', reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', variables('databaseName'), ';User Id=', parameters('sqlAdministratorLogin'), '@', reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName, ';Password=', parameters('sqlAdministratorLoginPassword'), ';')]",
"type": "SQLAzure"
}
}
}
]
}
]
}
Below is a table of the Parameter Values that will be passed to the ARM Template at runtime.
Name | Type | Default Value | Description |
---|---|---|---|
skuName | String | B1 | Describes the Web App Hosting plan's pricing tier and instance size. |
skuCapacity | Integer | 1 | The Number of instances to run. |
sqlAdministratorLogin | String | sqladmdays | The administrator username of the SQL Server. |
sqlAdministratorLoginPassword | secureString | ARM Template guid Function | The administrator password of the SQL Server. |
location | String | Resource Group Location | Azure Location to Use. |
Run the following command to deploy the ARM Template using the Azure CLI
az group deployment create \
--resource-group 100days-azsql \
--template-file azuredeploy.json \
--output table
The deployment should run for a few minutes and should output the following when it's completed.
Name ResourceGroup State Timestamp Mode
----------- ---------------- --------- -------------------------------- -----------
azuredeploy 100days-azsql Succeeded 2020-01-20T15:20:53.896948+00:00 Incremental
In Day 86 we provided three options you could use to retrieve the SQL Admin User Password that was auto-generated. Those same basic options apply here as well. To make this quick, replace the three characters after sqlsite- in the --name switch below with whatever is currently in place for your Web App in the Azure Portal and then run the command.
az webapp config connection-string list \
--name sqlsite-dol \
--resource-group 100days-azsql \
--query [].value.value \
--output tsv
You should get back the connection string currently in use for the Web App to connect to the PostgreSQL Server.
Data Source=tcp:100dayssqlsrv-dol.database.windows.net,1433;Initial Catalog=wide-world-imports-std;User [email protected];Password=ff5b2522-ff34-5477-96d5-da2d182d46cb;
Set the Password shown above into the SQL_SRV_ADMIN_PASSWORD variable as shown below.
SQL_SRV_ADMIN_PASSWORD="ff5b2522-ff34-5477-96d5-da2d182d46cb"
Next, run the following command to retrieve your Public IP Address.
MY_PUB_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
NOTE: You can also use a third-party site or Google "what is my ip address" to retrieve your Public IP Address and set it to the MY_PUB_IP variable.
Run the following command to create a new Firewall Rule on the Azure SQL Server to grant yourself access. Replace the three characters after 100dayssqlsrv- in the --name switch below with whatever is currently in place for your SQL Server in the Azure Portal and then run the command.
az sql server firewall-rule create \
--name "my-public-ip" \
--resource-group "100days-azsql" \
--server "100dayssqlsrv-dol" \
--start-ip-address $MY_PUB_IP \
--end-ip-address $MY_PUB_IP
You should get back a response similar to the output below.
{
"endIpAddress": "000.000.000.000",
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/100days-azuredb/providers/Microsoft.Sql/servers/100days-azuresqlsrv-st4c/firewallRules/my-public-ip",
"kind": "v12.0",
"location": "West Europe",
"name": "my-public-ip",
"resourceGroup": "100days-azuredb",
"startIpAddress": "000.000.000.000",
"type": "Microsoft.Sql/servers/firewallRules"
}
NOTE: Without this firewall rule, you won't be able to import the SQL Database later.
Next, run the following command to generate a random 4-character set of alphanumeric characters.
RANDOM_ALPHA=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 4 | head -n 1)
If you are using a Mac, use the command below instead.
RANDOM_ALPHA=$(LC_CTYPE=C tr -dc 'a-z0-9' < /dev/urandom | fold -w 4 | head -n 1)
Run the following command to create a new Storage Account
az storage account create \
--name "100daysqlimport$RANDOM_ALPHA" \
--resource-group "100days-azsql" \
--location "westeurope" \
--query '[provisioningState,statusOfPrimary]' \
--output tsv
After the Storage Account has successfully provisioned, you should get back something similar to the response below.
Succeeded
available
Next, run the following command to create a container for the SQL BACPAC file(s).
az storage container create \
--name "bacpac-files" \
--account-name "100daysqlimport$RANDOM_ALPHA"
You should get back the following response.
{
"created": true
}
Next, run the following command to retrieve the Storage Account Primary Key to use later.
AZ_STORAGE_PRIMARY_ACCOUNT_KEY=$(az storage account keys list \
--account-name "100daysqlimport$RANDOM_ALPHA" \
--query [0].value \
--output tsv)
Run the following command to download the WideWorldImporters-Standard.bacpac file from GitHub.
wget https://github.com/Microsoft/sql-server-samples/releases/download/wide-world-importers-v1.0/WideWorldImporters-Standard.bacpac
Next, run the following command to upload the WideWorldImporters-Standard.bacpac file to the Storage Account
az storage blob upload \
--name "WideWorldImporters-Standard.bacpac" \
--container-name "bacpac-files" \
--account-name "100daysqlimport$RANDOM_ALPHA" \
--file WideWorldImporters-Standard.bacpac
When the upload is finished, you should get back the following response.
Finished[#############################################################] 100.0000%
{
"etag": "\"0x8D7A2884878390D\"",
"lastModified": "2020-01-26T17:50:51+00:00"
}
Run the following command to import the WideWorldImporters-Standard.bacpac file into the wide-world-imports-std Database. Replace the three characters after 100dayssqlsrv- in the --server switch below with whatever is currently in place for your SQL Server in the Azure Portal and then run the command.
az sql db import \
--name "wide-world-imports-std" \
--server "100dayssqlsrv-dol" \
--resource-group "100days-azsql" \
--admin-user "sqladmdays" \
--admin-password $SQL_SRV_ADMIN_PASSWORD \
--storage-key-type "StorageAccessKey" \
--storage-key $AZ_STORAGE_PRIMARY_ACCOUNT_KEY \
--storage-uri "https://100daysqlimport$RANDOM_ALPHA.blob.core.windows.net/bacpac-files/WideWorldImporters-Standard.bacpac"
The Import process will take a few minutes to run. When it's completed you should get output similar to what is shown below.
{
"blobUri": "https://100daysqlimportst4c.blob.core.windows.net/bacpac-files/WideWorldImporters-Standard.bacpac",
"databaseName": "wide-world-imports-std",
"errorMessage": null,
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/100days-azuredb/providers/Microsoft.Sql/servers/100days-azuresqlsrv-st4c/databases/wide-world-imports-std/extensions/import",
"lastModifiedTime": "1/26/2020 9:50:51 PM",
"name": "import",
"queuedTime": "1/26/2020 9:36:48 PM",
"requestId": "0f30e0e9-fcd9-4e02-b6fb-ad964e334050",
"requestType": "Import",
"resourceGroup": "100days-azuredb",
"serverName": "100days-azuresqlsrv-st4c",
"status": "Completed",
"type": "Microsoft.Sql/servers/databases/extensions"
}
You can also Import SQL BACPAC files in an ARM Template which you can read about here. Irrespective of what you decide to use (ARM Templates, Azure PowerShell, and Azure CLI), choose the one that best suits your needs.
In today's article we covered how to deploy an Azure SQL Server in Azure using ARM and how to and how to import a Database using a BACPAC file. If there's a specific scenario that you wish to be covered in future articles, please create a New Issue in the starkfell/100DaysOfIaC GitHub repository.