Azure SQL Managed Instance Monitoring

A logistics company called me after its order system froze every Tuesday at 9 a.m. The database was an Azure SQL Managed Instance, and nobody could say why. There were no alerts, no saved logs, and no baseline of normal behavior. The team found out about each outage from angry warehouse supervisors.

The cause turned out to be a weekly report that caused heavy blocking and pushed CPU to the limit. We found it in under an hour once we had the right data. Good Azure SQL Managed Instance monitoring turns that kind of guesswork into a short, repeatable investigation.

This guide shows you what to watch, how to send metrics and logs to one place, and how to build alerts that fire before users complain. You will also learn the T-SQL checks, security settings, and cost controls that keep monitoring useful and affordable.

Azure SQL Managed Instance Monitoring: What to Watch

Azure SQL Managed Instance is a fully managed SQL Server engine that sits inside your own virtual network. It offers near-complete SQL Server compatibility, which makes it a common target for lift-and-shift migrations. If you are still deciding between services, read the comparison of Azure SQL Database vs. Managed Instance.

If you need to build one first, follow this guide to create a SQL Managed Instance in Azure.

Monitoring has three layers, and each answers a different question:

  • Platform metrics show resource health, such as CPU, storage, and I/O. Azure collects these automatically.
  • Resource logs show what happened inside the engine, such as errors, timeouts, blocks, and deadlocks. You must turn these on.
  • Database-level insights show which queries and waits cause the pain. You get these from Query Store and dynamic management views (DMVs), which are built-in SQL Server views that expose live engine state.

These Azure metrics matter most for a Managed Instance:

MetricWhat it tells youWhy it matters
avg_cpu_percentAverage CPU useSustained highs mean slow queries or an undersized instance
storage_space_used_mbSpace used by dataCompare with reserved storage to avoid hitting the limit
reserved_storage_mbStorage you have provisionedYour ceiling until you scale
io_requestsRead and write operationsSpikes point to scans and missing indexes
virtual_core_countProvisioned vCoresConfirms your current size

Storage deserves special attention. A Managed Instance has a fixed reserved storage size. When used space reaches that limit, writes fail and the application breaks. CPU spikes recover on their own, but a full disk does not.

The platform side is handled by Azure Monitor, which collects metrics and logs and triggers alerts. If you are new to it, read what Azure Monitor does and the overview of Azure monitoring tools.

Pro Tip: In my experience, teams alert on CPU first and storage last. I do the reverse. A CPU spike annoys users, but a full instance stops the business.

Plan Your Monitoring Architecture

Before you click anything, decide where the data goes and who can see it.

Central Log Analytics workspace. A Log Analytics workspace is a storage and query service for logs. Send Managed Instance data to one workspace per environment, or one shared workspace for a platform team. Keep production and non-production separate so retention and access rules differ.

Same region. Put the workspace in the same region as the instance when possible. It simplifies data residency and avoids confusing cross-region setups.

Resource group and tags. Keep monitoring resources in a dedicated resource group, such as rg-monitoring-prod. See how to create a resource group in Azure if you need the steps. Apply Azure tags like owner, cost center, and environment so you can trace log costs later.

Access control. Not everyone who views dashboards should change the database. Use Azure RBAC, which grants permissions through roles at a chosen scope. Learn the basics in what Azure RBAC is. Give support staff the Monitoring Reader role and give DBAs Log Analytics Reader on the workspace. Reserve Contributor for the few people who deploy changes.

Sign-in. Use Microsoft Entra ID for authentication, with multifactor authentication on admin accounts. If Entra is new to you, start with the Microsoft Entra ID tutorial for beginners.

Pro Tip: I always write down three numbers before configuring alerts: the normal CPU range, the normal storage growth per week, and the busiest hour of the day. Alerts without a baseline create noise, and people learn to ignore noise.

Configure Azure SQL Managed Instance Monitoring Step by Step

Now build the pipeline. You will create a workspace, connect the instance, create an action group, and add alerts.

Step 1: Create the Log Analytics Workspace

az monitor log-analytics workspace create \
--resource-group rg-monitoring-prod \
--workspace-name log-sqlmi-prod-eus \
--location eastus \
--retention-time 90

This creates a workspace named log-sqlmi-prod-eus in East US. The --retention-time 90 setting keeps data for 90 days. Longer retention helps with audits and trend analysis, but you pay for it. Match retention to a real requirement, not a guess.

Step 2: Turn On Diagnostic Settings

A diagnostic setting tells Azure which metrics and logs to send to a destination. First, get the resource IDs:

MI_ID=$(az sql mi show \
--name <managed-instance-name> \
--resource-group <resource-group-name> \
--query id --output tsv)

WS_ID=$(az monitor log-analytics workspace show \
--resource-group rg-monitoring-prod \
--workspace-name log-sqlmi-prod-eus \
--query id --output tsv)

The --query id --output tsv part returns only the ID as plain text, which you store in a shell variable. Using variables avoids copy-and-paste mistakes with long IDs. For more command patterns, see the Azure CLI tutorial.

Now connect the instance to the workspace:

az monitor diagnostic-settings create \
--name diag-sqlmi-to-law \
--resource "$MI_ID" \
--workspace "$WS_ID" \
--logs '[{"category":"ResourceUsageStats","enabled":true}]' \
--metrics '[{"category":"AllMetrics","enabled":true}]'

This sends instance-level resource usage logs and all metrics to your workspace. The --logs and --metrics parameters take JSON that names each category. Note that engine-level events like blocks, deadlocks, timeouts, and errors are configured separately on the individual databases. Repeat the command against each database’s resource ID for those categories. Only enable categories you will actually use, because every category adds ingestion cost.

Step 3: Create an Action Group

An action group defines who gets notified and how.

az monitor action-group create \
--name ag-dba-oncall \
--resource-group rg-monitoring-prod \
--short-name dbaoncall \
--action email dba-team <dba-team-email-address>

This creates a group named ag-dba-oncall that emails your DBA team. The --short-name is limited in length and appears in text messages. In a real environment, route critical alerts to a paging tool or a team chat channel, not a personal inbox.

Step 4: Create Metric Alerts

az monitor metrics alert create \
--name alert-sqlmi-cpu-high \
--resource-group rg-monitoring-prod \
--scopes "$MI_ID" \
--condition "avg avg_cpu_percent > 85" \
--window-size 15m \
--evaluation-frequency 5m \
--severity 2 \
--action ag-dba-oncall \
--description "Managed Instance CPU above 85 percent for 15 minutes"

Here is what the key parameters do:

  • --condition "avg avg_cpu_percent > 85" fires when average CPU passes 85 percent.
  • --window-size 15m looks at 15 minutes of data, so a brief spike does not wake anyone.
  • --evaluation-frequency 5m checks every five minutes.
  • --severity 2 marks it as a warning-level alert. Reserve severity 0 or 1 for outages.

Add a storage alert the same way, using a threshold based on your reserved storage:

az monitor metrics alert create \
--name alert-sqlmi-storage-high \
--resource-group rg-monitoring-prod \
--scopes "$MI_ID" \
--condition "avg storage_space_used_mb > <threshold-in-mb>" \
--window-size 30m \
--evaluation-frequency 15m \
--severity 1 \
--action ag-dba-oncall

Set <threshold-in-mb> to about 85 percent of your reserved storage. That gives you time to scale before writes fail. Use severity 1 because a full instance is an emergency.

Pro Tip: I create one “warning” and one “critical” alert for CPU and storage. The warning goes to chat, the critical one pages someone. Everything going to one channel is the fastest way to get alerts ignored.

Make It Repeatable

If you build several instances, define the diagnostic setting as code. Repeatable deployments mean every instance ships with monitoring, so nobody forgets it.

param managedInstanceName string
param workspaceId string

resource managedInstance 'Microsoft.Sql/managedInstances@2023-05-01-preview' existing = {
name: managedInstanceName
}

resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
name: 'diag-sqlmi-to-law'
scope: managedInstance
properties: {
workspaceId: workspaceId
metrics: [
{
category: 'AllMetrics'
enabled: true
}
]
logs: [
{
category: 'ResourceUsageStats'
enabled: true
}
]
}
}

The existing keyword refers to a Managed Instance that is already deployed. The second resource attaches a diagnostic setting to it, using scope to point at the instance. The workspace ID is a parameter, so you pass a different value for dev and prod. No IDs or secrets are hardcoded.

Check Metrics with PowerShell

For quick scripted checks, use Azure PowerShell:

powershell$ResourceId = "<managed-instance-resource-id>"

Get-AzMetric `
  -ResourceId $ResourceId `
  -MetricName "avg_cpu_percent" `
  -TimeGrain 00:05:00 `
  -StartTime (Get-Date).AddHours(-6) `
  -AggregationType Average

This returns average CPU in five-minute slices for the last six hours. The -TimeGrain parameter sets the slice size, and -AggregationTyp