
If you are new to Azure, you may have questions like, "What are orphaned resources in Azure Cloud?"
To clarify, orphaned resources in Azure refer to unused resources within an Azure subscription that continue to incur unnecessary costs and may lead to operational inefficiencies and potential misconfigurations, even though they are no longer actively used or contributing to business needs.
Regularly identifying and deleting the orphaned resources can significantly reduce project costs, prevent configuration issues, and simplify ongoing operations.
Examples of Azure Orphaned Resources:
- Orphaned Managed Disks
- Orphaned Public IPs
- Orphaned NICs (Network Interface Cards)
- Orphaned Network Security Groups (NSGs)
- Orphaned Route Tables
- Orphaned Resource Groups
- Orphaned Azure Availability Sets
- Orphaned Azure App Service Plans
- Orphaned Azure Load Balancers
- Orphaned Azure Traffic Manager Profiles
- Orphaned Front Door WAF Policies
Are you paying for unused Azure resources?
Learn how to find and delete orphaned Azure resources to save money. This guide covers the steps for identifying and deleting unused resources using the Azure portal, KQL, and Azure PowerShell.
How to Find Orphaned Resources in Azure?
Cleaning up Azure orphaned resources is one of the key cost optimization activities. To help identify and manage these resources, Azure provides various tools and workbooks. One of the simplest methods is using Azure Resource Graph queries for monitoring the resources.
Below are different ways to find orphaned resources in Azure:
- Azure Portal (Straightforward Method): For manually identifying orphaned Azure resources.
- Azure Resource Graph KQL Query: To efficiently search for orphaned resources, export a detailed list of the identified resources, and communicate any planned deletions to relevant stakeholders.
- Azure PowerShell Script (Automation): To automate the identification and deletion of orphaned resources.
In this article, I have provided quick solutions using the first two methods to find orphaned resources in Azure, and a third method for 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.
Note: Before taking action on any Azure orphaned resources, it's important to check whether any other resources depend on them. Always resolve any dependencies before proceeding with deletion. Once deleted, the changes cannot be undone. Ensure that no active resources rely on the identified orphaned resources before deleting them.
Find Azure Orphaned Disks in 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 in 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 in 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)