Azure resource management

How to find empty resource groups in Azure

Find empty resource groups 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 empty Azure resource groups, compare the resource groups in Azure Resource Graph with the resources indexed for each subscription. Resource Graph can omit some resource types and its index can lag, so verify a group in Azure before treating it as empty or deleting it.

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

Use an Azure account with read access to the subscriptions you want to check. The built-in Reader role is sufficient for Resource Graph queries; assign it at the subscription or a parent scope. The PowerShell example uses Az.Accounts and Az.ResourceGraph. Microsoft's Resource Graph prerequisites do not list a separate product licence.

The query counts entries in Resource Graph's Resources table for each group in ResourceContainers. It can only count resources the index contains. Azure documents Resource Graph as an asynchronous, near real-time index, and some resource types may not be represented in its data. The script scopes the query to enabled subscriptions the account can read and paginates beyond the first 1,000 results. It reads Azure and exports a CSV without changing resources. Its finally block disconnects the account and clears its local Azure contexts, so you may need to sign in again to use them. Check a reported group and its contents directly in Azure before any cleanup.

Option 1

Find empty resource groups with PowerShell

Install the modules once and connect to Azure. The query starts with resource group records, joins the indexed resources by subscription and group name, and keeps groups with a count of zero.

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 = @(
        'ResourceContainers'
        '| where type =~ "microsoft.resources/subscriptions/resourcegroups"'
        '| project id, name, subscriptionId, location, key = tolower(name)'
        '| join kind=leftouter ('
        '    Resources'
        '    | summarize itemCount = count() by subscriptionId,'
        '        key = tolower(resourceGroup)'
        ') on subscriptionId, key'
        '| extend resourceCount = coalesce(itemCount, long(0))'
        '| where resourceCount == 0'
        '| project id, name, subscriptionId, location, resourceCount'
        '| 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 empty resource groups were found in the indexed resources.'
    } else {
        $rows | Select-Object name, subscriptionId, location, resourceCount, id |
            Export-Csv -Path './empty-resource-groups.csv' -NoTypeInformation -Encoding utf8
        Write-Output "$($rows.Count) group rows saved to empty-resource-groups.csv."
    }
} finally {
    Disconnect-AzAccount | Out-Null
}

The export includes each group's name, subscription, location, indexed resource count and full resource ID. An empty result produces a message, not an empty CSV. The count is specifically a count of resources represented in Resource Graph; it is not a guarantee that every Azure service has no item in that group.

To narrow the search, keep only the subscription IDs you want in $subscriptionIds. The query keeps id in the output because Azure's continuation token requires it for reliable pagination.

Option 2

Review empty groups in Azure Resource Graph Explorer

  1. Sign in to the Azure portal with read access to 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 query in Query 1 and choose Run query:
    KQL
    ResourceContainers
    | where type =~ 'microsoft.resources/subscriptions/resourcegroups'
    | project id, name, subscriptionId, location, key = tolower(name)
    | join kind=leftouter (
        Resources
        | summarize itemCount = count() by subscriptionId,
            key = tolower(resourceGroup)
    ) on subscriptionId, key
    | extend resourceCount = coalesce(itemCount, long(0))
    | where resourceCount == 0
    | project id, name, subscriptionId, location, resourceCount
    | order by id asc
  5. Review the candidate groups in Results.

Explorer suits an occasional query when you want to inspect the result interactively. Use PowerShell when you need a repeatable CSV or automatic pagination. A zero count is a lead for verification, not authorisation to delete a group.

Common questions

Can I delete every group in the result?

No. Resource Graph may not list every resource type, and the group may hold resources you intend to keep. Check the live group contents, dependencies, ownership and retention requirements before deleting anything.

Why does an empty result still need a check?

The query counts only the resources represented in the Resource Graph index, which is updated asynchronously. A recent deployment or deletion may not have appeared yet. For a critical decision, verify the group's current contents with Azure Resource Manager.

Where can I find other Azure cleanup candidates?

The same report includes disks with no virtual machine and public IP addresses with no association. 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