What is Azure Resource Graph?
Azure Resource Graph lets you query all your resources across subscriptions using KQL (Kusto Query Language)—fast and at scale.
👉 Perfect for finding:
- Orphaned disks
- Unattached NICs
- Unused public IPs
- Resources missing relationships
What is an “Orphaned Resource”?
An orphaned resource is:
- Not attached to anything
- Still costing money or creating risk
Examples:
- Disk not attached to any VM
- Public IP not associated
- NIC not connected
- NSG not applied
Common Queries to Find Orphaned Resources
1. Unattached Managed Disks
Resources| where type == "microsoft.compute/disks"| where properties.diskState == "Unattached"| project name, resourceGroup, location, diskSizeGB
👉 Finds disks not connected to any VM
2. Unused Public IP Addresses
Resources| where type == "microsoft.network/publicipaddresses"| where isnull(properties.ipConfiguration)| project name, resourceGroup, location, sku
👉 These are exposed but unused → security + cost risk
3. Unattached Network Interfaces (NICs)
Resources| where type == "microsoft.network/networkinterfaces"| where isnull(properties.virtualMachine)| project name, resourceGroup, location
4. Unused Network Security Groups (NSGs)
Resources| where type == "microsoft.network/networksecuritygroups"| where isnull(properties.networkInterfaces) and isnull(properties.subnets)| project name, resourceGroup, location
5. Empty Resource Groups (Bonus)
ResourceContainers| where type == "microsoft.resources/subscriptions/resourcegroups"| join kind=leftouter ( Resources | summarize count() by resourceGroup) on resourceGroup| where count_ == 0 or isnull(count_)| project resourceGroup
How to Run These Queries
You can run them in:
- Azure Portal → Resource Graph Explorer
- CLI:
az graph query -q "<query>" - PowerShell:
Search-AzGraph -Query "<query>"
Pro Tip (Senior-Level Insight)
👉 Don’t just find orphaned resources—automate cleanup
- Schedule queries using:
- Azure Automation
- Logic Apps
- Trigger:
- Alerts
- Cleanup workflows
Interview Answer
I use Azure Resource Graph with KQL queries to identify orphaned resources at scale across subscriptions. For example, I can query for unmanaged disks where the disk state is unattached, or public IPs without an associated configuration. Similarly, I check for NICs not linked to VMs and NSGs not applied to subnets or interfaces.
Beyond detection, I typically integrate these queries into automated governance workflows—using alerts or scheduled jobs to either notify teams or trigger cleanup—so we continuously reduce cost and improve security posture.
One-Liner to Remember
👉
“Resource Graph + KQL = fast, cross-subscription visibility for orphaned resources.”
Here’s a solid production-ready pattern, plus a script approach you can talk through in an interview.
Production cleanup strategy
Use Azure Resource Graph for detection, then use Azure Automation with Managed Identity for controlled remediation. Resource Graph is built for cross-subscription inventory queries at scale, and its query language is based on KQL. You can run the same queries in the portal, Azure CLI with az graph query, or PowerShell with Search-AzGraph. (Microsoft Learn)
Safe workflow
Phase 1: Detect
Run queries for likely orphaned resources such as unattached disks, unused public IPs, unattached NICs, and unused NSGs. Azure documents advanced query samples and the CLI quickstart for running them. (Microsoft Learn)
Phase 2: Classify
Do not delete immediately. First separate findings into:
- definitely orphaned
- likely orphaned
- needs human review
A good rule is to require at least one of these before cleanup:
- older than X days
- no
keeptag - no recent change window
- not in protected subscriptions or resource groups
You can also use Resource Graph change history to review whether a resource was recently modified before acting. (Microsoft Learn)
Phase 3: Notify
Send a report to the owning team or central platform team. Include:
- resource ID
- resource group
- subscription
- resource age or last change
- proposed action
- deadline for objection
Phase 4: Quarantine before delete
For risky resource types, first tag them with something like:
cleanupCandidate=truecleanupMarkedDate=2026-04-13cleanupOwner=platform
Then wait 7 to 30 days depending on the environment.
Phase 5: Delete with guardrails
Only auto-delete low-risk items such as clearly unattached disks or unused public IPs after the waiting window. Keep production subscriptions on approval-based cleanup unless the criteria are extremely strict.
Good governance rules
A mature setup usually includes:
- exclusion tags like
doNotDelete=true - separate policy for prod vs non-prod
- allowlist of critical subscriptions
- dry-run mode by default
- centralized logs of all cleanup actions
- approval gate for medium-risk deletions
This aligns well with Azure’s broader security and operations guidance, and Azure Automation supports managed identities so runbooks can access Azure without stored secrets. (Microsoft Learn)
Example architecture
Azure Resource Graph | vScheduled Automation Runbook(with Managed Identity) | +--> Query orphaned resources +--> Filter by tags / age / subscription +--> Write report to Storage / Log Analytics +--> Notify owners +--> Optional approval step +--> Delete approved resources
Example: Azure CLI script
This is a simple version for unattached managed disks. Start in report-only mode.
#!/usr/bin/env bashset -euo pipefailQUERY="Resources| where type =~ 'microsoft.compute/disks'| where properties.diskState =~ 'Unattached'| project id, name, resourceGroup, subscriptionId, location, tags"echo "Finding unattached managed disks..."az graph query -q "$QUERY" --first 1000 -o json > orphaned-disks.jsonecho "Report saved to orphaned-disks.json"cat orphaned-disks.json | jq -r '.data[] | [.subscriptionId, .resourceGroup, .name, .id] | @tsv'
Azure CLI supports az graph query for Resource Graph queries. (Microsoft Learn)
Example: safer delete flow in Bash
This version only deletes disks that:
- are unattached
- are not tagged
doNotDelete=true
#!/usr/bin/env bashset -euo pipefailQUERY="Resources| where type =~ 'microsoft.compute/disks'| where properties.diskState =~ 'Unattached'| extend doNotDelete = tostring(tags.doNotDelete)| where doNotDelete !~ 'true'| project id, name, resourceGroup, subscriptionId, location"RESULTS=$(az graph query -q "$QUERY" --first 1000 -o json)echo "$RESULTS" | jq -c '.data[]' | while read -r row; do ID=$(echo "$row" | jq -r '.id') NAME=$(echo "$row" | jq -r '.name') echo "Deleting unattached disk: $NAME" az resource delete --ids "$ID"done
For production, add:
- dry-run flag
- approval list
- deletion logging
- retry handling
- resource locks check
Example: PowerShell runbook pattern
This is closer to what many platform teams use in Azure Automation.
Disable-AzContextAutosave -Scope ProcessConnect-AzAccount -Identity$query = @"Resources| where type =~ 'microsoft.network/publicipaddresses'| where isnull(properties.ipConfiguration)| extend doNotDelete = tostring(tags.doNotDelete)| where doNotDelete !~ 'true'| project id, name, resourceGroup, subscriptionId, location"@$results = Search-AzGraph -Query $queryforeach ($item in $results) { Write-Output "Cleanup candidate: $($item.name) [$($item.id)]" # Dry run by default # Remove-AzResource -ResourceId $item.id -Force}
Search-AzGraph is the PowerShell command for Resource Graph, and Azure Automation supports system-assigned or user-assigned managed identities for authenticating runbooks securely. (Microsoft Learn)
What to say in an interview
A strong answer would sound like this:
I’d use Azure Resource Graph to detect orphaned resources across subscriptions, then feed those results into an Azure Automation runbook running under Managed Identity. I would never delete immediately. Instead, I’d apply filters like age, tags, subscription scope, and recent change history, then notify owners or mark resources for cleanup first. For low-risk resources in non-production, I might automate deletion after a quarantine period. For production, I’d usually keep an approval gate. That gives you cost control without creating operational risk. (Microsoft Learn)
Best resource types to target first
Start with the safest, highest-confidence cleanup candidates:
- unattached managed disks
- public IPs with no association
- NICs not attached to VMs
- NSGs not attached to subnets or NICs (Microsoft Learn)