Remove empty Azure resource groups using Powershell
Tags: azure, powershell, azureoptimization, azureadministration
Published: 2021-01-07 14:01:11

Each time you create a new resource in Azure, you have to place it in a logical container called as "Resource Group". When the resources are deleted it leaves behind an empty resource group.

As your subscription grows in size, empty resource groups can also increase in number. To keep things easy to manage, it is a good practice to delete these empty groups. We will see how to create a custom Powershell script to find and delete the empty resource groups. 

**Prerequisites:**
- [Azure Powershell](https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-5.3.0) (For managing Azure resources from Powershell)
- [Service Principal](https://docs.microsoft.com/en-us/powershell/azure/create-azure-service-principal-azureps?view=azps-5.3.0) (To authenticate with Azure)

### **Authenticating with Azure**

The `$ErrorActionPreference = "Stop"` below tells the script to stop execution if it encounters an error while executing. You can change this behavior to suit your needs. Check the [about_Preference_Variables link](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.1) to learn more on this.

```powershell
$ErrorActionPreference = "Stop"
$tenantId = "<YOUR_TENANT_ID>"

# Enter your service principal client ID & secret here.
# You will get the client ID and client secret after creating the service principal.
$clientId = "<YOUR_CLIENT_ID>"
$clientSecret = "<YOUR_CLIENT_SECRET>"
$secClientSecret = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential ($clientId, $secClientSecret)

# Authenticate using the service principal
Connect-AzAccount -ServicePrincipal -Credential $credentials -Tenant $tenantId -WarningAction SilentlyContinue | Out-Null
```

Once authentication is successful, we will fetch the list of subscriptions

```powershell
# Get the list of subscriptions
$subscriptionIds = (Get-AzSubscription).Id
```

### **Loop through the subscriptions and perform the cleanup**

The below code snippet loops through the subscription and finds the empty resource groups. Then it asks for confirmation before performing the delete operation.

```powershell
# Loop through the list of subscriptions and find empty resource groups
foreach ($subscription in $subscriptionIds) {
    Set-AzContext -SubscriptionId $subscription | Out-Null

    Write-Host "`n--------- Working on subscription $subscription ----------`n"

    $emptyRgs = New-Object System.Collections.ArrayList

    $rgs = Get-AzResourceGroup

    Write-Host "Empty resource groups:- "

    foreach ($rg in $rgs) {
        $resourceCount = (Get-AzResource -ResourceGroupName $rg.ResourceGroupName).Count

        if ($resourceCount -eq 0) {            
            Write-Host $rg.ResourceGroupName
            $emptyRgs.Add($rg) | Out-Null
        }
    }

    if ($emptyRgs.Count -eq 0) {
        Write-Host "No empty resource groups found in this subscription" -ForegroundColor Red
    }
    else {
        # Ask permission to delete the empty resource groups
        $msg = "`nDo you want to delete the above resource groups? [y/n]"

        $choice = [string]::empty

        while ($choice -notmatch "[y|Y|n|N]") {
            $choice = Read-Host -Prompt $msg
        }

        if ($choice -match "[y|Y]") {
            $emptyRgs | Remove-AzResourceGroup -Force -AsJob | Out-Null
            Write-Host "`n", $emptyRgs.Count, "resource groups will be deleted" -ForegroundColor Green
        }
        else {
            Write-Host "`nNo resource groups were deleted" -ForegroundColor Red
        }
    }
    Write-Host "`n---- Finished working on subscription $subscription -----`n"
}
```

Notice that we have used `-AsJob` attribute for `Remove-AzResourceGroup` cmdlet here which performs the delete operation in the background. This is particularly useful when there are a lot of empty resource groups and we don't want to wait for each delete operation to complete.

`Out-Null` is used for various Powershell cmdlets. It suppresses the cmdlet output and helps keep the script output clean and just present the relevant information to the user executing the script.

Below is a sample output for the above script:

![ScriptOutput](https://dev-to-uploads.s3.amazonaws.com/i/a3cs12z6r2d4f824uxvh.png)

You can find the full Powershell script on Github link below:

{% github srs2210/azure-cleanup-empty-rg no-readme %}

Hope you find this post useful 😄