All scripts
Azure AI 329

Get-AzureUnusedResourceReport

Finds unattached disks, unused public IPs, and empty resource groups across the subscription in one pass, the classic sources of quiet Azure cost that nobody owns.

Get-AzureUnusedResourceReport.ps1
<#
.SYNOPSIS
    Finds common sources of unused Azure spend.

.DESCRIPTION
    Checks for unattached managed disks, unattached public IP addresses,
    and empty resource groups in one pass, since these three are the
    most common sources of quiet, unowned Azure cost that accumulate
    after testing or cleanup gets left half finished.

.EXAMPLE
    .\Get-AzureUnusedResourceReport.ps1

.NOTES
    Requires the Az.Compute, Az.Network and Az.Resources modules and an
    active Connect-AzAccount session.

.AUTHOR
    Shehryar Hassan
#>

$unattachedDisks = Get-AzDisk | Where-Object { $_.DiskState -eq "Unattached" }
$unattachedIps = Get-AzPublicIpAddress | Where-Object { -not $_.IpConfiguration }
$resourceGroups = Get-AzResourceGroup
$emptyGroups = $resourceGroups | Where-Object { (Get-AzResource -ResourceGroupName $_.ResourceGroupName).Count -eq 0 }

Write-Host "Unattached disks: $($unattachedDisks.Count)" -ForegroundColor Yellow
$unattachedDisks | Select-Object Name, ResourceGroupName, DiskSizeGB | Format-Table -AutoSize

Write-Host "Unattached public IPs: $($unattachedIps.Count)" -ForegroundColor Yellow
$unattachedIps | Select-Object Name, ResourceGroupName | Format-Table -AutoSize

Write-Host "Empty resource groups: $($emptyGroups.Count)" -ForegroundColor Yellow
$emptyGroups | Select-Object ResourceGroupName | Format-Table -AutoSize

Read it before you run it, and test in a safe tenant first.