Azure resource management

How to find unattached Azure disks

Find unattached managed disks with Azure PowerShell, review them in Azure Resource Graph Explorer, or run Altivane's Azure Unused Resources report for a workbook.

By the Published Updated

To find unattached Azure disks, query Azure Resource Graph for managed disks whose state is Unattached and that have no managing virtual machine. The index can lag behind a recent change, and the result does not tell you whether a disk is a backup that should be kept.

Choose the method that fits the job
MethodBest fit
PowerShellA repeatable, sortable CSV across selected subscriptions.
Admin centreA quick review in Azure Resource Graph Explorer without installing tools.
AltivaneRecommended for recurring reviews across readable subscriptions, with report files to keep.

Before you start

For PowerShell or Resource Graph Explorer, use an Azure account with read access to the subscriptions you want to review. The built-in Reader role provides that access; assign it at each subscription or a parent scope. The example connects interactively and uses the Az.Accounts and Az.ResourceGraph modules. The Microsoft references do not list a separate Resource Graph licence.

Azure Resource Graph is a near real-time index, not a strongly consistent copy of the resource provider. A recent attach or detach can take time to appear. The query covers managed disks in the selected subscriptions; it does not inventory unmanaged VHD files in storage accounts. It returns at most 1,000 rows per request and follows the continuation token for additional pages. The examples read Azure and export a CSV; they make no changes to Azure resources. Their finally block disconnects the account and clears its local Azure contexts, so you may need to sign in again to use them. Check the disk's purpose and current state before acting on a result.

Option 1

Find unattached Azure disks with PowerShell

Install the modules once, sign in, and query the subscriptions the account can read. The script retrieves every result page, including when the list is larger than the service's first response.

PowerShell
Install-Module -Name Az.Accounts, Az.ResourceGraph -Repository PSGallery -Scope CurrentUser
PowerShell
$ErrorActionPreference = 'Stop'
Connect-AzAccount -ErrorAction Stop
try {
    $subscriptionIds = @(
        Get-AzSubscription |
        Where-Object State -eq 'Enabled' |
        Select-Object -ExpandProperty Id
    )
    if ($subscriptionIds.Count -eq 0) {
        Write-Output 'No enabled subscriptions are available to this account.'
        return
    }
    $query = @(
        'Resources'
        '| where type =~ "microsoft.compute/disks"'
        '| where tostring(properties.diskState) =~ "Unattached"'
        '| where isempty(managedBy)'
        '| project id, name, resourceGroup, subscriptionId, location,'
        'sizeGb = tolong(properties.diskSizeGB), sku = tostring(sku.name),'
        'created = todatetime(properties.timeCreated)'
        '| order by id asc'
    ) -join ' '
    function Get-AzGraphRows {
        param([string]$Query, [string[]]$SubscriptionIds)
        $results = [System.Collections.Generic.List[object]]::new()
        $skipToken = $null
        do {
            $arguments = @{
                Query = $Query
                Subscription = $SubscriptionIds
                First = 1000
            }
            if ($skipToken) { $arguments.SkipToken = $skipToken }
            $page = Search-AzGraph @arguments
            foreach ($record in $page.Data) { $results.Add($record) }
            $skipToken = $page.SkipToken
        } while ($skipToken)
        return $results.ToArray()
    }
    $rows = @(Get-AzGraphRows -Query $query -SubscriptionIds $subscriptionIds)
    if ($rows.Count -eq 0) {
        Write-Output 'No unattached managed disks were found.'
    } else {
        $rows | Select-Object name, resourceGroup, subscriptionId, location,
            sizeGb, sku, created, id |
            Export-Csv -Path './unattached-disks.csv' -NoTypeInformation -Encoding utf8
        Write-Output "$($rows.Count) disk rows saved to unattached-disks.csv."
    }
} finally {
    Disconnect-AzAccount | Out-Null
}

The CSV includes the disk name, subscription, resource group, location, size, SKU, creation time and resource ID. An empty result prints a message instead of creating an empty file. Change the type or state filters only if you intend to widen the search; this example deliberately includes managed disks in the Unattached state with no managedBy value.

Resource Graph pagination requires id in the projected results. Keep that column in the query if you edit it.

Option 2

Review unattached disks in Azure Resource Graph Explorer

  1. Sign in to the Azure portal with an account that can read the subscriptions.
  2. Search for resource graph > choose Resource Graph Explorer.
  3. To change its scope, choose Directory, then choose the directory, management group or subscription to query.
  4. Paste this read-only query in Query 1 and choose Run query:
    KQL
    Resources
    | where type =~ 'microsoft.compute/disks'
    | where tostring(properties.diskState) =~ 'Unattached'
    | where isempty(managedBy)
    | project id, name, resourceGroup, subscriptionId, location,
              sizeGb = tolong(properties.diskSizeGB),
              sku = tostring(sku.name),
              created = todatetime(properties.timeCreated)
    | order by id asc
  5. Review the matching disks in Results.

This method suits an occasional check when you want to inspect results straight away. Use PowerShell for a repeatable CSV export and automatic retrieval of pages beyond the first 1,000 rows. A disk can be unattached on purpose, so confirm its owner and retention role before scheduling any cleanup.

Common questions

Does unattached mean safe to delete?

No. The query reports disks not attached to a virtual machine, but a disk may be an intentional backup or retained data. Confirm its owner, recovery purpose and current state with the resource provider before deleting it.

Why is a disk missing just after I detached it?

Resource Graph indexes changes asynchronously. Wait for the index to catch up, then run the query again. For a decision that depends on the disk's current state, check the disk directly in Azure rather than relying only on the index.

Does this include unused public IP addresses?

No. Those are a separate table in the same report. See how to find unused public IP addresses in Azure.

Microsoft references

Technical references checked on 7 October 2026. The example scripts have been reviewed and syntax checked; they have not been run against a live tenant.

Talk to us about Altivane

Questions, or pricing? Get in touch.

Contact us