references/capacity-planning.md
# Azure Capacity Planning
Analyze resource capacity and plan for growth across compute, networking, containers, databases, and infrastructure services.
## Table of Contents
- [Compute Capacity](#compute-capacity)
- [Network Capacity](#network-capacity)
- [Container & Serverless Capacity](#container--serverless-capacity)
- [Database & Storage Capacity](#database--storage-capacity)
- [Infrastructure Capacity](#infrastructure-capacity)
## Compute Capacity
VM SKU distribution across regions:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][properties][hardwareProfile][vmSize],
powerState = azjson[configuration][properties][extended][instanceView][powerState][displayStatus]
| summarize vm_count = count(), by: {vmSize, powerState, azure.location}
| sort vm_count desc
```
VMSS capacity analysis (current instance count vs configured SKU):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][sku][name],
tier = azjson[configuration][sku][tier],
capacity = azjson[configuration][sku][capacity]
| fields name, vmSize, tier, capacity, azure.resource.group, azure.location
| sort capacity desc
```
## Network Capacity
Subnet count per virtual network:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS"
| summarize subnet_count = count(), by: {name, azure.location}
| sort subnet_count desc
```
NIC usage across resource groups:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES"
| summarize nic_count = count(), by: {azure.resource.group, azure.location}
| sort nic_count desc
```
Public IP address counts by region:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_PUBLICIPADDRESSES"
| summarize ip_count = count(), by: {azure.location, azure.resource.group}
| sort ip_count desc
```
Virtual network address space inventory:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd addressPrefixes = toString(azjson[configuration][properties][addressSpace][addressPrefixes]),
ddosProtection = azjson[configuration][properties][enableDdosProtection]
| fields name, addressPrefixes, ddosProtection, azure.location, azure.resource.group
```
## Container & Serverless Capacity
AKS cluster capacity overview:
```dql
smartscapeNodes "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd k8sVersion = azjson[configuration][properties][kubernetesVersion],
currentVersion = azjson[configuration][properties][currentKubernetesVersion],
powerState = azjson[configuration][properties][powerState][code],
skuTier = azjson[configuration][sku][tier]
| fields name, k8sVersion, currentVersion, powerState, skuTier, azure.resource.group, azure.location
```
AKS agent pool sizing:
```dql
smartscapeNodes "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS_AGENTPOOLS"
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][vmSize],
nodeCount = azjson[configuration][count],
minCount = azjson[configuration][minCount],
maxCount = azjson[configuration][maxCount],
enableAutoScaling = azjson[configuration][enableAutoScaling],
mode = azjson[configuration][mode]
| fields name, vmSize, nodeCount, minCount, maxCount, enableAutoScaling, mode, azure.resource.group
| sort nodeCount desc
```
App Service Plan capacity and headroom:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SERVERFARMS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
capacity = azjson[configuration][sku][capacity]
| fields name, skuName, skuTier, capacity, azure.resource.group, azure.location
| sort capacity desc
```
Container App scaling configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_CONTAINERAPPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minReplicas = azjson[configuration][properties][template][scale][minReplicas],
maxReplicas = azjson[configuration][properties][template][scale][maxReplicas],
runningStatus = azjson[configuration][properties][runningStatus]
| fields name, minReplicas, maxReplicas, runningStatus, azure.resource.group, azure.location
```
## Database & Storage Capacity
SQL database sizing and tier distribution:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
skuCapacity = azjson[configuration][sku][capacity],
maxSizeBytes = azjson[configuration][properties][maxSizeBytes],
zoneRedundant = azjson[configuration][properties][zoneRedundant]
| fields name, skuName, skuTier, skuCapacity, maxSizeBytes, zoneRedundant, azure.resource.group
| sort skuCapacity desc
```
Storage account distribution across regions:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
kind = azjson[configuration][kind],
accessTier = azjson[configuration][properties][accessTier]
| summarize account_count = count(), by: {skuName, kind, azure.location}
| sort account_count desc
```
Event Hub namespace throughput units:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
throughputUnits = azjson[configuration][sku][capacity],
isAutoInflate = azjson[configuration][properties][isAutoInflateEnabled],
maxThroughputUnits = azjson[configuration][properties][maximumThroughputUnits]
| fields name, skuName, throughputUnits, isAutoInflate, maxThroughputUnits, azure.location
```
## Infrastructure Capacity
VPN gateway inventory:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd gatewaySku = azjson[configuration][properties][sku][name],
gatewayType = azjson[configuration][properties][gatewayType]
| fields name, gatewaySku, gatewayType, azure.resource.group, azure.location
```
ExpressRoute circuit inventory:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_EXPRESSROUTECIRCUITS"
| fields name, id, azure.resource.group, azure.location
```
references/cost-optimization.md
# Azure Cost Optimization
Identify cost savings opportunities and optimize Azure spending across compute, storage, networking, and managed services.
## Table of Contents
- [Compute Costs](#compute-costs)
- [Storage Costs](#storage-costs)
- [Network Costs](#network-costs)
- [Database Costs](#database-costs)
- [Serverless Costs](#serverless-costs)
- [Infrastructure Management Costs](#infrastructure-management-costs)
## Compute Costs
Analyze VM SKU distribution for right-sizing opportunities:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][properties][hardwareProfile][vmSize],
powerState = azjson[configuration][properties][extended][instanceView][powerState][displayStatus]
| summarize vm_count = count(), by: {vmSize, azure.location}
| sort vm_count desc
```
Find deallocated VMs (still incurring disk costs):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| parse azure.object, "JSON:azjson"
| fieldsAdd powerState = azjson[configuration][properties][extended][instanceView][powerState][displayStatus]
| filter powerState != "VM running"
| fields name, id, powerState, azure.resource.group, azure.location
```
Analyze VMSS instance sizing and capacity:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][sku][name],
capacity = azjson[configuration][sku][capacity]
| summarize vmss_count = count(), total_instances = sum(capacity), by: {vmSize}
| sort total_instances desc
```
## Storage Costs
Find unattached managed disks by SKU and size (wasted spend):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd diskState = azjson[configuration][properties][diskState],
diskSizeGB = azjson[configuration][properties][diskSizeGB],
skuName = azjson[configuration][sku][name]
| filter diskState == "Unattached"
| fields name, skuName, diskSizeGB, azure.resource.group, azure.location
| sort diskSizeGB desc
```
Analyze storage account access tier distribution:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd accessTier = azjson[configuration][properties][accessTier],
kind = azjson[configuration][kind]
| summarize account_count = count(), by: {accessTier, kind}
| sort account_count desc
```
Analyze storage account redundancy for cost reduction:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier]
| summarize account_count = count(), by: {skuName, skuTier}
| sort account_count desc
```
## Network Costs
Count public IP addresses (each incurs cost):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_PUBLICIPADDRESSES"
| parse azure.object, "JSON:azjson"
| fieldsAdd ipConfig = azjson[configuration][properties][ipConfiguration]
| fieldsAdd is_associated = if(isNotNull(ipConfig), "associated", else: "unassociated")
| summarize ip_count = count(), by: {is_associated, azure.location}
| sort ip_count desc
```
Analyze VPN gateway SKUs:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd gatewaySku = azjson[configuration][properties][sku][name],
gatewayType = azjson[configuration][properties][gatewayType]
| fields name, gatewaySku, gatewayType, azure.resource.group, azure.location
```
Review load balancer SKUs:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name]
| summarize lb_count = count(), by: {skuName, azure.location}
| sort lb_count desc
```
## Database Costs
Analyze SQL database tier and DTU allocation:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
skuCapacity = azjson[configuration][sku][capacity],
serviceObjective = azjson[configuration][properties][currentServiceObjectiveName]
| fields name, skuName, skuTier, skuCapacity, serviceObjective, azure.resource.group
| sort skuCapacity desc
```
Summarize SQL database costs by tier:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuTier = azjson[configuration][sku][tier],
skuCapacity = azjson[configuration][sku][capacity]
| summarize db_count = count(), total_capacity = sum(skuCapacity), by: {skuTier}
| sort total_capacity desc
```
Review Redis cache SKU distribution:
```dql
smartscapeNodes "AZURE_MICROSOFT_CACHE_REDIS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][properties][sku][name],
skuFamily = azjson[configuration][properties][sku][family],
skuCapacity = azjson[configuration][properties][sku][capacity]
| fields name, skuName, skuFamily, skuCapacity, azure.resource.group, azure.location
```
## Serverless Costs
Analyze Azure Functions runtime distribution:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd kind = azjson[configuration][kind],
runtime = azjson[configuration][properties][siteConfig][linuxFxVersion],
sku = azjson[configuration][properties][sku]
| filter contains(toString(kind), "functionapp")
| summarize function_count = count(), by: {runtime, sku}
| sort function_count desc
```
Analyze App Service Plan SKU distribution:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SERVERFARMS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
skuCapacity = azjson[configuration][sku][capacity]
| summarize plan_count = count(), total_instances = sum(skuCapacity), by: {skuName, skuTier}
| sort plan_count desc
```
## Infrastructure Management Costs
List Key Vaults and their configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_KEYVAULT_VAULTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rbacAuth = azjson[configuration][properties][enableRbacAuthorization],
softDelete = azjson[configuration][properties][enableSoftDelete]
| fields name, rbacAuth, softDelete, azure.resource.group, azure.location
```
Review monitoring resource counts (Log Analytics workspaces, Application Insights):
```dql
smartscapeNodes "AZURE_MICROSOFT_OPERATIONALINSIGHTS_WORKSPACES",
"AZURE_MICROSOFT_INSIGHTS_COMPONENTS"
| summarize count = count(), by: {type, azure.location}
| sort count desc
```
references/database-monitoring.md
# Azure Database Monitoring
Monitor and analyze Azure database services including Azure SQL, Cosmos DB, and Redis Cache.
## Table of Contents
- [Database Entity Types](#database-entity-types)
- [Azure SQL Monitoring](#azure-sql-monitoring)
- [Cosmos DB Monitoring](#cosmos-db-monitoring)
- [Redis Cache](#redis-cache)
- [Database Security](#database-security)
- [Cross-Service Analysis](#cross-service-analysis)
## Database Entity Types
All these types support the standard discovery pattern: `smartscapeNodes "<TYPE>" | fields name, id, azure.subscription, azure.resource.group, azure.location, ...`
| Entity Type | Description |
|---|---|
| `AZURE_MICROSOFT_SQL_SERVERS` | Azure SQL logical servers |
| `AZURE_MICROSOFT_SQL_SERVERS_DATABASES` | Azure SQL databases |
| `AZURE_MICROSOFT_CACHE_REDIS` | Azure Cache for Redis |
| `AZURE_MICROSOFT_CACHE_REDISENTERPRISE` | Azure Cache for Redis Enterprise |
| `AZURE_MICROSOFT_CACHE_REDISENTERPRISE_DATABASES` | Redis Enterprise databases |
| `AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS` | Cosmos DB accounts |
| `AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS_SQLDATABASES` | Cosmos DB SQL databases |
## Azure SQL Monitoring
List all SQL servers:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd fqdn = azjson[configuration][properties][fullyQualifiedDomainName],
state = azjson[configuration][properties][state],
version = azjson[configuration][properties][version],
adminLogin = azjson[configuration][properties][administratorLogin]
| fields name, fqdn, state, version, adminLogin, azure.resource.group, azure.location
```
List all SQL databases with SKU and tier details:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
skuCapacity = azjson[configuration][sku][capacity],
status = azjson[configuration][properties][status],
serviceObjective = azjson[configuration][properties][currentServiceObjectiveName],
maxSizeBytes = azjson[configuration][properties][maxSizeBytes]
| fields name, skuName, skuTier, skuCapacity, status, serviceObjective, maxSizeBytes,
azure.resource.group, azure.location
```
Find SQL databases by service tier:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuTier = azjson[configuration][sku][tier]
| summarize db_count = count(), by: {skuTier}
| sort db_count desc
```
Find zone-redundant SQL databases:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES"
| parse azure.object, "JSON:azjson"
| fieldsAdd zoneRedundant = azjson[configuration][properties][zoneRedundant],
skuTier = azjson[configuration][sku][tier]
| fields name, zoneRedundant, skuTier, azure.resource.group, azure.location
```
Find SQL databases belonging to a specific server (SQL Database → SQL Server backward traversal):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS"
| filter name == "<SQL_SERVER_NAME>"
| traverse "*", "AZURE_MICROSOFT_SQL_SERVERS_DATABASES", direction:backward
| fields name, id, azure.resource.group
```
Analyze backup redundancy configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES"
| parse azure.object, "JSON:azjson"
| fieldsAdd backupRedundancy = azjson[configuration][properties][currentBackupStorageRedundancy],
skuTier = azjson[configuration][sku][tier]
| summarize db_count = count(), by: {backupRedundancy, skuTier}
| sort db_count desc
```
## Cosmos DB Monitoring
List Cosmos DB accounts with configuration details:
```dql
smartscapeNodes "AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd accountKind = azjson[configuration][kind],
apiTypes = azjson[configuration][properties][EnabledApiTypes],
consistencyLevel = azjson[configuration][properties][consistencyPolicy][defaultConsistencyLevel],
autoFailover = azjson[configuration][properties][enableAutomaticFailover],
multiWrite = azjson[configuration][properties][enableMultipleWriteLocations],
endpoint = azjson[configuration][properties][documentEndpoint]
| fields name, accountKind, apiTypes, consistencyLevel, autoFailover, multiWrite, endpoint,
azure.resource.group, azure.location
```
Find serverless Cosmos DB accounts:
```dql
smartscapeNodes "AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd capabilities = azjson[configuration][properties][capabilities]
| expand capabilities
| filter capabilities[name] == "EnableServerless"
| fields name, azure.resource.group, azure.location
```
Analyze Cosmos DB backup configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd backupType = azjson[configuration][properties][backupPolicy][type],
vnetFilter = azjson[configuration][properties][isVirtualNetworkFilterEnabled]
| fields name, backupType, vnetFilter, azure.resource.group, azure.location
```
## Redis Cache
List all Redis Cache instances with configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_CACHE_REDIS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][properties][sku][name],
skuFamily = azjson[configuration][properties][sku][family],
skuCapacity = azjson[configuration][properties][sku][capacity],
hostName = azjson[configuration][properties][hostName],
redisVersion = azjson[configuration][properties][redisVersion],
sslPort = azjson[configuration][properties][sslPort]
| fields name, skuName, skuFamily, skuCapacity, hostName, redisVersion, sslPort,
azure.resource.group, azure.location
```
List Redis Enterprise clusters:
```dql
smartscapeNodes "AZURE_MICROSOFT_CACHE_REDISENTERPRISE"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuCapacity = azjson[configuration][sku][capacity]
| fields name, skuName, skuCapacity, azure.resource.group, azure.location
```
Find Redis Enterprise databases:
```dql
smartscapeNodes "AZURE_MICROSOFT_CACHE_REDISENTERPRISE_DATABASES"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Check Redis non-SSL port status:
```dql
smartscapeNodes "AZURE_MICROSOFT_CACHE_REDIS"
| parse azure.object, "JSON:azjson"
| fieldsAdd enableNonSslPort = azjson[configuration][properties][enableNonSslPort],
minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| fields name, enableNonSslPort, minimumTlsVersion, azure.resource.group
```
## Database Security
Find SQL servers with public network access enabled:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd publicAccess = azjson[configuration][properties][publicNetworkAccess],
minTls = azjson[configuration][properties][minimalTlsVersion],
outboundRestriction = azjson[configuration][properties][restrictOutboundNetworkAccess]
| fields name, publicAccess, minTls, outboundRestriction, azure.resource.group, azure.location
```
Analyze TLS versions across all database services:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTls = azjson[configuration][properties][minimalTlsVersion], service = "SQL Server"
| fields name, minTls, service
| append [
smartscapeNodes "AZURE_MICROSOFT_CACHE_REDIS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTls = azjson[configuration][properties][minimumTlsVersion], service = "Redis"
| fields name, minTls, service
]
| append [
smartscapeNodes "AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTls = azjson[configuration][properties][minimalTlsVersion], service = "Cosmos DB"
| fields name, minTls, service
]
| sort service, minTls
```
Find Redis instances with non-SSL port enabled (security risk):
```dql
smartscapeNodes "AZURE_MICROSOFT_CACHE_REDIS"
| parse azure.object, "JSON:azjson"
| fieldsAdd enableNonSslPort = azjson[configuration][properties][enableNonSslPort]
| filter enableNonSslPort == true
| fields name, azure.resource.group, azure.location
```
## Cross-Service Analysis
Count all database resources by type:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS", "AZURE_MICROSOFT_SQL_SERVERS_DATABASES",
"AZURE_MICROSOFT_CACHE_REDIS", "AZURE_MICROSOFT_CACHE_REDISENTERPRISE",
"AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| summarize db_count = count(), by: {type}
| sort db_count desc
```
Count databases across regions:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES", "AZURE_MICROSOFT_CACHE_REDIS",
"AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| summarize db_count = count(), by: {type, azure.location}
| sort azure.location, db_count desc
```
Find all databases in a specific resource group:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS", "AZURE_MICROSOFT_SQL_SERVERS_DATABASES",
"AZURE_MICROSOFT_CACHE_REDIS", "AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| filter azure.resource.group == "<RESOURCE_GROUP>"
| fields type, name, azure.location, azure.provisioning_state
```
references/load-balancing-api.md
# Azure Load Balancing & API Management
Monitor Azure Load Balancers, Application Gateways, and API Management services.
## Table of Contents
- [Load Balancing & API Entity Types](#load-balancing--api-entity-types)
- [Azure Load Balancer Topology Traversal](#azure-load-balancer-topology-traversal)
- [Application Gateway Configuration](#application-gateway-configuration)
- [WAF Configuration & Rule Analysis](#waf-configuration--rule-analysis)
- [WAF Mode Detection](#waf-mode-detection)
- [WAF-Enabled Gateway Inventory](#waf-enabled-gateway-inventory)
- [WAF Rule Configuration](#waf-rule-configuration)
- [Disabled Rule Groups](#disabled-rule-groups)
- [WAF Exclusions](#waf-exclusions)
- [Firewall Policy Association](#firewall-policy-association)
- [API Management](#api-management)
- [Security & Networking](#security--networking)
- [Cross-Service Analysis](#cross-service-analysis)
## Load Balancing & API Entity Types
All these types support the standard discovery pattern: `smartscapeNodes "<TYPE>" | fields name, id, azure.subscription, azure.resource.group, azure.location, ...`
| Entity Type | Description |
|---|---|
| `AZURE_MICROSOFT_NETWORK_LOADBALANCERS` | Azure Load Balancers (Standard and Basic SKU) |
| `AZURE_MICROSOFT_NETWORK_LOADBALANCERS_FRONTENDIPCONFIGURATIONS` | LB frontend IP configurations |
| `AZURE_MICROSOFT_NETWORK_LOADBALANCERS_BACKENDADDRESSPOOLS` | LB backend address pools |
| `AZURE_MICROSOFT_NETWORK_LOADBALANCERS_LOADBALANCINGRULES` | LB load balancing rules |
| `AZURE_MICROSOFT_NETWORK_LOADBALANCERS_OUTBOUNDRULES` | LB outbound rules |
| `AZURE_MICROSOFT_NETWORK_LOADBALANCERS_INBOUNDNATRULES` | LB inbound NAT rules |
| `AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS` | Application Gateways |
| `AZURE_MICROSOFT_APIMANAGEMENT_SERVICE` | API Management services |
## Azure Load Balancer Topology Traversal
### Complete LB → Backend Pool → VMSS Mapping
This is the most important query — maps load balancers through backend pools to the VMSS instances serving traffic:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name]
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_BACKENDADDRESSPOOLS", fieldsKeep:{skuName, name, id}
| fieldsAdd poolName = name, poolId = id
| traverse "*", "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS", direction:backward, fieldsKeep:{poolName, poolId}
| fieldsAdd lbName = dt.traverse.history[-2][name],
lbId = dt.traverse.history[-2][id],
lbSku = dt.traverse.history[-2][skuName]
| fields lbName, lbSku, poolName, name, id, azure.resource.group
```
### Simpler Traversals
LB to frontend IP configurations:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_FRONTENDIPCONFIGURATIONS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "lb."
| fields lb.name, name, id, azure.resource.group
```
LB to backend address pools:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_BACKENDADDRESSPOOLS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "lb."
| fields lb.name, name, id
```
LB to load balancing rules:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_LOADBALANCINGRULES"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "lb."
| fields lb.name, name, id
```
LB to outbound rules:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_OUTBOUNDRULES"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "lb."
| fields lb.name, name, id
```
### LB to AKS Cluster Mapping
Find which AKS clusters a load balancer is associated with:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| traverse "*", "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "lb."
| fields lb.name, name, id, azure.resource.group
```
Identify AKS-managed load balancers via tags:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| fieldsAdd aksCluster = tags[`aks-managed-cluster-name`]
| filter isNotNull(aksCluster)
| fields name, aksCluster, azure.resource.group, azure.location
```
### Load Balancer Configuration
List all load balancers with SKU:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name]
| fields name, skuName, azure.resource.group, azure.location, azure.provisioning_state
```
## Application Gateway Configuration
List Application Gateways with configuration details:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][properties][sku][name],
skuTier = azjson[configuration][properties][sku][tier],
skuCapacity = azjson[configuration][properties][sku][capacity],
operationalState = azjson[configuration][properties][operationalState],
enableHttp2 = azjson[configuration][properties][enableHttp2]
| fields name, skuName, skuTier, skuCapacity, operationalState, enableHttp2,
azure.resource.group, azure.location
```
Analyze Application Gateway backend pools:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd backendPools = azjson[configuration][properties][backendAddressPools]
| expand backendPools
| fieldsAdd poolName = backendPools[name]
| fields name, poolName, azure.resource.group
```
Analyze Application Gateway HTTP listeners:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd listeners = azjson[configuration][properties][httpListeners]
| expand listeners
| fieldsAdd listenerName = listeners[name],
protocol = listeners[properties][protocol],
hostName = listeners[properties][hostName]
| fields name, listenerName, protocol, hostName
```
Analyze Application Gateway routing rules:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = azjson[configuration][properties][requestRoutingRules]
| expand rules
| fieldsAdd ruleName = rules[name],
ruleType = rules[properties][ruleType],
priority = rules[properties][priority]
| fields name, ruleName, ruleType, priority
| sort priority asc
```
Application Gateway sub-resource entities for fine-grained traversal:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS_BACKENDADDRESSPOOLS",
"AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS_HTTPLISTENERS",
"AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS_REQUESTROUTINGRULES",
"AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS_URLPATHMAPS",
"AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS_FRONTENDIPCONFIGURATIONS",
"AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS_FRONTENDPORTS"
| fields type, name, id, azure.resource.group
| sort type
```
## WAF Configuration & Rule Analysis
Queries for investigating Web Application Firewall configuration on Application Gateways. Essential during false-positive incident investigation.
### WAF Mode Detection
List WAF-enabled gateways with their mode (Detection vs Prevention):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
wafMode = azjson[configuration][properties][webApplicationFirewallConfiguration][firewallMode],
skuTier = azjson[configuration][properties][sku][tier]
| filter wafEnabled == true
| fields name, wafMode, skuTier, azure.resource.group, azure.location
```
Find gateways in Detection mode only (logging but not blocking — common during rollout or after false-positive issues):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
wafMode = azjson[configuration][properties][webApplicationFirewallConfiguration][firewallMode]
| filter wafEnabled == true and wafMode == "Detection"
| fields name, wafMode, azure.resource.group, azure.location
```
### WAF-Enabled Gateway Inventory
Full inventory with SKU, WAF status, and rule set info:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
wafMode = azjson[configuration][properties][webApplicationFirewallConfiguration][firewallMode],
ruleSetType = azjson[configuration][properties][webApplicationFirewallConfiguration][ruleSetType],
ruleSetVersion = azjson[configuration][properties][webApplicationFirewallConfiguration][ruleSetVersion],
skuName = azjson[configuration][properties][sku][name],
skuTier = azjson[configuration][properties][sku][tier]
| fields name, skuName, skuTier, wafEnabled, wafMode, ruleSetType, ruleSetVersion,
azure.resource.group, azure.location
```
Find gateways with a WAF-capable SKU but WAF not enabled:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
skuTier = azjson[configuration][properties][sku][tier]
| filter contains(toString(skuTier), "WAF") and wafEnabled != true
| fields name, skuTier, wafEnabled, azure.resource.group, azure.location
```
### WAF Rule Configuration
Inspect rule set type, version, and body inspection limits:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
ruleSetType = azjson[configuration][properties][webApplicationFirewallConfiguration][ruleSetType],
ruleSetVersion = azjson[configuration][properties][webApplicationFirewallConfiguration][ruleSetVersion],
maxRequestBodySizeInKb = azjson[configuration][properties][webApplicationFirewallConfiguration][maxRequestBodySizeInKb],
fileUploadLimitInMb = azjson[configuration][properties][webApplicationFirewallConfiguration][fileUploadLimitInMb],
requestBodyCheck = azjson[configuration][properties][webApplicationFirewallConfiguration][requestBodyCheck]
| filter wafEnabled == true
| fields name, ruleSetType, ruleSetVersion, maxRequestBodySizeInKb, fileUploadLimitInMb, requestBodyCheck,
azure.resource.group
```
Summarize rule set versions in use across all gateways:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
ruleSetType = azjson[configuration][properties][webApplicationFirewallConfiguration][ruleSetType],
ruleSetVersion = azjson[configuration][properties][webApplicationFirewallConfiguration][ruleSetVersion]
| filter wafEnabled == true
| summarize count = count(), by: {ruleSetType, ruleSetVersion}
| sort count desc
```
### Disabled Rule Groups
This is the most critical section for false-positive investigation. Disabled rule groups indicate rules that were turned off to avoid blocking legitimate traffic.
Expand disabled rule groups to show which rules are disabled per gateway:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
disabledRuleGroups = azjson[configuration][properties][webApplicationFirewallConfiguration][disabledRuleGroups]
| filter wafEnabled == true
| expand disabledRuleGroups
| fieldsAdd ruleGroupName = disabledRuleGroups[ruleGroupName],
rules = toString(disabledRuleGroups[rules])
| fields name, ruleGroupName, rules, azure.resource.group
```
Find gateways with no disabled rules (fully enforcing all rule groups):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
disabledRuleGroups = azjson[configuration][properties][webApplicationFirewallConfiguration][disabledRuleGroups]
| filter wafEnabled == true and (isnull(disabledRuleGroups) or arraySize(disabledRuleGroups) == 0)
| fields name, azure.resource.group, azure.location
```
> **Investigation tip:** Common false-positive rule groups include `REQUEST-942-APPLICATION-ATTACK-SQLI` (SQL injection) and `REQUEST-941-APPLICATION-ATTACK-XSS` (cross-site scripting). If these appear in `disabledRuleGroups`, the team likely encountered false positives from application payloads matching SQL/XSS patterns. Check whether exclusions (below) would be a more targeted fix than disabling entire rule groups.
### WAF Exclusions
Expand WAF exclusions to see which request attributes are excluded from rule evaluation:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
exclusions = azjson[configuration][properties][webApplicationFirewallConfiguration][exclusions]
| filter wafEnabled == true
| expand exclusions
| fieldsAdd matchVariable = exclusions[matchVariable],
selectorMatchOperator = exclusions[selectorMatchOperator],
selector = exclusions[selector]
| fields name, matchVariable, selectorMatchOperator, selector, azure.resource.group
```
> **Note:** Common `matchVariable` values are `RequestHeaderNames`, `RequestCookieNames`, `RequestArgNames`, and `RequestBodyPostArgNames`. Exclusions are more targeted than disabling entire rule groups and are the preferred approach for handling false positives.
### Firewall Policy Association
Find gateways with a linked firewall policy (newer policy-based WAF configuration model):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd firewallPolicyId = azjson[configuration][properties][firewallPolicy][id],
wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled]
| filter isNotNull(firewallPolicyId)
| fields name, firewallPolicyId, wafEnabled, azure.resource.group, azure.location
```
> **Note:** Azure supports two WAF configuration models: inline `webApplicationFirewallConfiguration` (classic) and linked `firewallPolicy` (newer). When a firewall policy is linked, rule configuration and exclusions are managed on the policy resource rather than inline on the gateway. The queries above inspect inline configuration; policy-based WAF requires querying the policy resource separately.
## API Management
List API Management services with configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_APIMANAGEMENT_SERVICE"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuCapacity = azjson[configuration][sku][capacity],
gatewayUrl = azjson[configuration][properties][gatewayUrl],
devPortalUrl = azjson[configuration][properties][developerPortalUrl],
vnetType = azjson[configuration][properties][virtualNetworkType],
platformVersion = azjson[configuration][properties][platformVersion]
| fields name, skuName, skuCapacity, gatewayUrl, devPortalUrl, vnetType, platformVersion,
azure.resource.group, azure.location
```
Find API Management policies and subscriptions:
```dql
smartscapeNodes "AZURE_MICROSOFT_APIMANAGEMENT_SERVICE_POLICIES",
"AZURE_MICROSOFT_APIMANAGEMENT_SERVICE_SUBSCRIPTIONS"
| fields type, name, id, azure.resource.group
```
## Security & Networking
Identify public vs. internal load balancers (check frontend IP configuration for public IP association):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_FRONTENDIPCONFIGURATIONS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| parse azure.object, "JSON:azjson"
| fieldsAdd publicIpId = azjson[configuration][properties][publicIPAddress][id],
privateIp = azjson[configuration][properties][privateIPAddress]
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "lb."
| fieldsAdd lbType = if(isNotNull(publicIpId), "Public", else: "Internal")
| fields lb.name, name, lbType, publicIpId, privateIp
```
Find API Management services exposed without VNet integration:
```dql
smartscapeNodes "AZURE_MICROSOFT_APIMANAGEMENT_SERVICE"
| parse azure.object, "JSON:azjson"
| fieldsAdd vnetType = azjson[configuration][properties][virtualNetworkType]
| filter vnetType == "None"
| fields name, vnetType, azure.resource.group, azure.location
```
## Cross-Service Analysis
Count all load balancing and API resources by type:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS",
"AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS",
"AZURE_MICROSOFT_APIMANAGEMENT_SERVICE"
| summarize count = count(), by: {type}
| sort count desc
```
Count load balancing resources by region:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS",
"AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS",
"AZURE_MICROSOFT_APIMANAGEMENT_SERVICE"
| summarize count = count(), by: {type, azure.location}
| sort azure.location, count desc
```
Find all load balancing resources in a specific resource group:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS",
"AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS",
"AZURE_MICROSOFT_APIMANAGEMENT_SERVICE"
| filter azure.resource.group == "<RESOURCE_GROUP>"
| fields type, name, azure.location, azure.provisioning_state
```
references/messaging-integration.md
# Azure Messaging & Integration
Monitor Azure Event Hubs, Service Bus, and messaging infrastructure.
## Table of Contents
- [Messaging & Integration Entity Types](#messaging--integration-entity-types)
- [Event Hubs](#event-hubs)
- [Service Bus](#service-bus)
- [Namespace Inventory](#namespace-inventory-1)
- [Zone Redundancy](#zone-redundancy-1)
- [TLS Configuration](#tls-configuration-1)
- [Public Network Access](#public-network-access)
- [Topics](#topics)
- [Subscriptions](#subscriptions)
- [Queue Configuration](#queue-configuration)
- [Namespace Traversals](#namespace-traversals)
- [Dead-Letter Queue Detection](#dead-letter-queue-detection)
- [Cross-Service Analysis](#cross-service-analysis)
## Messaging & Integration Entity Types
All these types support the standard discovery pattern: `smartscapeNodes "<TYPE>" | fields name, id, azure.subscription, azure.resource.group, azure.location, ...`
| Entity Type | Description |
|---|---|
| `AZURE_MICROSOFT_EVENTHUB_NAMESPACES` | Event Hub namespaces |
| `AZURE_MICROSOFT_EVENTHUB_NAMESPACES_EVENTHUBS` | Individual Event Hubs within a namespace |
| `AZURE_MICROSOFT_SERVICEBUS_NAMESPACES` | Service Bus namespaces |
| `AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES` | Service Bus queues |
| `AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS` | Service Bus topics |
| `AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS_SUBSCRIPTIONS` | Service Bus subscriptions |
## Event Hubs
### Namespace Inventory
List all Event Hub namespaces with configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
skuCapacity = azjson[configuration][sku][capacity],
status = azjson[configuration][properties][status],
kafkaEnabled = azjson[configuration][properties][kafkaEnabled],
zoneRedundant = azjson[configuration][properties][zoneRedundant]
| fields name, skuName, skuTier, skuCapacity, status, kafkaEnabled, zoneRedundant,
azure.resource.group, azure.location
```
Summarize Event Hub namespaces by SKU tier:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name]
| summarize ns_count = count(), by: {skuName}
| sort ns_count desc
```
### Kafka Enablement
Find Kafka-enabled Event Hub namespaces:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd kafkaEnabled = azjson[configuration][properties][kafkaEnabled],
skuName = azjson[configuration][sku][name]
| filter kafkaEnabled == true
| fields name, skuName, azure.resource.group, azure.location
```
### Throughput Units and Auto-Inflate
Analyze throughput unit allocation and auto-inflate configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd throughputUnits = azjson[configuration][sku][capacity],
autoInflate = azjson[configuration][properties][isAutoInflateEnabled],
maxThroughputUnits = azjson[configuration][properties][maximumThroughputUnits]
| fields name, throughputUnits, autoInflate, maxThroughputUnits,
azure.resource.group, azure.location
| sort throughputUnits desc
```
Find namespaces without auto-inflate (potential scaling risk):
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd autoInflate = azjson[configuration][properties][isAutoInflateEnabled],
throughputUnits = azjson[configuration][sku][capacity]
| filter autoInflate == false
| fields name, throughputUnits, azure.resource.group, azure.location
```
### Zone Redundancy
Find Event Hub namespaces that are not zone-redundant:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd zoneRedundant = azjson[configuration][properties][zoneRedundant],
skuName = azjson[configuration][sku][name]
| filter zoneRedundant == false
| fields name, skuName, azure.resource.group, azure.location
```
### TLS Configuration
Check minimum TLS version across Event Hub namespaces:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| summarize ns_count = count(), by: {minimumTlsVersion}
| sort minimumTlsVersion desc
```
Find namespaces with TLS version below 1.2:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| filter minimumTlsVersion != "1.2"
| fields name, minimumTlsVersion, azure.resource.group, azure.location
```
### Event Hub Entities
List all individual Event Hubs:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES_EVENTHUBS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Find Event Hubs belonging to a specific namespace (Event Hub → Namespace backward traversal):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| filter name == "<NAMESPACE_NAME>"
| traverse "*", "AZURE_MICROSOFT_EVENTHUB_NAMESPACES_EVENTHUBS", direction:backward
| fields name, id, azure.resource.group
```
Count Event Hubs per namespace:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES_EVENTHUBS"
| traverse "*", "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| fieldsAdd namespaceName = name
| summarize eh_count = count(), by: {namespaceName}
| sort eh_count desc
```
## Service Bus
### Namespace Inventory
List all Service Bus namespaces with configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
skuCapacity = azjson[configuration][sku][capacity],
status = azjson[configuration][properties][status],
zoneRedundant = azjson[configuration][properties][zoneRedundant],
minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion],
disableLocalAuth = azjson[configuration][properties][disableLocalAuth]
| fields name, skuName, skuTier, skuCapacity, status, zoneRedundant, minimumTlsVersion, disableLocalAuth,
azure.resource.group, azure.location
```
Summarize Service Bus namespaces by SKU tier:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name]
| summarize ns_count = count(), by: {skuName}
| sort ns_count desc
```
### Zone Redundancy
Find Service Bus namespaces that are not zone-redundant:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd zoneRedundant = azjson[configuration][properties][zoneRedundant],
skuName = azjson[configuration][sku][name]
| filter zoneRedundant == false
| fields name, skuName, azure.resource.group, azure.location
```
### TLS Configuration
Check minimum TLS version across Service Bus namespaces:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| summarize ns_count = count(), by: {minimumTlsVersion}
| sort minimumTlsVersion desc
```
Find namespaces with TLS version below 1.2:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| filter minimumTlsVersion != "1.2"
| fields name, minimumTlsVersion, azure.resource.group, azure.location
```
### Public Network Access
Find Service Bus namespaces with public network access enabled:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd publicNetworkAccess = azjson[configuration][properties][publicNetworkAccess]
| filter publicNetworkAccess == "Enabled"
| fields name, publicNetworkAccess, azure.resource.group, azure.location
```
### Topics
List all Service Bus topics:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
List topics with configuration details:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS"
| parse azure.object, "JSON:azjson"
| fieldsAdd maxSizeInMegabytes = azjson[configuration][properties][maxSizeInMegabytes],
status = azjson[configuration][properties][status],
enablePartitioning = azjson[configuration][properties][enablePartitioning],
requiresDuplicateDetection = azjson[configuration][properties][requiresDuplicateDetection]
| fields name, maxSizeInMegabytes, status, enablePartitioning, requiresDuplicateDetection,
azure.resource.group, azure.location
```
### Subscriptions
List all Service Bus subscriptions:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS_SUBSCRIPTIONS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
List subscriptions with configuration details:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS_SUBSCRIPTIONS"
| parse azure.object, "JSON:azjson"
| fieldsAdd maxDeliveryCount = azjson[configuration][properties][maxDeliveryCount],
lockDuration = azjson[configuration][properties][lockDuration],
deadLetteringOnMessageExpiration = azjson[configuration][properties][deadLetteringOnMessageExpiration],
status = azjson[configuration][properties][status]
| fields name, maxDeliveryCount, lockDuration, deadLetteringOnMessageExpiration, status,
azure.resource.group, azure.location
```
### Queue Configuration
List all Service Bus queues:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
List Service Bus queues with configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES"
| parse azure.object, "JSON:azjson"
| fieldsAdd maxSizeInMegabytes = azjson[configuration][properties][maxSizeInMegabytes],
status = azjson[configuration][properties][status],
enablePartitioning = azjson[configuration][properties][enablePartitioning],
requiresDuplicateDetection = azjson[configuration][properties][requiresDuplicateDetection],
deadLetteringOnMessageExpiration = azjson[configuration][properties][deadLetteringOnMessageExpiration],
maxDeliveryCount = azjson[configuration][properties][maxDeliveryCount],
lockDuration = azjson[configuration][properties][lockDuration]
| fields name, maxSizeInMegabytes, status, enablePartitioning, requiresDuplicateDetection,
deadLetteringOnMessageExpiration, maxDeliveryCount, lockDuration,
azure.resource.group, azure.location
```
Find queues by name pattern:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES"
| filter contains(name, "<PATTERN>")
| fields name, id, azure.resource.group, azure.location
```
### Namespace Traversals
Find queues belonging to a specific Service Bus namespace (Queue → Namespace backward traversal):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| filter name == "<NAMESPACE_NAME>"
| traverse "*", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES", direction:backward
| fields name, id, azure.resource.group
```
Find topics belonging to a specific Service Bus namespace (Topic → Namespace backward traversal):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| filter name == "<NAMESPACE_NAME>"
| traverse "*", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS", direction:backward
| fields name, id, azure.resource.group
```
Count queues per namespace:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES"
| traverse "*", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| fieldsAdd namespaceName = name
| summarize queue_count = count(), by: {namespaceName}
| sort queue_count desc
```
Count topics per namespace:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS"
| traverse "*", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| fieldsAdd namespaceName = name
| summarize topic_count = count(), by: {namespaceName}
| sort topic_count desc
```
Count subscriptions per topic:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS_SUBSCRIPTIONS"
| traverse "*", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS"
| fieldsAdd topicName = name
| summarize subscription_count = count(), by: {topicName}
| sort subscription_count desc
```
### Dead-Letter Queue Detection
Find queues with dead-lettering on message expiration enabled:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES"
| parse azure.object, "JSON:azjson"
| fieldsAdd deadLetteringOnMessageExpiration = azjson[configuration][properties][deadLetteringOnMessageExpiration]
| filter deadLetteringOnMessageExpiration == true
| fields name, azure.resource.group, azure.location
```
Find subscriptions with dead-lettering on message expiration enabled:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS_SUBSCRIPTIONS"
| parse azure.object, "JSON:azjson"
| fieldsAdd deadLetteringOnMessageExpiration = azjson[configuration][properties][deadLetteringOnMessageExpiration]
| filter deadLetteringOnMessageExpiration == true
| fields name, azure.resource.group, azure.location
```
Find queues with low max delivery count (messages reach dead-letter faster):
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES"
| parse azure.object, "JSON:azjson"
| fieldsAdd maxDeliveryCount = azjson[configuration][properties][maxDeliveryCount],
deadLetteringOnMessageExpiration = azjson[configuration][properties][deadLetteringOnMessageExpiration]
| filter maxDeliveryCount <= 3
| fields name, maxDeliveryCount, deadLetteringOnMessageExpiration, azure.resource.group, azure.location
```
## Cross-Service Analysis
Count all messaging resources by type:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES", "AZURE_MICROSOFT_EVENTHUB_NAMESPACES_EVENTHUBS",
"AZURE_MICROSOFT_SERVICEBUS_NAMESPACES", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES",
"AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS_SUBSCRIPTIONS"
| summarize total = count(), by: {type}
| sort total desc
```
Count messaging resources by region:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES", "AZURE_MICROSOFT_EVENTHUB_NAMESPACES_EVENTHUBS",
"AZURE_MICROSOFT_SERVICEBUS_NAMESPACES", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_QUEUES",
"AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES_TOPICS_SUBSCRIPTIONS"
| summarize total = count(), by: {type, azure.location}
| sort azure.location, total desc
```
Filter messaging resources to a specific subscription:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES", "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| filter azure.subscription == "<SUBSCRIPTION_ID>"
| fields type, name, azure.resource.group, azure.location
```
references/metrics-performance.md
# Azure Metrics & Performance
DQL timeseries patterns for Azure Monitor-sourced metrics. Use during investigation to determine whether a resource is saturated, erroring, or slow.
## Table of Contents
- [Query Template](#query-template)
- [VM Metrics](#vm-metrics)
- [Azure SQL Metrics](#azure-sql-metrics)
- [Storage Account Metrics](#storage-account-metrics)
- [Event Hub Metrics](#event-hub-metrics)
- [Service Bus Metrics](#service-bus-metrics)
- [Load Balancer Metrics](#load-balancer-metrics)
- [App Service / Functions Metrics](#app-service--functions-metrics)
- [AKS (Managed Cluster) Metrics](#aks-managed-cluster-metrics)
- [Cosmos DB Metrics](#cosmos-db-metrics)
- [Redis Cache Metrics](#redis-cache-metrics)
- [Application Gateway Metrics](#application-gateway-metrics)
- [Combining Entity Queries with Metrics](#combining-entity-queries-with-metrics)
- [Metric Availability Note](#metric-availability-note)
## Query Template
Azure metrics in Dynatrace follow the naming convention:
```
cloud.azure.<resource_provider_path>.<MetricName>
```
Where `<resource_provider_path>` is `<provider_namespace>.<resource_type>` (lowercase, dots separating hierarchy levels):
| Resource provider path | Service |
|---|---|
| `microsoft_compute.virtualmachines` | Virtual Machines |
| `microsoft_sql.servers.databases` | Azure SQL Databases |
| `microsoft_storage.storageaccounts` | Storage Accounts |
| `microsoft_network.loadbalancers` | Load Balancers |
| `microsoft_eventhub.namespaces` | Event Hub Namespaces |
| `microsoft_servicebus.namespaces` | Service Bus Namespaces |
| `microsoft_web.sites` | App Service / Functions |
| `microsoft_containerservice.managedclusters` | AKS Managed Clusters |
| `microsoft_documentdb.databaseaccounts` | Cosmos DB Accounts |
| `microsoft_cache.redis` | Redis Cache |
| `microsoft_cache.redisenterprise` | Redis Enterprise |
| `microsoft_network.applicationgateways` | Application Gateways |
The `dt.smartscape_source.id` dimension splits results by Dynatrace entity. Use it in the `by:` clause and `filter` to scope metrics to a specific resource.
```dql-template
timeseries cpu = avg(cloud.azure.microsoft_compute.virtualmachines.<METRIC_NAME>),
by: { dt.smartscape_source.id },
from: <PROBLEM_START - 30m>, to: <PROBLEM_END + 15m>
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")
```
Replace `<METRIC_NAME>` with the metric from the tables below, and `<ROOT_CAUSE_ENTITY_ID>` with the Dynatrace entity ID (e.g., `AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES-2B0D33F11CE649F4`). Omit the `| filter` clause to get all instances of that metric.
> **Time windows:** The template above uses `<PROBLEM_START>` and `<PROBLEM_END>` for scoping queries to a specific incident window. The per-service examples below use `from: now()-1h` for simplicity — substitute your incident timestamps when investigating a specific problem.
---
## VM Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_compute.virtualmachines.PercentageCPU` | CPU utilization | % | > 85% sustained |
| `cloud.azure.microsoft_compute.virtualmachines.AvailableMemoryBytes` | Available memory | Bytes | Trending toward 0 |
| `cloud.azure.microsoft_compute.virtualmachines.DiskReadBytes` | Disk read throughput | Bytes/sec | Spike vs baseline |
| `cloud.azure.microsoft_compute.virtualmachines.DiskWriteBytes` | Disk write throughput | Bytes/sec | Spike vs baseline |
| `cloud.azure.microsoft_compute.virtualmachines.NetworkInTotal` | Inbound network traffic | Bytes | Spike or drop vs baseline |
| `cloud.azure.microsoft_compute.virtualmachines.NetworkOutTotal` | Outbound network traffic | Bytes | Spike or drop vs baseline |
Check CPU utilization for a specific VM:
```dql-template
timeseries cpu = avg(cloud.azure.microsoft_compute.virtualmachines.PercentageCPU),
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")
```
Check available memory (low values indicate memory pressure):
```dql-template
timeseries mem = avg(cloud.azure.microsoft_compute.virtualmachines.AvailableMemoryBytes),
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")
```
---
## Azure SQL Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_sql.servers.databases.cpu_percent` | Database CPU utilization | % | > 85% sustained |
| `cloud.azure.microsoft_sql.servers.databases.dtu_consumption_percent` | DTU consumption percentage | % | > 90% sustained |
| `cloud.azure.microsoft_sql.servers.databases.storage_percent` | Storage space used | % | > 85% (plan expansion) |
| `cloud.azure.microsoft_sql.servers.databases.deadlock` | Deadlock count | Count | > 0 during incident |
| `cloud.azure.microsoft_sql.servers.databases.connection_failed` | Failed connections | Count | > 0 during incident |
Check CPU and DTU consumption for a specific SQL database:
```dql-template
timeseries { cpu = avg(cloud.azure.microsoft_sql.servers.databases.cpu_percent),
dtu = avg(cloud.azure.microsoft_sql.servers.databases.dtu_consumption_percent) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<SQL_DB_ENTITY_ID>")
```
Check storage percentage and deadlocks:
```dql-template
timeseries { storage = avg(cloud.azure.microsoft_sql.servers.databases.storage_percent),
deadlocks = sum(cloud.azure.microsoft_sql.servers.databases.deadlock) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<SQL_DB_ENTITY_ID>")
```
---
## Storage Account Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_storage.storageaccounts.UsedCapacity` | Total storage used | Bytes | Trending toward account limit |
| `cloud.azure.microsoft_storage.storageaccounts.Ingress` | Data ingress | Bytes | Spike vs baseline |
| `cloud.azure.microsoft_storage.storageaccounts.Egress` | Data egress | Bytes | Spike vs baseline (cost impact) |
| `cloud.azure.microsoft_storage.storageaccounts.Transactions` | Transaction count | Count | Spike vs baseline |
| `cloud.azure.microsoft_storage.storageaccounts.Availability` | Service availability | % | < 100% indicates issues |
Check ingress, egress, and transactions for a storage account:
```dql-template
timeseries { ingress = sum(cloud.azure.microsoft_storage.storageaccounts.Ingress),
egress = sum(cloud.azure.microsoft_storage.storageaccounts.Egress),
txn = sum(cloud.azure.microsoft_storage.storageaccounts.Transactions) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<STORAGE_ENTITY_ID>")
```
Check availability:
```dql-template
timeseries avail = avg(cloud.azure.microsoft_storage.storageaccounts.Availability),
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<STORAGE_ENTITY_ID>")
```
---
## Event Hub Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_eventhub.namespaces.IncomingMessages` | Messages received | Count | Drop vs baseline (upstream issue) |
| `cloud.azure.microsoft_eventhub.namespaces.OutgoingMessages` | Messages delivered | Count | Drop vs baseline (consumer issue) |
| `cloud.azure.microsoft_eventhub.namespaces.IncomingBytes` | Ingress bytes | Bytes | Near throughput unit limit |
| `cloud.azure.microsoft_eventhub.namespaces.ThrottledRequests` | Throttled requests | Count | > 0 (throughput limit hit) |
Check message flow and throttling:
```dql-template
timeseries { incoming = sum(cloud.azure.microsoft_eventhub.namespaces.IncomingMessages),
outgoing = sum(cloud.azure.microsoft_eventhub.namespaces.OutgoingMessages),
throttled = sum(cloud.azure.microsoft_eventhub.namespaces.ThrottledRequests) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<EVENTHUB_ENTITY_ID>")
```
---
## Service Bus Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_servicebus.namespaces.IncomingMessages` | Messages received | Count | Drop vs baseline (upstream issue) |
| `cloud.azure.microsoft_servicebus.namespaces.OutgoingMessages` | Messages delivered | Count | Drop vs baseline (consumer issue) |
| `cloud.azure.microsoft_servicebus.namespaces.IncomingRequests` | Total incoming requests | Count | Spike vs baseline |
| `cloud.azure.microsoft_servicebus.namespaces.SuccessfulRequests` | Successful requests | Count | Drop vs baseline |
| `cloud.azure.microsoft_servicebus.namespaces.ServerErrors` | Server errors (5xx) | Count | > 0 during incident |
| `cloud.azure.microsoft_servicebus.namespaces.UserErrors` | User errors (4xx) | Count | Spike vs baseline (bad messages) |
| `cloud.azure.microsoft_servicebus.namespaces.ThrottledRequests` | Throttled requests | Count | > 0 (throughput limit hit) |
| `cloud.azure.microsoft_servicebus.namespaces.ActiveMessages` | Active messages in queue/topic | Count | Growing backlog |
| `cloud.azure.microsoft_servicebus.namespaces.DeadletteredMessages` | Dead-lettered messages | Count | > 0 (poison messages) |
| `cloud.azure.microsoft_servicebus.namespaces.ScheduledMessages` | Scheduled messages | Count | Context-dependent |
| `cloud.azure.microsoft_servicebus.namespaces.Size` | Size of queue/topic in bytes | Bytes | Near max size |
Check message flow and throttling:
```dql-template
timeseries { incoming = sum(cloud.azure.microsoft_servicebus.namespaces.IncomingMessages),
outgoing = sum(cloud.azure.microsoft_servicebus.namespaces.OutgoingMessages),
throttled = sum(cloud.azure.microsoft_servicebus.namespaces.ThrottledRequests) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<SERVICEBUS_ENTITY_ID>")
```
Check dead-letter accumulation:
```dql-template
timeseries { deadLettered = sum(cloud.azure.microsoft_servicebus.namespaces.DeadletteredMessages),
active = sum(cloud.azure.microsoft_servicebus.namespaces.ActiveMessages) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<SERVICEBUS_ENTITY_ID>")
```
Check server and user errors:
```dql-template
timeseries { serverErrors = sum(cloud.azure.microsoft_servicebus.namespaces.ServerErrors),
userErrors = sum(cloud.azure.microsoft_servicebus.namespaces.UserErrors),
requests = sum(cloud.azure.microsoft_servicebus.namespaces.IncomingRequests) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<SERVICEBUS_ENTITY_ID>")
```
> **Important:** Non-zero `DeadletteredMessages` indicates poison messages that failed processing. Cross-reference with the dead-letter analysis queries in [messaging-integration.md](messaging-integration.md) to identify root causes.
---
## Load Balancer Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_network.loadbalancers.ByteCount` | Bytes processed | Bytes | Near throughput limit |
| `cloud.azure.microsoft_network.loadbalancers.PacketCount` | Packets processed | Count | Spike vs baseline |
| `cloud.azure.microsoft_network.loadbalancers.DipAvailability` | Backend pool health (data path) | % | < 100% (unhealthy backends) |
| `cloud.azure.microsoft_network.loadbalancers.VipAvailability` | Frontend data path availability | % | < 100% (frontend issues) |
Check backend health and traffic for a load balancer:
```dql-template
timeseries { dipHealth = avg(cloud.azure.microsoft_network.loadbalancers.DipAvailability),
bytes = sum(cloud.azure.microsoft_network.loadbalancers.ByteCount) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<LB_ENTITY_ID>")
```
> **Important:** A `DipAvailability` below 100% indicates one or more backend instances are failing health probes. Cross-reference with VM metrics to identify the unhealthy instance.
---
## App Service / Functions Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_web.sites.HttpResponseTime` | Average HTTP response time | Seconds | > p99 baseline |
| `cloud.azure.microsoft_web.sites.Requests` | Total HTTP requests | Count | Drop vs baseline |
| `cloud.azure.microsoft_web.sites.Http5xx` | 5xx server error responses | Count | > 0 during incident |
| `cloud.azure.microsoft_web.sites.FunctionExecutionCount` | Function execution count | Count | Drop vs baseline |
| `cloud.azure.microsoft_web.sites.FunctionExecutionUnits` | Function execution units | MB-ms | Spike vs baseline |
Check response time and errors for an App Service:
```dql-template
timeseries { responseTime = avg(cloud.azure.microsoft_web.sites.HttpResponseTime),
errors = sum(cloud.azure.microsoft_web.sites.Http5xx),
requests = sum(cloud.azure.microsoft_web.sites.Requests) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<APP_SERVICE_ENTITY_ID>")
```
Check Function execution metrics:
```dql-template
timeseries { executions = sum(cloud.azure.microsoft_web.sites.FunctionExecutionCount),
units = sum(cloud.azure.microsoft_web.sites.FunctionExecutionUnits) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<FUNCTION_ENTITY_ID>")
```
---
## AKS (Managed Cluster) Metrics
AKS infrastructure-layer metrics cover API server, etcd, and node-level resource usage. For workload-level observability (pods, deployments, services), defer to `dt-obs-kubernetes`.
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_containerservice.managedclusters.apiserver_cpu_usage_percentage` | API server CPU | % | > 80% sustained |
| `cloud.azure.microsoft_containerservice.managedclusters.apiserver_memory_usage_percentage` | API server memory | % | > 80% sustained |
| `cloud.azure.microsoft_containerservice.managedclusters.etcd_database_usage_percentage` | etcd storage usage | % | > 80% (risk of cluster instability) |
| `cloud.azure.microsoft_containerservice.managedclusters.node_cpu_usage_percentage` | Node CPU usage | % | > 85% sustained |
| `cloud.azure.microsoft_containerservice.managedclusters.node_memory_working_set_percentage` | Node memory working set | % | > 85% sustained |
| `cloud.azure.microsoft_containerservice.managedclusters.node_disk_usage_percentage` | Node disk usage | % | > 85% (eviction risk) |
| `cloud.azure.microsoft_containerservice.managedclusters.kube_node_status_condition` | Node readiness status | Status | Not-ready nodes |
| `cloud.azure.microsoft_containerservice.managedclusters.kube_pod_status_ready` | Pod readiness | Status | Drop vs baseline |
Check API server and etcd health for an AKS cluster:
```dql-template
timeseries { apiCpu = avg(cloud.azure.microsoft_containerservice.managedclusters.apiserver_cpu_usage_percentage),
etcd = avg(cloud.azure.microsoft_containerservice.managedclusters.etcd_database_usage_percentage) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<AKS_ENTITY_ID>")
```
Check node resource pressure:
```dql-template
timeseries { nodeCpu = avg(cloud.azure.microsoft_containerservice.managedclusters.node_cpu_usage_percentage),
nodeMem = avg(cloud.azure.microsoft_containerservice.managedclusters.node_memory_working_set_percentage),
nodeDisk = avg(cloud.azure.microsoft_containerservice.managedclusters.node_disk_usage_percentage) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<AKS_ENTITY_ID>")
```
---
## Cosmos DB Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_documentdb.databaseaccounts.TotalRequestUnits` | RU consumption | RU/s | Near provisioned limit |
| `cloud.azure.microsoft_documentdb.databaseaccounts.TotalRequests` | Total requests | Count | Drop vs baseline |
| `cloud.azure.microsoft_documentdb.databaseaccounts.ServerSideLatency` | Server-side latency | ms | > p99 baseline |
| `cloud.azure.microsoft_documentdb.databaseaccounts.ServiceAvailability` | Service availability | % | < 100% |
| `cloud.azure.microsoft_documentdb.databaseaccounts.DataUsage` | Data storage used | Bytes | Near partition limit |
| `cloud.azure.microsoft_documentdb.databaseaccounts.DocumentCount` | Document count | Count | Trending toward partition limit |
Check RU consumption and latency for a Cosmos DB account:
```dql-template
timeseries { ru = sum(cloud.azure.microsoft_documentdb.databaseaccounts.TotalRequestUnits),
latency = avg(cloud.azure.microsoft_documentdb.databaseaccounts.ServerSideLatency) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<COSMOSDB_ENTITY_ID>")
```
Check availability:
```dql-template
timeseries avail = avg(cloud.azure.microsoft_documentdb.databaseaccounts.ServiceAvailability),
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<COSMOSDB_ENTITY_ID>")
```
> **Important:** A `TotalRequestUnits` value near the provisioned RU limit means the account is at risk of 429 (throttled) responses. Cross-reference with `TotalRequests` to check if request volume is spiking.
---
## Redis Cache Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_cache.redis.serverLoad` | Server CPU load | % | > 80% sustained |
| `cloud.azure.microsoft_cache.redis.usedmemorypercentage` | Memory usage | % | > 85% (eviction risk) |
| `cloud.azure.microsoft_cache.redis.cachehits` | Cache hits | Count | Drop vs baseline |
| `cloud.azure.microsoft_cache.redis.cachemisses` | Cache misses | Count | Spike vs baseline |
| `cloud.azure.microsoft_cache.redis.cachemissrate` | Cache miss rate | % | Sustained increase |
| `cloud.azure.microsoft_cache.redis.connectedclients` | Connected clients | Count | Near maxclients limit |
| `cloud.azure.microsoft_cache.redis.evictedkeys` | Evicted keys | Count | > 0 (memory pressure) |
| `cloud.azure.microsoft_cache.redis.cacheLatency` | Operation latency | ms | > p99 baseline |
| `cloud.azure.microsoft_cache.redis.errors` | Error count | Count | > 0 during incident |
| `cloud.azure.microsoft_cache.redis.totalcommandsprocessed` | Commands processed | Count/s | Drop vs baseline |
Check server load and memory for a Redis instance:
```dql-template
timeseries { load = avg(cloud.azure.microsoft_cache.redis.serverLoad),
mem = avg(cloud.azure.microsoft_cache.redis.usedmemorypercentage) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<REDIS_ENTITY_ID>")
```
Check hit/miss ratio and evictions:
```dql-template
timeseries { hits = sum(cloud.azure.microsoft_cache.redis.cachehits),
misses = sum(cloud.azure.microsoft_cache.redis.cachemisses),
evictions = sum(cloud.azure.microsoft_cache.redis.evictedkeys) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<REDIS_ENTITY_ID>")
```
> **Note:** Redis Enterprise metrics use the `microsoft_cache.redisenterprise` path with the same metric names. Replace `redis` with `redisenterprise` in the metric key. The `dt.smartscape_source.id` dimension works for all entity types.
---
## Application Gateway Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
| `cloud.azure.microsoft_network.applicationgateways.TotalRequests` | Total requests | Count | Drop vs baseline |
| `cloud.azure.microsoft_network.applicationgateways.FailedRequests` | Failed requests | Count | > 0 during incident |
| `cloud.azure.microsoft_network.applicationgateways.Throughput` | Data throughput | Bytes/sec | Near SKU limit |
| `cloud.azure.microsoft_network.applicationgateways.CurrentConnections` | Active connections | Count | Near connection limit |
| `cloud.azure.microsoft_network.applicationgateways.HealthyHostCount` | Healthy backend hosts | Count | Decrease from baseline |
| `cloud.azure.microsoft_network.applicationgateways.UnhealthyHostCount` | Unhealthy backend hosts | Count | > 0 (backend failure) |
| `cloud.azure.microsoft_network.applicationgateways.WebApplicationFirewallBlockedRequests` | WAF blocked request count | Count | > 0 (active blocking) |
| `cloud.azure.microsoft_network.applicationgateways.WebApplicationFirewallMatchedRequests` | WAF matched (triggered) request count | Count | Spike vs baseline (possible false positives) |
| `cloud.azure.microsoft_network.applicationgateways.WebApplicationFirewallTotalRuleDistribution` | WAF rule hit distribution | Count | Identifies which rules trigger most |
| `cloud.azure.microsoft_network.applicationgateways.WebApplicationFirewallManagedRuleDistribution` | WAF managed rule hit distribution | Count | Identifies managed rules triggering |
Check request volume and errors for an Application Gateway:
```dql-template
timeseries { requests = sum(cloud.azure.microsoft_network.applicationgateways.TotalRequests),
failures = sum(cloud.azure.microsoft_network.applicationgateways.FailedRequests) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<APPGW_ENTITY_ID>")
```
Check backend pool health:
```dql-template
timeseries { healthy = avg(cloud.azure.microsoft_network.applicationgateways.HealthyHostCount),
unhealthy = avg(cloud.azure.microsoft_network.applicationgateways.UnhealthyHostCount) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<APPGW_ENTITY_ID>")
```
> **Important:** An `UnhealthyHostCount` > 0 means backend VMs or containers are failing health probes. Cross-reference with the backend resource metrics (VM, App Service) to identify the root cause.
Check WAF blocked and matched requests during an incident (spikes in matched requests with user-reported issues indicate false positives):
```dql-template
timeseries { blocked = sum(cloud.azure.microsoft_network.applicationgateways.WebApplicationFirewallBlockedRequests),
matched = sum(cloud.azure.microsoft_network.applicationgateways.WebApplicationFirewallMatchedRequests) },
by: { dt.smartscape_source.id },
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<APPGW_ENTITY_ID>")
```
> **Investigation tip:** A spike in `WebApplicationFirewallMatchedRequests` correlated with user-reported 403 errors strongly suggests a false positive. Cross-reference with the disabled rule groups and exclusions in [load-balancing-api.md](load-balancing-api.md) to identify which rules are triggering and whether they should be tuned.
---
## Combining Entity Queries with Metrics
Find a set of entities by filter, then query metrics for all of them. Example: are all VMs in a resource group experiencing high CPU, or just one?
**Step 1 — Find resource IDs for the group:**
```dql-template
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| filter azure.resource.group == "<RESOURCE_GROUP>"
| fields name, id
```
**Step 2 — Query metrics for all VMs (no filter = all series):**
```dql-template
timeseries cpu = avg(cloud.azure.microsoft_compute.virtualmachines.PercentageCPU),
by: { dt.smartscape_source.id },
from: now()-1h
```
Cross-reference the `dt.smartscape_source.id` dimension values against the entity IDs from Step 1 to identify which VMs in the resource group are affected.
---
## Metric Availability Note
Azure metrics are only available when Azure Monitor integration is enabled and configured for the relevant services in Dynatrace (Settings > Cloud and Virtualization > Azure). If a timeseries query returns no data:
1. Verify the entity exists: run the corresponding `smartscapeNodes` query
2. Confirm the Azure Monitor integration is configured and metric ingestion is enabled for this service
3. Check the metric name matches the naming convention: `cloud.azure.<resource_provider_path>.<MetricName>`
Do **not** interpret empty timeseries results as "no problem" — it may mean the metric is not configured for this resource type.
> **Note:** All metric keys documented here were verified against a live Dynatrace tenant with Azure Monitor integration enabled. If a metric key is not found in your environment, confirm Azure Monitor integration is configured for that service in Settings > Cloud and Virtualization > Azure.
references/README.md
# Azure Skill References
Detailed Azure-specific reference documentation organized by use case category.
## Reference Files
- **[vnet-networking-security.md](vnet-networking-security.md)** — VNet infrastructure, NSGs, subnets, public IPs, VPN gateways, and network connectivity
- **[database-monitoring.md](database-monitoring.md)** — Azure SQL, Cosmos DB, Redis Cache monitoring and configuration analysis
- **[serverless-containers.md](serverless-containers.md)** — Functions, App Service, AKS infrastructure layer, Container Apps
- **[load-balancing-api.md](load-balancing-api.md)** — Azure Load Balancers, Application Gateways, API Management
- **[messaging-integration.md](messaging-integration.md)** — Event Hubs, Service Bus, Event Grid
- **[storage-monitoring.md](storage-monitoring.md)** — Storage Accounts (blob, file, queue, table) and managed disks
- **[resource-management.md](resource-management.md)** — Resource inventory, tag compliance, unattached resources, and lifecycle management
- **[cost-optimization.md](cost-optimization.md)** — Cost savings, SKU analysis, unused resources
- **[capacity-planning.md](capacity-planning.md)** — VMSS scaling, subnet utilization, and capacity analysis
- **[security-compliance.md](security-compliance.md)** — NSG rule analysis, Key Vault, encryption status, and public resource detection
- **[resource-ownership.md](resource-ownership.md)** — Cost allocation, chargeback, team-based grouping, and ownership tracking
- **[workload-detection.md](workload-detection.md)** — Identify how a VM is orchestrated (AKS node, VMSS member, standalone)
- **[metrics-performance.md](metrics-performance.md)** — DQL timeseries patterns for Azure VM, SQL, Storage, Event Hub metrics
## Usage
These reference files provide detailed DQL query patterns and examples for specific Azure use cases. Load them as needed based on your monitoring requirements.
references/resource-management.md
# Azure Resource Management & Optimization
Analyze Azure resource usage, identify optimization opportunities, and manage resource tagging across subscriptions and resource groups.
## Table of Contents
- [Resource Inventory](#resource-inventory)
- [Tag Compliance](#tag-compliance)
- [Resource Lifecycle](#resource-lifecycle)
- [Regional & Resource Group Distribution](#regional--resource-group-distribution)
- [Storage & Security Resources](#storage--security-resources)
## Resource Inventory
Count all Azure resources by type:
```dql
smartscapeNodes "AZURE_*"
| summarize resource_count = count(), by: {type}
| sort resource_count desc
```
View resource distribution across subscriptions:
```dql
smartscapeNodes "AZURE_*"
| summarize resource_count = count(), by: {azure.subscription, azure.location}
| sort resource_count desc
```
Find resource types spanning multiple regions:
```dql
smartscapeNodes "AZURE_*"
| summarize
region_count = countDistinct(azure.location),
total_resources = count(),
by: {type}
| filter region_count > 1
| sort region_count desc
```
## Tag Compliance
Find completely untagged resources:
```dql
smartscapeNodes "AZURE_*"
| filter isNull(tags)
| fields type, name, id, azure.subscription, azure.resource.group, azure.location
```
Find resources missing a specific required tag:
```dql-template
smartscapeNodes "AZURE_*"
| filter isNull(tags[`<TAG_NAME>`]) or tags[`<TAG_NAME>`] == ""
| summarize count = count(), by: {type, azure.subscription}
```
Calculate tag coverage percentages across resource types:
```dql
smartscapeNodes "AZURE_*"
| fieldsAdd has_owner_tag = if(isNotNull(tags[`dt_owner_email`]), 1)
| fieldsAdd has_env_tag = if(isNotNull(tags[`Environment`]), 1)
| summarize
total = count(),
with_env = sum(has_env_tag),
with_owner = sum(has_owner_tag),
by: { type }
| fieldsAdd
env_coverage_pct = (with_env * 100.0) / total,
owner_coverage_pct = (with_owner * 100.0) / total
| sort env_coverage_pct asc
```
Find resources by tag value:
```dql-template
smartscapeNodes "AZURE_*"
| filter tags[`<TAG_NAME>`] == "<TAG_VALUE>"
| summarize count = count(), by: {type, azure.location}
```
Find resources by naming convention:
```dql-template
smartscapeNodes "AZURE_*"
| filter matchesPhrase(name, "<SEARCH_TERM>")
| fields type, name, id, azure.location, azure.resource.group, tags[`Environment`]
```
## Resource Lifecycle
Detect deleted resources:
```dql
smartscapeNodes "AZURE_*"
| filter cloud.acquisitionStatus == "DELETED"
| fields type, name, id, azure.subscription, azure.resource.group, azure.location
```
Find resources with acquisition issues:
```dql
smartscapeNodes "AZURE_*"
| filter cloud.acquisitionStatus != "OK"
| fields type, name, id, cloud.acquisitionStatus, azure.subscription
```
Find unattached managed disks:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd diskState = azjson[configuration][properties][diskState]
| filter diskState == "Unattached"
| fields name, id, azure.resource.group, azure.location, azure.subscription
```
Find unassociated public IPs:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_PUBLICIPADDRESSES"
| parse azure.object, "JSON:azjson"
| fieldsAdd ipConfig = azjson[configuration][properties][ipConfiguration]
| filter isNull(ipConfig)
| fields name, id, azure.resource.group, azure.location, azure.subscription
```
## Regional & Resource Group Distribution
View resources by region:
```dql
smartscapeNodes "AZURE_*"
| summarize resource_count = count(), by: {azure.location}
| sort resource_count desc
```
Count resources per resource group:
```dql
smartscapeNodes "AZURE_*"
| summarize resource_count = count(), by: {azure.resource.group, type}
| sort resource_count desc
```
## Storage & Security Resources
Count storage services:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS",
"AZURE_MICROSOFT_COMPUTE_DISKS",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_BLOBSERVICES_CONTAINERS"
| summarize count = count(), by: {type, azure.location}
| sort count desc
```
Count security resources:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS",
"AZURE_MICROSOFT_KEYVAULT_VAULTS",
"AZURE_MICROSOFT_MANAGEDIDENTITY_USERASSIGNEDIDENTITIES"
| summarize count = count(), by: {type}
| sort count desc
```
List storage accounts:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| fields name, azure.subscription, azure.resource.group, azure.location, id
```
List managed disks:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| fields name, id, azure.resource.group, azure.location, azure.subscription
```
references/resource-ownership.md
# Azure Resource Ownership & Chargeback
Track resource ownership and enable cost allocation across teams using Azure resource tags, subscriptions, and resource groups.
## Table of Contents
- [Tag-Based Ownership Pattern](#tag-based-ownership-pattern)
- [Common Ownership Tags](#common-ownership-tags)
- [Service-Specific Ownership](#service-specific-ownership)
- [Multi-Subscription Resource Summary](#multi-subscription-resource-summary)
## Tag-Based Ownership Pattern
All ownership queries follow the same pattern — filter by a tag, then summarize by that tag and a grouping dimension:
```dql-template
smartscapeNodes "AZURE_*"
| filter isNotNull(tags[`<TAG_NAME>`])
| summarize resource_count = count(), by: {tags[`<TAG_NAME>`], type}
| sort resource_count desc
```
Replace `<TAG_NAME>` with any tag from the table below. Replace `type` with `azure.location`, `azure.subscription`, or `azure.resource.group` for alternative groupings. Replace `"AZURE_*"` with a specific entity type to scope to one service.
## Common Ownership Tags
| Tag | Use case | Typical values |
|---|---|---|
| `dt_owner_email` | Individual accountability | Email address |
| `dt_owner_team` | Team-level allocation | Team names |
| `ACE:CREATED-BY` | Resource creator tracking | Email address |
| `project` | Project-based grouping | Project identifiers (e.g., `azure-demo`) |
| `managed-by` | Management tool tracking | Tool names (e.g., `dynatrace`) |
| `CostCenter` | Financial chargeback | Cost center codes |
| `Environment` | Environment segmentation | `production`, `staging`, `dev` |
## Service-Specific Ownership
To scope ownership queries to a specific Azure service, replace `"AZURE_*"` with the entity type:
| Entity type | Example use case |
|---|---|
| `AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES` | VM costs by department/team |
| `AZURE_MICROSOFT_WEB_SITES` | App Service / Functions costs by application |
| `AZURE_MICROSOFT_SQL_SERVERS_DATABASES` | Database ownership tracking |
| `AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS` | AKS cluster ownership by business unit |
| `AZURE_MICROSOFT_COMPUTE_DISKS` | Disk costs by project |
| `AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS` | Storage account ownership by team |
| `AZURE_MICROSOFT_APP_CONTAINERAPPS` | Container App ownership by team |
For service-specific queries, you can also select detail fields instead of summarizing:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS_DATABASES"
| filter isNotNull(tags[`dt_owner_email`])
| fields name, id, tags[`dt_owner_email`], azure.resource.group, azure.location
```
Ownership by resource group (useful when tags are not consistently applied):
```dql
smartscapeNodes "AZURE_*"
| summarize resource_count = count(), by: {azure.resource.group, type}
| sort resource_count desc
| limit 50
```
## Multi-Subscription Resource Summary
Summarize resources across subscriptions (independent of tags):
```dql
smartscapeNodes "AZURE_*"
| summarize resource_count = count(), by: {azure.subscription, type}
| sort resource_count desc
| limit 50
```
Summarize resources by subscription and resource group:
```dql
smartscapeNodes "AZURE_*"
| summarize resource_count = count(), by: {azure.subscription, azure.resource.group}
| sort resource_count desc
| limit 50
```
references/security-compliance.md
# Azure Security & Compliance
Monitor security configurations and compliance across Azure resources including network security groups, storage accounts, encryption, key management, and public access detection.
## Table of Contents
- [NSG Rule Analysis](#nsg-rule-analysis)
- [NSG Blast Radius](#nsg-blast-radius)
- [Storage Account Security](#storage-account-security)
- [Disk Encryption](#disk-encryption)
- [Key Vault & Managed Identity](#key-vault--managed-identity)
- [Public Access Detection](#public-access-detection)
- [Service Bus Security](#service-bus-security)
- [WAF Security Posture](#waf-security-posture)
- [Network Security](#network-security)
## NSG Rule Analysis
NSG security rules are stored in `azure.object` as `properties.securityRules[]` arrays. Since these are arrays within the JSON, convert to strings with `toString()` and use `contains()` to search for risky patterns.
Find NSGs with **any inbound rule open to the internet** (source `0.0.0.0/0` or `*`):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = toString(azjson[configuration][properties][securityRules])
| filter contains(rules, "0.0.0.0/0") or contains(rules, "\"sourceAddressPrefix\":\"*\"")
| fields name, id, azure.resource.group, azure.location, rules
```
Count internet-open NSGs per resource group:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = toString(azjson[configuration][properties][securityRules])
| filter contains(rules, "0.0.0.0/0") or contains(rules, "\"sourceAddressPrefix\":\"*\"")
| summarize open_nsg_count = count(), by: {azure.resource.group}
```
Find NSGs with rules allowing **SSH (port 22) from the internet** — a common audit finding:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = toString(azjson[configuration][properties][securityRules])
| filter contains(rules, "0.0.0.0/0") or contains(rules, "\"sourceAddressPrefix\":\"*\"")
| filter contains(rules, "\"destinationPortRange\":\"22\"")
| fields name, id, azure.resource.group, azure.location, rules
```
Find NSGs with rules allowing **RDP (port 3389) from the internet**:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = toString(azjson[configuration][properties][securityRules])
| filter contains(rules, "0.0.0.0/0") or contains(rules, "\"sourceAddressPrefix\":\"*\"")
| filter contains(rules, "\"destinationPortRange\":\"3389\"")
| fields name, id, azure.resource.group, azure.location, rules
```
Find NSGs with rules allowing **SQL (port 1433) from the internet**:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = toString(azjson[configuration][properties][securityRules])
| filter contains(rules, "0.0.0.0/0") or contains(rules, "\"sourceAddressPrefix\":\"*\"")
| filter contains(rules, "\"destinationPortRange\":\"1433\"")
| fields name, id, azure.resource.group, azure.location, rules
```
Find NSGs with rules allowing **PostgreSQL (port 5432) from the internet**:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = toString(azjson[configuration][properties][securityRules])
| filter contains(rules, "0.0.0.0/0") or contains(rules, "\"sourceAddressPrefix\":\"*\"")
| filter contains(rules, "\"destinationPortRange\":\"5432\"")
| fields name, id, azure.resource.group, azure.location, rules
```
Find NSGs that are **wide open** — all traffic allowed from the internet (protocol `*` combined with source `*` or `0.0.0.0/0`):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = toString(azjson[configuration][properties][securityRules])
| filter contains(rules, "\"sourceAddressPrefix\":\"*\"") or contains(rules, "0.0.0.0/0")
| filter contains(rules, "\"protocol\":\"*\"")
| filter contains(rules, "\"access\":\"Allow\"")
| fields name, id, azure.resource.group, azure.location, rules
```
Audit **egress rules** — count NSGs with unrestricted outbound traffic:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = toString(azjson[configuration][properties][securityRules])
| filter contains(rules, "\"direction\":\"Outbound\"")
| filter contains(rules, "\"destinationAddressPrefix\":\"*\"") or contains(rules, "0.0.0.0/0")
| filter contains(rules, "\"access\":\"Allow\"")
| summarize egress_open_count = count(), by: {azure.resource.group}
```
> **Tip:** The `securityRules` field is a JSON array. To detect specific open ports (e.g., Redis 6379, MongoDB 27017), combine the internet source filter with a port-specific string match: `contains(rules, "\"destinationPortRange\":\"6379\"")`.
## NSG Blast Radius
Find NSGs associated with the most resources. NSGs can be attached to NICs and subnets. Use backward traversals to find which compute resources are affected.
NSGs with the most NIC associations:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| summarize nic_count = count(), by: {name, id, azure.resource.group}
| sort nic_count desc
| limit 20
```
NSGs associated via subnets:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| summarize subnet_count = count(), by: {name, id, azure.resource.group}
| sort subnet_count desc
```
NSGs associated with VMSS (often AKS-managed):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| summarize vmss_count = count(), by: {name, id, azure.resource.group}
| sort vmss_count desc
```
List all NSGs (for finding unused ones, cross-reference with association queries above):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| fields name, id, azure.resource.group, azure.location
```
## Storage Account Security
Audit **HTTPS enforcement** across all storage accounts:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd httpsOnly = azjson[configuration][properties][supportsHttpsTrafficOnly]
| summarize account_count = count(), by: {httpsOnly}
```
Find storage accounts with **public blob access enabled**:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd allowBlobPublicAccess = azjson[configuration][properties][allowBlobPublicAccess]
| filter allowBlobPublicAccess == true
| fields name, id, azure.resource.group, azure.location
```
Audit **encryption** settings across storage accounts:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd keySource = azjson[configuration][properties][encryption][keySource],
blobEncryption = azjson[configuration][properties][encryption][services][blob][enabled]
| summarize account_count = count(), by: {keySource, blobEncryption}
```
Audit **TLS version** across storage accounts:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| summarize account_count = count(), by: {minTlsVersion}
```
Find storage accounts with **no TLS 1.2 minimum** (using legacy TLS):
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| filter minTlsVersion != "TLS1_2"
| fields name, id, minTlsVersion, azure.resource.group, azure.location
```
Summarize storage account **security posture** — HTTPS, public access, encryption, TLS:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd
httpsOnly = azjson[configuration][properties][supportsHttpsTrafficOnly],
allowBlobPublicAccess = azjson[configuration][properties][allowBlobPublicAccess],
keySource = azjson[configuration][properties][encryption][keySource],
minTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| fields name, httpsOnly, allowBlobPublicAccess, keySource, minTlsVersion, azure.resource.group
```
Audit storage account **network access rules**:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd defaultAction = azjson[configuration][properties][networkAcls][defaultAction]
| summarize account_count = count(), by: {defaultAction}
```
## Disk Encryption
Summarize managed disk encryption status by SKU:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
diskState = azjson[configuration][properties][diskState],
encryptionType = azjson[configuration][properties][encryption][type]
| summarize disk_count = count(), by: {encryptionType, skuName}
| sort disk_count desc
```
List managed disks with their encryption details:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
diskSizeGB = azjson[configuration][properties][diskSizeGB],
encryptionType = azjson[configuration][properties][encryption][type]
| fields name, skuName, diskSizeGB, encryptionType, azure.resource.group, azure.location
```
## Key Vault & Managed Identity
Audit Key Vault security configuration — RBAC authorization, soft delete, purge protection, and public access:
```dql
smartscapeNodes "AZURE_MICROSOFT_KEYVAULT_VAULTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd
rbacAuth = azjson[configuration][properties][enableRbacAuthorization],
softDelete = azjson[configuration][properties][enableSoftDelete],
softDeleteDays = azjson[configuration][properties][softDeleteRetentionInDays],
purgeProtection = azjson[configuration][properties][enablePurgeProtection],
publicAccess = azjson[configuration][properties][publicNetworkAccess]
| fields name, rbacAuth, softDelete, softDeleteDays, purgeProtection, publicAccess, azure.resource.group
```
Find Key Vaults **without RBAC authorization** (using legacy access policies):
```dql
smartscapeNodes "AZURE_MICROSOFT_KEYVAULT_VAULTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rbacAuth = azjson[configuration][properties][enableRbacAuthorization]
| filter rbacAuth != true
| fields name, id, azure.resource.group, azure.location
```
Find Key Vaults with **public access enabled**:
```dql
smartscapeNodes "AZURE_MICROSOFT_KEYVAULT_VAULTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd publicAccess = azjson[configuration][properties][publicNetworkAccess]
| filter publicAccess == "Enabled"
| fields name, id, azure.resource.group, azure.location
```
List user-assigned managed identities:
```dql
smartscapeNodes "AZURE_MICROSOFT_MANAGEDIDENTITY_USERASSIGNEDIDENTITIES"
| fields name, id, azure.resource.group, azure.location, azure.subscription
```
## Public Access Detection
Find VMs with public IP addresses (via NIC → IP Config, parsing `azure.object` for `publicIPAddress`):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES_IPCONFIGURATIONS"
| parse azure.object, "JSON:azjson"
| fieldsAdd publicIpId = azjson[configuration][properties][publicIPAddress][id]
| filter isNotNull(publicIpId)
| fields name, id, publicIpId, azure.resource.group
```
Find SQL servers with **public network access enabled**:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd publicAccess = azjson[configuration][properties][publicNetworkAccess]
| filter publicAccess == "Enabled"
| fields name, id, azure.resource.group, azure.location
```
Identify internet-facing load balancers (via public frontend IP configurations):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_FRONTENDIPCONFIGURATIONS"
| parse azure.object, "JSON:azjson"
| fieldsAdd publicIpId = azjson[configuration][properties][publicIPAddress][id]
| filter isNotNull(publicIpId)
| fields name, id, publicIpId, azure.resource.group
```
Find App Service / Function apps with **public network access**:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd publicAccess = azjson[configuration][properties][publicNetworkAccess],
httpsOnly = azjson[configuration][properties][httpsOnly]
| filter publicAccess == "Enabled"
| fields name, id, publicAccess, httpsOnly, azure.resource.group, azure.location
```
Find Container Apps with **external ingress**:
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_CONTAINERAPPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd externalIngress = azjson[configuration][properties][configuration][ingress][external],
fqdn = azjson[configuration][properties][configuration][ingress][fqdn]
| filter externalIngress == true
| fields name, fqdn, azure.resource.group, azure.location
```
Find Cosmos DB accounts with **VNet filtering disabled**:
```dql
smartscapeNodes "AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd vnetFilter = azjson[configuration][properties][isVirtualNetworkFilterEnabled]
| filter vnetFilter != true
| fields name, id, azure.resource.group, azure.location
```
## Service Bus Security
Check Service Bus namespaces for security configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_SERVICEBUS_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion],
publicNetworkAccess = azjson[configuration][properties][publicNetworkAccess],
disableLocalAuth = azjson[configuration][properties][disableLocalAuth]
| filter minimumTlsVersion != "1.2"
or publicNetworkAccess == "Enabled"
or disableLocalAuth == false
| fields name, minimumTlsVersion, publicNetworkAccess, disableLocalAuth,
azure.resource.group, azure.location
```
This query finds Service Bus namespaces with any security concern: old TLS, public access enabled, or local auth not disabled.
## WAF Security Posture
Find Application Gateways with WAF-capable SKU that have WAF disabled or in Detection mode:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_APPLICATIONGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuTier = azjson[configuration][properties][sku][tier],
wafEnabled = azjson[configuration][properties][webApplicationFirewallConfiguration][enabled],
wafMode = azjson[configuration][properties][webApplicationFirewallConfiguration][firewallMode]
| filter contains(toString(skuTier), "WAF")
and (wafEnabled == false or isNull(wafEnabled) or wafMode == "Detection")
| fields name, skuTier, wafEnabled, wafMode, azure.resource.group, azure.location
```
This query identifies gateways that are paying for WAF capability but not fully utilizing it — either WAF is disabled entirely or running in Detection mode (logging only, not blocking).
## Network Security
Audit **TLS version** across all services that expose it:
SQL Servers:
```dql
smartscapeNodes "AZURE_MICROSOFT_SQL_SERVERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTls = azjson[configuration][properties][minimalTlsVersion]
| summarize server_count = count(), by: {minTls}
```
Redis Cache:
```dql
smartscapeNodes "AZURE_MICROSOFT_CACHE_REDIS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTls = azjson[configuration][properties][minimumTlsVersion],
nonSslPort = azjson[configuration][properties][enableNonSslPort]
| fields name, minTls, nonSslPort, azure.resource.group, azure.location
```
Event Hub namespaces:
```dql
smartscapeNodes "AZURE_MICROSOFT_EVENTHUB_NAMESPACES"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTls = azjson[configuration][properties][minimumTlsVersion]
| summarize namespace_count = count(), by: {minTls}
```
Cosmos DB:
```dql
smartscapeNodes "AZURE_MICROSOFT_DOCUMENTDB_DATABASEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minTls = azjson[configuration][properties][minimalTlsVersion]
| fields name, minTls, azure.resource.group, azure.location
```
App Service / Functions HTTPS-only enforcement:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd httpsOnly = azjson[configuration][properties][httpsOnly]
| summarize site_count = count(), by: {httpsOnly}
```
Find App Services **not enforcing HTTPS**:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd httpsOnly = azjson[configuration][properties][httpsOnly]
| filter httpsOnly != true
| fields name, id, azure.resource.group, azure.location
```
API Management VNet integration audit:
```dql
smartscapeNodes "AZURE_MICROSOFT_APIMANAGEMENT_SERVICE"
| parse azure.object, "JSON:azjson"
| fieldsAdd vnetType = azjson[configuration][properties][virtualNetworkType]
| fields name, vnetType, azure.resource.group, azure.location
```
references/serverless-containers.md
# Azure Serverless & Container Workloads
Monitor Azure Functions, App Service, AKS infrastructure layer, and Container Apps.
## Table of Contents
- [Serverless & Container Entity Types](#serverless--container-entity-types)
- [Azure Functions Monitoring](#azure-functions-monitoring)
- [App Service Monitoring](#app-service-monitoring)
- [AKS Infrastructure Monitoring](#aks-infrastructure-monitoring)
- [Container Apps](#container-apps)
- [Cross-Service Analysis](#cross-service-analysis)
## Serverless & Container Entity Types
All these types support the standard discovery pattern: `smartscapeNodes "<TYPE>" | fields name, id, azure.subscription, azure.resource.group, azure.location, ...`
| Entity Type | Description |
|---|---|
| `AZURE_MICROSOFT_WEB_SITES` | App Service and Function Apps (differentiate via `kind`) |
| `AZURE_MICROSOFT_WEB_SERVERFARMS` | App Service Plans |
| `AZURE_MICROSOFT_WEB_SITES_FUNCTIONS` | Individual functions within a Function App |
| `AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS` | AKS clusters |
| `AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS_AGENTPOOLS` | AKS agent pools |
| `AZURE_MICROSOFT_CONTAINERREGISTRY_REGISTRIES` | Azure Container Registry |
| `AZURE_MICROSOFT_APP_CONTAINERAPPS` | Azure Container Apps |
| `AZURE_MICROSOFT_APP_MANAGEDENVIRONMENTS` | Container Apps managed environments |
| `AZURE_MICROSOFT_APP_JOBS` | Container Apps jobs |
## Azure Functions Monitoring
Function Apps are `AZURE_MICROSOFT_WEB_SITES` entities where the `kind` field contains `functionapp`. Filter using `azure.object` to separate them from App Service.
List all Function Apps:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd kind = azjson[configuration][kind]
| filter contains(kind, "functionapp")
| fieldsAdd state = azjson[configuration][properties][state],
defaultHostName = azjson[configuration][properties][defaultHostName],
runtime = azjson[configuration][properties][siteConfig][linuxFxVersion],
httpsOnly = azjson[configuration][properties][httpsOnly]
| fields name, kind, state, defaultHostName, runtime, httpsOnly,
azure.resource.group, azure.location
```
Find Function Apps on consumption (Dynamic) plans vs. dedicated plans:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd kind = azjson[configuration][kind],
sku = azjson[configuration][properties][sku]
| filter contains(kind, "functionapp")
| summarize func_count = count(), by: {sku}
| sort func_count desc
```
Find Function Apps and their App Service Plans (Web Site → Server Farm traversal):
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd kind = azjson[configuration][kind]
| filter contains(kind, "functionapp")
| traverse "*", "AZURE_MICROSOFT_WEB_SERVERFARMS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| fieldsAdd planName = name, planId = id
| lookup [smartscapeNodes "AZURE_MICROSOFT_WEB_SITES" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "app."
| fields app.name, planName, planId
```
List individual functions within Function Apps (Function → Web Site backward traversal):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| filter name == "<FUNCTION_APP_NAME>"
| traverse "*", "AZURE_MICROSOFT_WEB_SITES_FUNCTIONS", direction:backward
| fields name, id, azure.resource.group
```
Find Function Apps with VNet integration (check for virtual network subnet ID):
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd kind = azjson[configuration][kind],
vnetSubnetId = azjson[configuration][properties][virtualNetworkSubnetId]
| filter contains(kind, "functionapp")
| filter isNotNull(vnetSubnetId)
| fields name, vnetSubnetId, azure.resource.group, azure.location
```
## App Service Monitoring
List all App Service web apps (exclude Function Apps):
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd kind = azjson[configuration][kind]
| filter not(contains(kind, "functionapp"))
| fieldsAdd state = azjson[configuration][properties][state],
defaultHostName = azjson[configuration][properties][defaultHostName],
runtime = azjson[configuration][properties][siteConfig][linuxFxVersion],
httpsOnly = azjson[configuration][properties][httpsOnly]
| fields name, kind, state, defaultHostName, runtime, httpsOnly,
azure.resource.group, azure.location
```
List all App Service Plans with SKU details:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SERVERFARMS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
skuCapacity = azjson[configuration][sku][capacity]
| fields name, skuName, skuTier, skuCapacity, azure.resource.group, azure.location
```
Find stopped App Service apps:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd state = azjson[configuration][properties][state]
| filter state == "Stopped"
| fields name, state, azure.resource.group, azure.location
```
Find apps with public network access enabled:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES"
| parse azure.object, "JSON:azjson"
| fieldsAdd publicAccess = azjson[configuration][properties][publicNetworkAccess],
httpsOnly = azjson[configuration][properties][httpsOnly]
| fields name, publicAccess, httpsOnly, azure.resource.group, azure.location
```
## AKS Infrastructure Monitoring
> **Note:** This section covers the Azure infrastructure layer of AKS (clusters, agent pools, VMSS backing). For Kubernetes workload-level monitoring (pods, deployments, services), use the **dt-obs-kubernetes** skill.
List all AKS clusters with configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd k8sVersion = azjson[configuration][properties][kubernetesVersion],
currentVersion = azjson[configuration][properties][currentKubernetesVersion],
powerState = azjson[configuration][properties][powerState][code],
networkPlugin = azjson[configuration][properties][networkProfile][networkPlugin],
rbac = azjson[configuration][properties][enableRBAC],
fqdn = azjson[configuration][properties][fqdn],
skuTier = azjson[configuration][sku][tier]
| fields name, k8sVersion, currentVersion, powerState, networkPlugin, rbac, fqdn, skuTier,
azure.resource.group, azure.location
```
Find AKS cluster networking configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd networkPlugin = azjson[configuration][properties][networkProfile][networkPlugin],
loadBalancerSku = azjson[configuration][properties][networkProfile][loadBalancerSku],
podCidr = azjson[configuration][properties][networkProfile][podCidr],
serviceCidr = azjson[configuration][properties][networkProfile][serviceCidr],
nodeResourceGroup = azjson[configuration][properties][nodeResourceGroup]
| fields name, networkPlugin, loadBalancerSku, podCidr, serviceCidr, nodeResourceGroup
```
Find agent pools for an AKS cluster (Agent Pool → AKS backward traversal):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| filter name == "<AKS_CLUSTER_NAME>"
| traverse "*", "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS_AGENTPOOLS", direction:backward
| fields name, id, azure.resource.group
```
Find VMSS backing an AKS cluster (VMSS → AKS backward traversal):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| filter name == "<AKS_CLUSTER_NAME>"
| traverse "*", "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS", direction:backward
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][sku][name],
capacity = azjson[configuration][sku][capacity],
poolName = tags[`aks-managed-poolName`]
| fields name, vmSize, capacity, poolName, azure.resource.group
```
Find AKS clusters with deallocated power state:
```dql
smartscapeNodes "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd powerState = azjson[configuration][properties][powerState][code]
| filter powerState == "Deallocated"
| fields name, powerState, azure.resource.group, azure.location
```
List Container Registries:
```dql
smartscapeNodes "AZURE_MICROSOFT_CONTAINERREGISTRY_REGISTRIES"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
## Container Apps
List all Container Apps with running status:
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_CONTAINERAPPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd runningStatus = azjson[configuration][properties][runningStatus],
provisioningState = azjson[configuration][properties][provisioningState],
latestRevision = azjson[configuration][properties][latestReadyRevisionName]
| fields name, runningStatus, provisioningState, latestRevision,
azure.resource.group, azure.location
```
Find Container App scaling configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_CONTAINERAPPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minReplicas = azjson[configuration][properties][template][scale][minReplicas],
maxReplicas = azjson[configuration][properties][template][scale][maxReplicas]
| fields name, minReplicas, maxReplicas, azure.resource.group, azure.location
```
Find Container App container images:
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_CONTAINERAPPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd containers = azjson[configuration][properties][template][containers]
| expand containers
| fieldsAdd image = containers[image],
cpu = containers[resources][cpu],
memory = containers[resources][memory]
| fields name, image, cpu, memory, azure.resource.group
```
Find Container App ingress configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_CONTAINERAPPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd ingressFqdn = azjson[configuration][properties][configuration][ingress][fqdn],
external = azjson[configuration][properties][configuration][ingress][external],
targetPort = azjson[configuration][properties][configuration][ingress][targetPort]
| fields name, ingressFqdn, external, targetPort, azure.resource.group
```
Find Container Apps and their managed environments (Container App → Managed Environment traversal):
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_CONTAINERAPPS"
| traverse "*", "AZURE_MICROSOFT_APP_MANAGEDENVIRONMENTS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| fieldsAdd envName = name, envId = id
| lookup [smartscapeNodes "AZURE_MICROSOFT_APP_CONTAINERAPPS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "app."
| fields app.name, envName, envId
```
List Container App managed environments:
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_MANAGEDENVIRONMENTS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
List Container App jobs:
```dql
smartscapeNodes "AZURE_MICROSOFT_APP_JOBS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
## Cross-Service Analysis
Count all serverless and container resources by type:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES", "AZURE_MICROSOFT_WEB_SERVERFARMS",
"AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS",
"AZURE_MICROSOFT_APP_CONTAINERAPPS", "AZURE_MICROSOFT_CONTAINERREGISTRY_REGISTRIES"
| summarize count = count(), by: {type}
| sort count desc
```
Count all serverless and container resources by region:
```dql
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES", "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS",
"AZURE_MICROSOFT_APP_CONTAINERAPPS"
| summarize count = count(), by: {type, azure.location}
| sort azure.location, count desc
```
Find all resources in a specific resource group:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_WEB_SITES", "AZURE_MICROSOFT_WEB_SERVERFARMS",
"AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS",
"AZURE_MICROSOFT_APP_CONTAINERAPPS", "AZURE_MICROSOFT_APP_MANAGEDENVIRONMENTS"
| filter azure.resource.group == "<RESOURCE_GROUP>"
| fields type, name, azure.location, azure.provisioning_state
```
references/storage-monitoring.md
# Azure Storage Monitoring
Monitor Azure Storage Accounts, blob containers, file shares, queues, tables, and managed disks.
## Table of Contents
- [Storage Entity Types](#storage-entity-types)
- [Storage Account Configuration](#storage-account-configuration)
- [Storage Services](#storage-services)
- [Managed Disks](#managed-disks)
- [Cross-Service Analysis](#cross-service-analysis)
## Storage Entity Types
All these types support the standard discovery pattern: `smartscapeNodes "<TYPE>" | fields name, id, azure.subscription, azure.resource.group, azure.location, ...`
| Entity Type | Description |
|---|---|
| `AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS` | Azure Storage Accounts |
| `AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_BLOBSERVICES_CONTAINERS` | Blob containers |
| `AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_FILESERVICES_SHARES` | File shares |
| `AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_QUEUESERVICES_QUEUES` | Storage queues |
| `AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_TABLESERVICES_TABLES` | Storage tables |
| `AZURE_MICROSOFT_COMPUTE_DISKS` | Managed disks |
## Storage Account Configuration
### Inventory and SKU Analysis
List all Storage Accounts with SKU and tier:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd accountKind = azjson[configuration][kind],
skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
accessTier = azjson[configuration][properties][accessTier]
| fields name, accountKind, skuName, skuTier, accessTier,
azure.resource.group, azure.location
```
Summarize Storage Accounts by replication type (SKU):
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name]
| summarize account_count = count(), by: {skuName}
| sort account_count desc
```
Summarize Storage Accounts by access tier:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd accessTier = azjson[configuration][properties][accessTier]
| summarize account_count = count(), by: {accessTier}
| sort account_count desc
```
### HTTPS Enforcement and TLS
Find Storage Accounts and their HTTPS/TLS configuration:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd httpsOnly = azjson[configuration][properties][supportsHttpsTrafficOnly],
minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| fields name, httpsOnly, minimumTlsVersion, azure.resource.group, azure.location
```
Find Storage Accounts not enforcing HTTPS:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd httpsOnly = azjson[configuration][properties][supportsHttpsTrafficOnly]
| filter httpsOnly == false
| fields name, azure.resource.group, azure.location
```
Find Storage Accounts with TLS version below 1.2:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd minimumTlsVersion = azjson[configuration][properties][minimumTlsVersion]
| filter minimumTlsVersion != "TLS1_2"
| fields name, minimumTlsVersion, azure.resource.group, azure.location
```
### Public Access and Network Rules
Find Storage Accounts allowing public blob access:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd allowBlobPublicAccess = azjson[configuration][properties][allowBlobPublicAccess],
networkDefaultAction = azjson[configuration][properties][networkAcls][defaultAction]
| fields name, allowBlobPublicAccess, networkDefaultAction,
azure.resource.group, azure.location
```
Find Storage Accounts with open network access (defaultAction = Allow):
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd networkDefaultAction = azjson[configuration][properties][networkAcls][defaultAction]
| filter networkDefaultAction == "Allow"
| fields name, azure.resource.group, azure.location
```
### Encryption Configuration
Check encryption key source for Storage Accounts:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd keySource = azjson[configuration][properties][encryption][keySource],
blobEncryption = azjson[configuration][properties][encryption][services][blob][enabled]
| fields name, keySource, blobEncryption, azure.resource.group, azure.location
```
### Primary Region Status
Check primary region availability status:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| parse azure.object, "JSON:azjson"
| fieldsAdd statusOfPrimary = azjson[configuration][properties][statusOfPrimary],
primaryEndpoint = azjson[configuration][properties][primaryEndpoints][blob]
| fields name, statusOfPrimary, primaryEndpoint, azure.resource.group, azure.location
```
## Storage Services
### Blob Containers
List all blob containers:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_BLOBSERVICES_CONTAINERS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Find blob containers belonging to a specific Storage Account (backward traversal):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| filter name == "<STORAGE_ACCOUNT_NAME>"
| traverse "*", "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_BLOBSERVICES_CONTAINERS", direction:backward
| fields name, id, azure.resource.group
```
Count blob containers per Storage Account:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_BLOBSERVICES_CONTAINERS"
| traverse "*", "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| fieldsAdd accountName = name
| summarize container_count = count(), by: {accountName}
| sort container_count desc
```
### File Shares
List all file shares:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_FILESERVICES_SHARES"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Find file shares belonging to a specific Storage Account:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| filter name == "<STORAGE_ACCOUNT_NAME>"
| traverse "*", "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_FILESERVICES_SHARES", direction:backward
| fields name, id, azure.resource.group
```
Count file shares per Storage Account:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_FILESERVICES_SHARES"
| traverse "*", "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| fieldsAdd accountName = name
| summarize share_count = count(), by: {accountName}
| sort share_count desc
```
### Queue Services
List all storage queues:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_QUEUESERVICES_QUEUES"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Find queues belonging to a specific Storage Account:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| filter name == "<STORAGE_ACCOUNT_NAME>"
| traverse "*", "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_QUEUESERVICES_QUEUES", direction:backward
| fields name, id, azure.resource.group
```
### Table Services
List all storage tables:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_TABLESERVICES_TABLES"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Find tables belonging to a specific Storage Account:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| filter name == "<STORAGE_ACCOUNT_NAME>"
| traverse "*", "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_TABLESERVICES_TABLES", direction:backward
| fields name, id, azure.resource.group
```
### All Sub-Resources for a Storage Account
Find all storage services (blob, file, queue, table) for a specific account:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_BLOBSERVICES_CONTAINERS",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_FILESERVICES_SHARES",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_QUEUESERVICES_QUEUES",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_TABLESERVICES_TABLES"
| traverse "*", "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS"
| filter name == "<STORAGE_ACCOUNT_NAME>"
| fieldsAdd accountName = name
| fields accountName, dt.traverse.history[0][id], type
| fieldsRename subResourceId = `dt.traverse.history[0][id]`
| sort type
```
## Managed Disks
List all managed disks:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Find managed disks with SKU and size details:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name],
skuTier = azjson[configuration][sku][tier],
diskSizeGB = azjson[configuration][properties][diskSizeGB],
diskState = azjson[configuration][properties][diskState],
osType = azjson[configuration][properties][osType]
| fields name, skuName, skuTier, diskSizeGB, diskState, osType,
azure.resource.group, azure.location
```
Find unattached managed disks (potential cost waste):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd diskState = azjson[configuration][properties][diskState],
diskSizeGB = azjson[configuration][properties][diskSizeGB],
skuName = azjson[configuration][sku][name]
| filter diskState == "Unattached"
| fields name, diskState, diskSizeGB, skuName, azure.resource.group, azure.location
```
Summarize managed disks by SKU:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name]
| summarize disk_count = count(), by: {skuName}
| sort disk_count desc
```
Find VMs and their attached disks (VM → Disks forward traversal):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| traverse "*", "AZURE_MICROSOFT_COMPUTE_DISKS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| parse azure.object, "JSON:azjson"
| fieldsAdd diskSizeGB = azjson[configuration][properties][diskSizeGB],
skuName = azjson[configuration][sku][name]
| lookup [smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "vm."
| fields vm.name, name, diskSizeGB, skuName, azure.resource.group
```
Find disks attached to AKS clusters (Disk → AKS backward traversal):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS"
| traverse "*", "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_COMPUTE_DISKS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "disk."
| fields disk.name, name, azure.resource.group
```
## Cross-Service Analysis
Count all storage resources by type:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_BLOBSERVICES_CONTAINERS",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_FILESERVICES_SHARES",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_QUEUESERVICES_QUEUES",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_TABLESERVICES_TABLES",
"AZURE_MICROSOFT_COMPUTE_DISKS"
| summarize total = count(), by: {type}
| sort total desc
```
Count storage resources by region:
```dql
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS",
"AZURE_MICROSOFT_COMPUTE_DISKS"
| summarize total = count(), by: {type, azure.location}
| sort azure.location, total desc
```
Find all storage resources in a specific resource group:
```dql-template
smartscapeNodes "AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_BLOBSERVICES_CONTAINERS",
"AZURE_MICROSOFT_STORAGE_STORAGEACCOUNTS_FILESERVICES_SHARES",
"AZURE_MICROSOFT_COMPUTE_DISKS"
| filter azure.resource.group == "<RESOURCE_GROUP>"
| fields type, name, azure.location, azure.provisioning_state
```
references/vnet-networking-security.md
# Azure VNet Networking & Security
Monitor and troubleshoot Azure Virtual Network infrastructure, Network Security Groups, subnets, and connectivity.
## Table of Contents
- [VNet Discovery](#vnet-discovery)
- [NSG Analysis](#nsg-analysis)
- [Subnet & VM Distribution](#subnet--vm-distribution)
- [Internet-Facing Resources](#internet-facing-resources)
- [Network Infrastructure](#network-infrastructure)
- [Availability Zone Distribution](#availability-zone-distribution)
## VNet Discovery
List all VNets:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS"
| fields name, id, azure.subscription, azure.resource.group, azure.location,
azure.provisioning_state
```
Get VNet address space:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd addressPrefixes = azjson[configuration][properties][addressSpace][addressPrefixes],
ddosProtection = azjson[configuration][properties][enableDdosProtection]
| fields name, azure.resource.group, azure.location, addressPrefixes, ddosProtection
```
Get all subnets in a specific VNet via backward traversal (Subnet → VNet):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS"
| filter id == "<VNET_ENTITY_ID>"
| traverse "*", "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS", direction:backward
| fields name, id, azure.resource.group
```
Count resources connected to each VNet by type (via subnets):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS"
| fieldsAdd vnetName = name
| lookup [
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES_IPCONFIGURATIONS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| fields subnetId = id
], sourceField:id, lookupField:subnetId
| summarize resource_count = count(), by: {vnetName}
| sort resource_count desc
```
## NSG Analysis
List all NSGs with their associated resource counts:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Find subnets and their associated NSGs (Subnet → NSG forward traversal):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| fieldsAdd nsgName = name, nsgId = id
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "src."
| fields src.name, nsgName, nsgId
```
Find NICs associated with a specific NSG (NIC → NSG backward on NSG):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| filter id == "<NSG_ENTITY_ID>"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES", direction:backward
| fields name, id, azure.resource.group
```
Find VMSS instances associated with an NSG (VMSS → NSG backward on NSG):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| filter id == "<NSG_ENTITY_ID>"
| traverse "*", "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS", direction:backward
| fields name, id, azure.resource.group
```
Analyze NSG security rules from azure.object:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = azjson[configuration][properties][securityRules]
| expand rules
| fieldsAdd ruleName = rules[name],
direction = rules[properties][direction],
access = rules[properties][access],
protocol = rules[properties][protocol],
sourcePrefix = rules[properties][sourceAddressPrefix],
destPrefix = rules[properties][destinationAddressPrefix],
destPort = rules[properties][destinationPortRange],
priority = rules[properties][priority]
| fields name, ruleName, direction, access, protocol, sourcePrefix, destPrefix, destPort, priority
| sort name, priority asc
```
Find NSGs with inbound rules allowing traffic from the internet:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKSECURITYGROUPS"
| parse azure.object, "JSON:azjson"
| fieldsAdd rules = azjson[configuration][properties][securityRules]
| expand rules
| fieldsAdd ruleName = rules[name],
direction = rules[properties][direction],
access = rules[properties][access],
sourcePrefix = rules[properties][sourceAddressPrefix],
destPort = rules[properties][destinationPortRange],
priority = rules[properties][priority]
| filter direction == "Inbound" AND access == "Allow"
| filter sourcePrefix == "*" OR sourcePrefix == "Internet" OR sourcePrefix == "0.0.0.0/0"
| fields name, ruleName, sourcePrefix, destPort, priority
| sort priority asc
```
## Subnet & VM Distribution
List all subnets with their parent VNet:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| fieldsAdd vnetName = name
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS" | fields name, azure.resource.group, id], sourceField: sourceId, lookupField: id, prefix: "src."
| fields src.name, vnetName, src.azure.resource.group
```
Count NIC IP configurations per subnet (approximation of VMs per subnet):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES_IPCONFIGURATIONS", direction:backward
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "src."
| summarize nic_count = count(), by: {src.name}
| sort nic_count desc
```
Count VMSS instances per subnet:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| traverse "*", "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS", direction:backward
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "src."
| summarize vmss_count = count(), by: {src.name}
| sort vmss_count desc
```
Find VMs in a specific VNet (VM → NIC → IP Config → Subnet → VNet chain):
```dql-template
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| fieldsAdd vmName = name, vmId = id
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES", fieldsKeep:{vmName, vmId}
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES_IPCONFIGURATIONS", direction:backward, fieldsKeep:{vmName, vmId}
| traverse "*", "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS", fieldsKeep:{vmName, vmId}
| traverse "*", "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS", fieldsKeep:{vmName, vmId}
| filter name == "<VNET_NAME>"
| fields vmName, vmId
```
## Internet-Facing Resources
List all public IP addresses:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_PUBLICIPADDRESSES"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
Find public IPs with their allocation method and assigned address:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_PUBLICIPADDRESSES"
| parse azure.object, "JSON:azjson"
| fieldsAdd ipAddress = azjson[configuration][properties][ipAddress],
allocationMethod = azjson[configuration][properties][publicIPAllocationMethod],
sku = azjson[configuration][sku][name]
| fields name, ipAddress, allocationMethod, sku, azure.resource.group, azure.location
```
Find VMs with public IPs (VM → NIC, then check NIC for public IP association):
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| traverse "*", "AZURE_MICROSOFT_NETWORK_NETWORKINTERFACES"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| parse azure.object, "JSON:azjson"
| fieldsAdd publicIpId = azjson[configuration][properties][ipConfigurations][0][properties][publicIPAddress][id]
| filter isNotNull(publicIpId)
| lookup [smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "vm."
| fields vm.name, vm.id, name, publicIpId
```
## Network Infrastructure
NAT gateways (if present):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NATGATEWAYS"
| fields name, id, azure.resource.group, azure.location
```
VPN gateways:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKGATEWAYS"
| parse azure.object, "JSON:azjson"
| fieldsAdd gatewayType = azjson[configuration][properties][gatewayType],
vpnType = azjson[configuration][properties][vpnType],
sku = azjson[configuration][properties][sku][name]
| fields name, gatewayType, vpnType, sku, azure.resource.group, azure.location
```
VPN connections:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_CONNECTIONS"
| parse azure.object, "JSON:azjson"
| fieldsAdd connectionType = azjson[configuration][properties][connectionType],
connectionStatus = azjson[configuration][properties][connectionStatus]
| fields name, connectionType, connectionStatus, azure.resource.group, azure.location
```
ExpressRoute circuits:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_EXPRESSROUTECIRCUITS"
| parse azure.object, "JSON:azjson"
| fieldsAdd serviceProviderName = azjson[configuration][properties][serviceProviderProperties][serviceProviderName],
bandwidthInMbps = azjson[configuration][properties][serviceProviderProperties][bandwidthInMbps],
circuitProvisioningState = azjson[configuration][properties][circuitProvisioningState]
| fields name, serviceProviderName, bandwidthInMbps, circuitProvisioningState, azure.location
```
VNet peering connections (embedded in VNet azure.object):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS"
| parse azure.object, "JSON:azjson"
| fieldsAdd peerings = azjson[configuration][properties][virtualNetworkPeerings]
| expand peerings
| fieldsAdd peeringName = peerings[name],
peeringState = peerings[properties][peeringState],
remoteVNetId = peerings[properties][remoteVirtualNetwork][id]
| filter isNotNull(peeringName)
| fields name, peeringName, peeringState, remoteVNetId
```
Network watchers:
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_NETWORKWATCHERS"
| fields name, id, azure.resource.group, azure.location, azure.provisioning_state
```
## Availability Zone Distribution
View VM distribution across availability zones:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| traverse "*", "AZURE_MICROSOFT_RESOURCES_LOCATIONS_AVAILABILITYZONES"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES" | fields name, azure.location, id], sourceField: sourceId, lookupField: id, prefix: "vm."
| fields vm.name, vm.azure.location, name
| sort vm.azure.location, name
```
Summarize VM count per availability zone:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| traverse "*", "AZURE_MICROSOFT_RESOURCES_LOCATIONS_AVAILABILITYZONES"
| summarize vm_count = count(), by: {name}
| sort vm_count desc
```
VMSS distribution across availability zones:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| traverse "*", "AZURE_MICROSOFT_RESOURCES_LOCATIONS_AVAILABILITYZONES"
| fieldsAdd sourceId = dt.traverse.history[0][id]
| lookup [smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS" | fields name, id], sourceField: sourceId, lookupField: id, prefix: "src."
| fields src.name, name
| sort name
```
references/workload-detection.md
# Workload Detection Reference
Identify how an Azure VM or VMSS is orchestrated. Run these detection queries during problem analysis to determine which orchestration system manages the affected resource — this determines the correct resolution path.
## Table of Contents
- [Overview](#overview)
- [1. Load Balancer Detection](#1-load-balancer-detection)
- [2. VMSS Detection](#2-vmss-detection)
- [3. AKS Node Detection](#3-aks-node-detection)
- [4. Standalone VM](#4-standalone-vm)
## Overview
Azure VMs and VMSS instances can be managed by different orchestration systems, each requiring a different resolution approach. Run the detection queries below in order to identify the workload pattern before following a resolution path.
**Detection Hierarchy:**
Run detections in this order — stop at the first match:
1. **Load Balancer / Application Gateway** — is the resource behind a load balancer?
2. **VMSS membership** — is the VM part of a Virtual Machine Scale Set?
3. **AKS node** — is the VMSS managed by AKS?
4. **Standalone VM** — none of the above
**Workload Pattern Summary**
| Indicator | Workload Pattern | Resolution Path |
|---|---|---|
| `aks-managed-poolName` tag present on VMSS | AKS node | Cordon + drain via kubectl; node pool autoscaler handles replacement |
| Part of VMSS (no AKS tags) | VMSS member | VMSS handles replacement via scaling rules |
| Behind Load Balancer / App Gateway | Load balanced | Shift traffic before remediation |
| None of the above | Standalone VM | Direct remediation (restart, resize, replace) |
Replace `<WORKLOAD_VM_ENTITY_ID>` with the Dynatrace entity ID of the affected VM (e.g., `AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES-2B0D33F11CE649F4`).
---
## 1. Load Balancer Detection
Determine if the VM is behind an Azure Load Balancer. VMs connect to LBs through NICs → Backend Address Pools → Load Balancers.
**Check if VM's VMSS is in a Load Balancer backend pool:**
```dql-template
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| filter id == "<WORKLOAD_VM_ENTITY_ID>"
| traverse "*", "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_BACKENDADDRESSPOOLS"
| fields name, id, azure.resource.group
```
**Find the Load Balancer for the backend pool:**
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_LOADBALANCERS_BACKENDADDRESSPOOLS"
| fields name, id, azure.resource.group
```
**Find the VM's subnet (via VMSS, for network context):**
> **Note:** Azure Application Gateways do not have direct Smartscape relationships to subnets or VMs. This query identifies the subnet a VMSS-backed VM belongs to. To confirm App Gateway involvement, check the App Gateway's backend pool configuration separately (see `references/load-balancing-api.md`).
```dql-template
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| filter id == "<WORKLOAD_VM_ENTITY_ID>"
| traverse "*", "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| traverse "*", "AZURE_MICROSOFT_NETWORK_VIRTUALNETWORKS_SUBNETS"
| fields name, id, azure.resource.group
```
**If results returned:** The VM is part of a VMSS connected to a subnet. Cross-reference the subnet with load balancer backend pools and Application Gateway configurations to determine if traffic routing is involved.
Identify the Load Balancer SKU and AKS association (if any):
```dql
smartscapeNodes "AZURE_MICROSOFT_NETWORK_LOADBALANCERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd skuName = azjson[configuration][sku][name]
| fieldsAdd aksCluster = azjson[tags][`aks-managed-cluster-name`]
| fields name, skuName, aksCluster, azure.resource.group, azure.location
```
---
## 2. VMSS Detection
Determine if the VM is part of a Virtual Machine Scale Set.
**Check VMSS membership:**
```dql-template
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| filter id == "<WORKLOAD_VM_ENTITY_ID>"
| traverse "*", "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][sku][name],
capacity = azjson[configuration][sku][capacity]
| fields name, id, vmSize, capacity, azure.resource.group
```
**If results returned:** The VM is part of a VMSS. Record the VMSS name, current capacity, and VM size. Check if it is AKS-managed (next section).
**If no results:** The VM is standalone — skip to [Section 4](#4-standalone-vm).
Check VMSS-to-AKS association to distinguish generic VMSS from AKS node pools:
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| parse azure.object, "JSON:azjson"
| fieldsAdd aksPoolName = azjson[configuration][tags][`aks-managed-poolName`],
aksOrchestrator = azjson[configuration][tags][`aks-managed-orchestrator`]
| filter isNotNull(aksPoolName)
| fields name, id, aksPoolName, aksOrchestrator, azure.resource.group
```
---
## 3. AKS Node Detection
Detect whether the VMSS is an AKS node pool. AKS-managed VMSS instances carry specific tags that identify them.
**Key AKS tags on VMSS:**
| Tag | Description | Example |
|---|---|---|
| `aks-managed-poolName` | AKS node pool name | `spotnodes` |
| `aks-managed-orchestrator` | Kubernetes version | `Kubernetes:1.33.7` |
| `aks-managed-cluster-name` | AKS cluster name (on LB) | `aks-parser-dev` |
**Step 1 — Detect AKS tags on the VMSS:**
```dql-template
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| filter id == "<WORKLOAD_VM_ENTITY_ID>"
| traverse "*", "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| parse azure.object, "JSON:azjson"
| fieldsAdd aksPoolName = azjson[configuration][tags][`aks-managed-poolName`],
aksOrchestrator = azjson[configuration][tags][`aks-managed-orchestrator`]
| filter isNotNull(aksPoolName)
| fields name, id, aksPoolName, aksOrchestrator, azure.resource.group
```
**Step 2 — Find the AKS cluster entity:**
```dql
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINESCALESETS"
| traverse "*", "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS"
| parse azure.object, "JSON:azjson"
| fieldsAdd k8sVersion = azjson[configuration][properties][kubernetesVersion],
powerState = azjson[configuration][properties][powerState][code]
| fields name, id, k8sVersion, powerState, azure.resource.group, azure.location
```
**Step 3 — Check AKS agent pool details:**
```dql
smartscapeNodes "AZURE_MICROSOFT_CONTAINERSERVICE_MANAGEDCLUSTERS_AGENTPOOLS"
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][vmSize],
nodeCount = azjson[configuration][count],
minCount = azjson[configuration][minCount],
maxCount = azjson[configuration][maxCount],
enableAutoScaling = azjson[configuration][enableAutoScaling],
mode = azjson[configuration][mode]
| fields name, vmSize, nodeCount, minCount, maxCount, enableAutoScaling, mode
```
**Record:** AKS cluster name, node pool name, Kubernetes version, and autoscaler configuration — needed for resolution guidance (e.g., cordon/drain via kubectl, scale the node pool, or rely on cluster autoscaler for replacement).
---
## 4. Standalone VM
If none of the above detection queries return results, the VM is standalone — not part of a VMSS, not behind a load balancer, and not AKS-managed.
**Gather VM details for direct remediation:**
```dql-template
smartscapeNodes "AZURE_MICROSOFT_COMPUTE_VIRTUALMACHINES"
| filter id == "<WORKLOAD_VM_ENTITY_ID>"
| parse azure.object, "JSON:azjson"
| fieldsAdd vmSize = azjson[configuration][properties][hardwareProfile][vmSize],
powerState = azjson[configuration][properties][extended][instanceView][powerState][displayStatus],
osType = azjson[configuration][properties][storageProfile][osDisk][osType],
provisioningState = azjson[configuration][properties][provisioningState]
| fields name, id, vmSize, powerState, osType, provisioningState,
azure.resource.group, azure.location, azure.subscription
```
**Resolution options for standalone VMs:**
- **Restart:** Restart the VM directly via Azure portal or CLI
- **Resize:** Change VM SKU if resource saturation is the issue
- **Replace:** Redeploy the VM if it is in a failed state