Skip to main content
Azure · lesson 6 of 10
Azure intermediate Lesson 6 of 10

Azure SQL Basics

Provision an Azure SQL Database and logical server, connect with sqlcmd past a firewall error, then restore to a point in time after a bad delete.

By SkillByExample · Updated

azure azure-sql databases sql

Azure SQL Database is a managed, single-database PaaS offering: you provision a logical server for connectivity and firewall rules, then one or more databases under it, and Azure handles patching, high availability, and backups without you managing a VM.

Creating the server and database

az sql server create \
  --resource-group learn-rg \
  --name learn-sql-srv \
  --location eastus \
  --admin-user sqladmin \
  --admin-password "Tr0ub4dor&3xample!"
$ az sql server create --resource-group learn-rg --name learn-sql-srv --location eastus --admin-user sqladmin --admin-password "Tr0ub4dor&3xample!"
{
  "administratorLogin": "sqladmin",
  "fullyQualifiedDomainName": "learn-sql-srv.database.windows.net",
  "id": "/subscriptions/3b1f9c2a-6d4e-4a7b-9c11-2f5e8a0b7d33/resourceGroups/learn-rg/providers/Microsoft.Sql/servers/learn-sql-srv",
  "name": "learn-sql-srv",
  "state": "Ready"
}
az sql db create \
  --resource-group learn-rg \
  --server learn-sql-srv \
  --name orders-db \
  --service-objective Basic
$ az sql db create --resource-group learn-rg --server learn-sql-srv --name orders-db --service-objective Basic
{
  "name": "orders-db",
  "status": "Online",
  "currentServiceObjectiveName": "Basic",
  "maxSizeBytes": 2147483648,
  "collation": "SQL_Latin1_General_CP1_CI_AS"
}

Basic tops out at 2 GB and 5 DTUs — enough for this lesson and priced for it, not for production. fullyQualifiedDomainName is the hostname every client connects to; there’s no separate concept of a private IP to reach it directly.

Connecting before and after the firewall rule

sqlcmd -S learn-sql-srv.database.windows.net -d orders-db -U sqladmin -P "Tr0ub4dor&3xample!" -Q "SELECT 1"
$ sqlcmd -S learn-sql-srv.database.windows.net -d orders-db -U sqladmin -P "Tr0ub4dor&3xample!" -Q "SELECT 1"
Sqlcmd: Error: Microsoft ODBC Driver 18 for SQL Server : Cannot open server 'learn-sql-srv'
requested by the login. Client with IP address '203.0.113.50' is not allowed to access the
server. To enable access, use the Windows Azure Management Portal or run sp_set_firewall_rule
on the master database to create a firewall rule for this IP address or address range. It may
take up to five minutes for this change to take effect..

Azure SQL blocks every inbound connection by default, and the error usefully includes the IP that got rejected — copy it directly into the fix:

az sql server firewall-rule create \
  --resource-group learn-rg \
  --server learn-sql-srv \
  --name allow-my-ip \
  --start-ip-address 203.0.113.50 \
  --end-ip-address 203.0.113.50
$ az sql server firewall-rule create --resource-group learn-rg --server learn-sql-srv --name allow-my-ip --start-ip-address 203.0.113.50 --end-ip-address 203.0.113.50
{
  "name": "allow-my-ip",
  "startIpAddress": "203.0.113.50",
  "endIpAddress": "203.0.113.50"
}
sqlcmd -S learn-sql-srv.database.windows.net -d orders-db -U sqladmin -P "Tr0ub4dor&3xample!" -Q "SELECT 1"
$ sqlcmd -S learn-sql-srv.database.windows.net -d orders-db -U sqladmin -P "Tr0ub4dor&3xample!" -Q "SELECT 1"

-----------
          1

(1 rows affected)

Loading data and taking a backup checkpoint

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer VARCHAR(50),
    amount DECIMAL(10,2)
);

INSERT INTO orders VALUES
    (1, 'Ada Lovelace', 25.50),
    (2, 'Grace Hopper', 12.00);
$ sqlcmd -S learn-sql-srv.database.windows.net -d orders-db -U sqladmin -P "..." -i create_orders.sql
Changed database context to 'orders-db'.
(2 rows affected)

Azure SQL backs this up automatically — there’s no manual backup command to run. Note the time, since restore is by timestamp, not by backup file:

date -u +"%Y-%m-%dT%H:%M:%SZ"
$ date -u +"%Y-%m-%dT%H:%M:%SZ"
2026-09-12T15:40:00Z

Simulating a mistake, then restoring to before it happened

DELETE FROM orders WHERE order_id = 1;
$ sqlcmd -S learn-sql-srv.database.windows.net -d orders-db -U sqladmin -P "..." -Q "DELETE FROM orders WHERE order_id = 1"
(1 rows affected)
az sql db restore \
  --resource-group learn-rg \
  --server learn-sql-srv \
  --name orders-db \
  --dest-name orders-db-restored \
  --time "2026-09-12T15:40:00Z"
$ az sql db restore --resource-group learn-rg --server learn-sql-srv --name orders-db --dest-name orders-db-restored --time "2026-09-12T15:40:00Z"
{
  "name": "orders-db-restored",
  "status": "Online",
  "creationDate": "2026-09-12T15:47:12Z",
  "restorePointInTime": "2026-09-12T15:40:00Z"
}
SELECT * FROM orders;
$ sqlcmd -S learn-sql-srv.database.windows.net -d orders-db-restored -U sqladmin -P "..." -Q "SELECT * FROM orders"

order_id   customer       amount
---------  -------------  --------
1          Ada Lovelace   25.50
2          Grace Hopper   12.00

(2 rows affected)

The restore lands in a brand-new database, orders-db-restored, sitting alongside the original orders-db — which still has the deleted row missing. Recovering the data means copying the row back or repointing the application, not that the restore silently replaced anything.

Practice

1. List the automatic restore points currently available for the database.
az sql db list-deleted --resource-group learn-rg --server learn-sql-srv --output table
az sql db show --resource-group learn-rg --server learn-sql-srv --name orders-db --query "earliestRestoreDate"
$ az sql db show --resource-group learn-rg --server learn-sql-srv --name orders-db --query "earliestRestoreDate"
"2026-09-05T15:40:00+00:00"

earliestRestoreDate marks the start of the retention window — any timestamp between it and now is a valid --time for az sql db restore.

2. Scale the database up to handle more load, then confirm the new tier.
az sql db update --resource-group learn-rg --server learn-sql-srv --name orders-db --service-objective S1
az sql db show --resource-group learn-rg --server learn-sql-srv --name orders-db --query "currentServiceObjectiveName" --output tsv
$ az sql db show --resource-group learn-rg --server learn-sql-srv --name orders-db --query "currentServiceObjectiveName" --output tsv
S1

Scaling a DTU-based tier (Basic, Standard S-tiers, Premium P-tiers) briefly interrupts active connections during the switch but requires no schema or connection-string change.

3. Allow Azure services (like a Function App in the same subscription) to reach the database.
az sql server firewall-rule create \
  --resource-group learn-rg \
  --server learn-sql-srv \
  --name AllowAzureServices \
  --start-ip-address 0.0.0.0 \
  --end-ip-address 0.0.0.0
$ az sql server firewall-rule create --resource-group learn-rg --server learn-sql-srv --name AllowAzureServices --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0
{
  "name": "AllowAzureServices",
  "startIpAddress": "0.0.0.0",
  "endIpAddress": "0.0.0.0"
}

0.0.0.0-0.0.0.0 is a special-cased rule Azure recognizes as “allow any Azure-hosted resource,” not a literal IP range — it doesn’t open the server to the public internet.

4. Delete the restored database once you've recovered what you needed from it.
az sql db delete --resource-group learn-rg --server learn-sql-srv --name orders-db-restored --yes
$ az sql db delete --resource-group learn-rg --server learn-sql-srv --name orders-db-restored --yes

Restored databases bill exactly like any other database while they exist — the point of restoring to a new name is inspection and recovery, not to keep two permanent copies running.

5. Export the database to a .bacpac file for long-term archival outside Azure's automatic backup window.
az sql db export \
  --resource-group learn-rg \
  --server learn-sql-srv \
  --name orders-db \
  --admin-user sqladmin \
  --admin-password "Tr0ub4dor&3xample!" \
  --storage-key-type StorageAccessKey \
  --storage-key "<account-key>" \
  --storage-uri "https://learnstorage482.blob.core.windows.net/backups/orders-db.bacpac"
$ az sql db export --resource-group learn-rg --server learn-sql-srv --name orders-db ...
{
  "requestId": "8f2a1c6d-9e3b-4a17-8c5f-2d6a0b9e7f41",
  "status": "Succeeded"
}

Automatic backups only cover the retention window (7-35 days depending on tier); a .bacpac export is a portable file you control the lifetime of, useful for retention beyond what Azure keeps automatically or for moving the schema and data elsewhere entirely.

Next: Azure Functions — writing, deploying, and invoking a function, and what changes between a cold start and a warm one.

Frequently Asked Questions

What is the difference between an Azure SQL server and an Azure SQL Database?
The logical server (created with az sql server create) is a management and connectivity boundary — it holds the fully qualified DNS name, firewall rules, and admin login — but it doesn't run on dedicated hardware and isn't billed itself. The database is the actual billed, storage-and-compute resource that lives under that server; one server can host many databases.
Why did my connection fail with a firewall error even though the server exists?
Azure SQL blocks all inbound connections by default, including from Azure services unless you explicitly allow them. A client's IP address must be added via a server-level firewall rule (az sql server firewall-rule create) before any tool, including sqlcmd, can reach it — this is enforced before authentication is even attempted.
Do I need to configure backups myself?
No. Azure SQL Database takes automatic backups — full weekly, differential every 12-24 hours, and transaction log backups every 5-10 minutes — with no setup required, and point-in-time restore works out of the box within the configured retention window (7 days by default on most tiers).
What happens to the original database when I restore to a point in time?
Nothing — a point-in-time restore always creates a new database with a new name, alongside the original, rather than overwriting it in place. You inspect or promote the restored copy, then decide whether to rename it, swap connection strings, or drop the original.