All scripts
Azure AI 662
Azure Orphaned Managed Disk Report
Finds managed disks in Azure that are not attached to any VM, so you can review and clean up disks that are quietly costing money every month.
Get-AzureOrphanedDiskReport.ps1
<#
.SYNOPSIS
Reports on Azure managed disks that are not attached to any virtual machine.
.DESCRIPTION
Unattached managed disks keep billing every month even though nothing is using them.
This script scans the current subscription, finds every managed disk with a disk state
of "Unattached", and reports the disk name, resource group, size, SKU, region, and how
many days it has existed. Use it to catch leftover disks from deleted VMs, failed
deployments, or old test environments before they add up on the bill.
.AUTHOR
Shehryar Hassan
.EXAMPLE
.\Get-AzureOrphanedDiskReport.ps1
Scans the current subscription context and prints a table of orphaned disks with their
size, SKU, and age.
.EXAMPLE
.\Get-AzureOrphanedDiskReport.ps1 -SubscriptionId "11111111-2222-3333-4444-555555555555" -ExportPath "C:\Reports\orphaned-disks.csv"
Scans a specific subscription and exports the results to CSV.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$SubscriptionId,
[Parameter(Mandatory = $false)]
[string]$ExportPath
)
if (-not (Get-Module -ListAvailable -Name Az.Compute)) {
Write-Error "The Az.Compute module is not installed. Run: Install-Module -Name Az -Scope CurrentUser"
return
}
if ($SubscriptionId) {
Write-Host "Setting context to subscription $SubscriptionId" -ForegroundColor Cyan
Set-AzContext -SubscriptionId $SubscriptionId | Out-Null
}
$context = Get-AzContext
if (-not $context) {
Write-Error "No active Azure context found. Run Connect-AzAccount first."
return
}
Write-Host "Scanning subscription: $($context.Subscription.Name)" -ForegroundColor Cyan
$allDisks = Get-AzDisk
$orphanedDisks = $allDisks | Where-Object { $_.DiskState -eq "Unattached" }
if (-not $orphanedDisks -or $orphanedDisks.Count -eq 0) {
Write-Host "No unattached managed disks found. Nothing to clean up." -ForegroundColor Green
return
}
$report = foreach ($disk in $orphanedDisks) {
$daysSinceCreated = if ($disk.TimeCreated) {
[math]::Round(((Get-Date) - $disk.TimeCreated).TotalDays)
}
else {
$null
}
[PSCustomObject]@{
DiskName = $disk.Name
ResourceGroup = $disk.ResourceGroupName
Region = $disk.Location
SizeGB = $disk.DiskSizeGB
Sku = $disk.Sku.Name
DaysSinceCreated = $daysSinceCreated
}
}
$report = $report | Sort-Object -Property SizeGB -Descending
Write-Host ""
Write-Host "Found $($report.Count) unattached managed disk(s):" -ForegroundColor Yellow
$report | Format-Table -AutoSize
$totalGB = ($report | Measure-Object -Property SizeGB -Sum).Sum
Write-Host ""
Write-Host "Total unattached capacity: $totalGB GB across $($report.Count) disk(s)."
Write-Host "Review each disk before deleting, some are kept intentionally as backups or snapshots pending restore."
if ($ExportPath) {
$report | Export-Csv -Path $ExportPath -NoTypeInformation
Write-Host "Report exported to $ExportPath" -ForegroundColor Green
}
Read it before you run it, and test in a safe tenant first.