Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jasond2014/9f8a6f0f8b045e5a2891915adab9c69a to your computer and use it in GitHub Desktop.
Save jasond2014/9f8a6f0f8b045e5a2891915adab9c69a to your computer and use it in GitHub Desktop.
Generate a report of all Azure and AWS disks (volumes) that are not attached to a virtual machine. Output to stdout or to CSV file.
#Requires -Version 6.0
#Requires -Modules Az.Accounts, Az.ResourceGraph
<#
.SYNOPSIS
Generate a report of Azure orphaned volumes
.DESCRIPTION
Generate a report of Azure Disks which are not attached to any virtual machine
.EXAMPLE
Process all Azure Subscriptions and write results to stdout
.\Get-AzOrphanedVolumes.ps1
.EXAMPLE
Process a single Azure Subscription and write results to stdout
.\Get-AzOrphanedVolumes.ps1 -Subscription 'mysubscription1'
.EXAMPLE
Process multiple Azure Subscriptions and write results to CSV file in the current user's profile directory
.\Get-AzOrphanedVolumes.ps1 -Subscription 'mysubscription1', 'mysubscription2' -GenerateReport
.EXAMPLE
Process all Azure Subscriptions and write results to CSV file in user-defined location
.\Get-AzOrphanedVolumes.ps1 -GenerateReport -OutFile 'C:\Users\myuser\Desktop\AzOrphanedVols.csv'
#>
#region Init
param(
# String array of Azure Subscription names to process
[Parameter()]
[string[]] $Subscription,
# Whether to output the results to a CSV file
[Parameter()]
[switch] $GenerateReport,
# User-defined location for output CSV file
[Parameter()]
[string] $OutFile
)
# Report location
if ($GenerateReport -and -not $OutFile) {
if ($IsWindows) {
$ReportFile = "$env:USERPROFILE\AzureOrphanedVolumes_$(Get-Date -Format 'yyyy-MM-dd').csv"
} else {
$ReportFile = "$env:HOME/AzureOrphanedVolumes_$(Get-Date -Format 'yyyy-MM-dd').csv"
}
} else {
$ReportFile = $OutFile
}
#endregion Init
#region ProcessAzure
try {
# Login to Azure and select correct Subscription(s)
#Connect-AzAccount
if (-not $Subscription) {
$Subscriptions = Get-AzSubscription | Select-Object -ExpandProperty Id
} else {
$Subscriptions = $Subscription | ForEach-Object {
Get-AzSubscription -SubscriptionName $_ | Select-Object -ExpandProperty Id
}
}
} catch {
Write-Error "Error connecting to Azure: $_" -ErrorAction Stop
}
try {
$Query = @'
resources
| where type =~ 'microsoft.compute/disks'
| where (coalesce(split(managedBy, '/')[(-1)], '-')) =~ '-'
'@
# Execute Azure Resource Graph query to find orphaned disks in selected Subscriptions
$AzOrphanedVols = Search-AzGraph -Subscription $Subscriptions -Query $Query -First 1000 | Select-Object -ExpandProperty Data
} catch {
Write-Error "Error executing Azure Resource Graph query: $_"
}
$AzOrphanedVolObjects = foreach ($OrphanedVol in $AzOrphanedVols) {
# Construct Azure output objects
[PSCustomObject]@{
Subscription = (Get-AzSubscription -SubscriptionId $OrphanedVol.subscriptionId).Name
Location = $OrphanedVol.location
Id = $OrphanedVol.id
Name = $OrphanedVol.name
SizeGB = $OrphanedVol.properties.diskSizeGB
CreationDate = $OrphanedVol.properties.timeCreated
}
}
#endregion ProcessAzure
#region Output
if ($GenerateReport) {
try {
# Export PowerShell object arrays to CSV file
$AzOrphanedVolObjects | Export-Csv -Path $ReportFile -NoTypeInformation
} catch {
Write-Error "Error exporting orphaned volumes object array to CSV file [$ReportFile]: $_"
}
} else {
$AzOrphanedVolObjects
}
#endregion Output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment