Many of you might have questions like, "What are orphaned resources in Azure Cloud?" To make it clear, orphaned resources in Azure refer to unused resources in Azure Subscription(s) that are still incurring costs, even though they are not being used or utilized for business needs. By finding and deleting such orphaned Azure resources, you will help in cost-saving for the project.
Note: Before taking action on any of the Azure orphaned resources, it's important to check if any other resources are dependent on them. Always remember to rectify them before deleting. Once deleted, you cannot revert back the changes. Ensure that no other active resources depend on the identified orphaned resources before deletion.
Are you paying for unused Azure resources? Find out how to identify and delete them with this guide. We'll show you how to use the Azure portal, KQL, and Azure PowerShell to find orphaned resources and how to delete them safely.
How to Find Orphan Resources in Azure?
These are the different ways to find orphaned resources in Azure:
- Azure Portal (Straightforward Method): for manually finding the azure orphan resources.
- Azure Resource Graph KQL Query: to search for orphaned resources efficiently and export the detailed list of identified orphaned resources and communicate any planned deletions to relevant stakeholders.
- Azure PowerShell Script (Automation): for identification and deletion of orphaned resources.
In this article, I will be using the first two methods to find orphaned resources in Azure and the third method to automation using PowerShell or Azure CLI to streamline the identification and deletion of orphaned resources.
Let's dive into the topics with a step-by-step solution guide.
Find Azure Orphaned Disks from Azure Portal:
Step 1: Log into the Azure Portal >> Open Disks from the Azure global search.
Step 2: Now, add a filter and select the following, then click on Apply.
- Filter - “Disk state”
- Operator - “Equals”
- Value - “Unattached”
Reference Sample Output:
Find Azure Orphaned Disks using KQL Query:
To find orphaned disks using KQL (Kusto Query Language) in Azure Resource Graph Explorer, you can use following KQL query:
Resources | where type has 'microsoft.compute/disks' | extend diskState = tostring(properties.diskState) | where diskState == 'Unattached' or managedBy == ''
Resources | where type has 'microsoft.compute/disks' | extend diskState = tostring(properties.diskState) | where diskState == 'Unattached' or managedBy == '' | project name, diskState, managedBy, subscriptionId, resourceGroup, location, tags
The query will return a list of managed disks that are not attached to any virtual machines.
Tip: In Azure Resource Graph Explorer, you can run the KQL query by pressing "Shift+Enter" on your keyboard.
Delete Azure Orphaned Disks using PowerShell Script:
$SubscriptionName = "" Set-AzContext -Subscription $SubscriptionName $Disk = Get-AzDisk $Orphan = $Disk | Select-Object -Property Name,SubscriptionName,ResourceGroupName,Type,DiskSizeGB,DiskState $state_reserved = $Orphan | Where-Object -Property DiskState -eq “Reserved” $state_unattached = $Orphan | Where-Object -Property DiskState -eq “Unattached” $State_attached = $Orphan | Where-Object -Property DiskState -eq “attached” #Unattached State Disk Foreach ($disks in $state_unattached){ $ResourceGroup=$disks.ResourceGroupName $DiskName=$disks.Name $DiskState=$disks.DiskState Write-Host "Disk Name : $DiskName" Write-Host "ResourceGoupName: $ResourceGroup" Write-Host "State : $DiskState" Write-Host "" Write-Host "Deleting unattached Managed Disk:$DiskName" $disks | Remove-AzDisk -Force Write-Host "Managed Disk: $DiskName Deleted!" }
Find Azure Orphaned NICs from Azure Portal:
Step 1: Log into the Azure Portal >> Open Network interfaces from the Azure global search.
Step 2: Now, add a filter and select the following, then click on Apply.
- Filter - “Attached to”
- Operator - “Equals”
- Value - “-”
Reference Sample Output:
Find Azure Orphaned NICs using KQL Query:
To find Azure orphaned Network Interface Cards (NICs) using KQL (Kusto Query Language) in Azure Resource Graph Explorer, you can use the following KQL query:
Resources | where type has 'microsoft.network/networkinterfaces' | where '{nicWithPrivateEndpoints}' !has id | where properties !has 'virtualmachine'
Resources | where type has 'microsoft.network/networkinterfaces' | where '{nicWithPrivateEndpoints}' !has id | where properties !has 'virtualmachine' | project name, resourceGroup, subscriptionId, location, tags
Delete Orphaned NICs in Azure using PowerShell Script:
$SubscriptionName = "_add_subscription_name" Set-AzContext -Subscription $SubscriptionName $NICs = Get-AzNetworkInterface $Orphan = $Nics | Select-Object Name, ResourceGroupName, VirtualMachine | Where-Object { $_.VirtualMachine -eq $null } Foreach ($nic in $Orphan){ $VMName=$nic.virtualmachine $NICName=$nic.Name $ResourceGroup=$nic.ResourceGroupName Write-Host "NIC Name : $NICName" Write-Host "Resource Group : $ResourceGroup" Write-Host "Deleting NIC: $NICName" $nic | Remove-AzNetworkInterface -Force Write-Host "NIC: $NICName Deleted Successfully" Write-Host "" }
Find Orphaned NSGs from Azure Portal:
Step 1: Log into the Azure Portal >> Open Network security groups from the Azure global search.
- Filter - “Associated with”
- Operator - “Equals”
- Value - “0 subnet, 0 network interface”
Reference Sample Output:
Find Azure Orphan NSGs using KQL Query:
Use the following KQL (Kusto Query Language) resource graph query to find the orphaned nsgs in Azure:
Resources | where type =~ 'microsoft.network/networksecuritygroups' and isnull(properties.networkInterfaces) and isnull(properties.subnets)
Resources | where type =~ 'microsoft.network/networksecuritygroups' and isnull(properties.networkInterfaces) and isnull(properties.subnets) | project name, resourceGroup, subscriptionId, location, tags
Delete Azure Orphaned NSGs using PowerShell:
$SubscriptionName = "_add_subscription_name" Set-AzContext -Subscription $SubscriptionName $NSGs = Get-AzNetworkSecurityGroup $Orphan = $NSGs | Select-Object name,resourcegroupname | Where-Object { $_.NetworkInterfaces.count -eq 0 } Foreach ($nsg in $Orphan){ $ResourceGroup=$nsg.resourcegroupname $NSGName=$nsg.name Write-Host "NSG Name : $NSGName" Write-Host "ResourceGoupName: $ResourceGroup" Write-Host "Deleting NSG: $NSGName " $nsg | Remove-AzDisk -Force Write-Host "NSG: $NSGName Successfully Deleted" }
Find Orphaned Public IP Addresses in Azure Portal:
Step 1: Login into Azure Portal >> Open Public IP addresses from azure global search.
Step 2: Now Add a filter and select the following and then click on Apply.
- Filter - “Associated to”
- Operator - “Equals”
- Value - “-"
Reference Sample Output:
Find Azure Orphan Public IP Addresses using KQL Query:
To find orphaned Public IP Addresses(PIPs) using KQL (Kusto Query Language) in Azure Resource Graph Explorer, you can use the following KQL query:
resources | where type =~ 'microsoft.network/publicipaddresses' | extend IpConfig = properties.ipConfiguration.id | where isempty(IpConfig)
Find Orphaned Azure Availability Sets using KQL Query:
To find orphaned Azure Availability Sets using KQL (Kusto Query Language) in Azure Resource Graph Explorer, you can use the following KQL query:
resources | where type =~ 'microsoft.compute/availabilitysets' | extend VirtualMachines = array_length(properties.virtualMachines) | where VirtualMachines == 0
Find Orphaned Resource Group in Azure using KQL Query:
ResourceContainers | where type == 'microsoft.resources/subscriptions/resourcegroups' | extend rgAndSub = strcat(resourceGroup, '--', subscriptionId) | join kind=leftouter ( Resources | extend rgAndSub = strcat(resourceGroup, '--', subscriptionId) | summarize count() by rgAndSub ) on rgAndSub | where isnull(count_) | extend SubscriptionName=case( subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Connectivity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Identity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Management', subscriptionId) | project ResourceGroup=resourceGroup, SubscriptionName, Location=location, Tags=tostring(tags)
Find Orphaned Azure App Service Plans using KQL Query:
resources | where type =~ "microsoft.web/serverfarms" | where properties.numberOfSites == 0 | extend Details = pack_all() | extend SubscriptionName=case( subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Connectivity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Identity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Management', subscriptionId) | project ResourceName=name, ResourceGroupName=resourceGroup, Location=location, SubscriptionName, Tags=tostring(tags)
Find Orphaned Azure Load Balancers using KQL Query:
resources | where type == 'microsoft.network/loadbalancers' | where properties.backendAddressPools == "[]" | extend Details = pack_all() | extend SubscriptionName=case( subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Connectivity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Identity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Management', subscriptionId) | project ResourceName=name, ResourceGroupName=resourceGroup, Location=location, SubscriptionName, Tags=tostring(tags)
Find Orphaned Azure Route Tables using KQL Query:
resources | where type == 'microsoft.network/routetables' | where isnull(properties.subnets) | extend Details = pack_all() | extend SubscriptionName=case( subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Connectivity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Identity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Management', subscriptionId) | project ResourceName=name, ResourceGroupName=resourceGroup, Location=location, SubscriptionName, Tags=tostring(tags)
Find Orphaned Azure Traffic Manager Profiles using KQL Query:
resources | where type == "microsoft.network/trafficmanagerprofiles" | where properties.endpoints == "[]" | extend Details = pack_all() | extend SubscriptionName=case( subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Connectivity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Identity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Management', subscriptionId) | project ResourceName=name, ResourceGroupName=resourceGroup, Location=location, SubscriptionName, Tags=tostring(tags)
Find Orphaned Front Door WAF Policy using KQL Query:
resources | where type == "microsoft.network/frontdoorwebapplicationfirewallpolicies" | where properties.frontendEndpointLinks== "[]" and properties.securityPolicyLinks == "[]" | extend Details = pack_all() | extend SubscriptionName=case( subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Connectivity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Identity', subscriptionId =~ 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx','Management', subscriptionId) | project ResourceName=name, ResourceGroupName=resourceGroup, Location=location, SubscriptionName, Sku=sku.name, Tags=tostring(tags)