Automate Azure Cleanup — Before It Costs Me
Most of us don’t realize how fast an Azure bill can grow — until we get that painful monthly invoice.
The surprise? It’s rarely because of production workloads.
It’s the small, forgotten things:
- Test VMs that should’ve been deleted
- Staging App Services left running
- Orphaned managed disks
- Zombie NICs that no one even remembers creating
These idle resources quietly drain money.
Solution: Weekly Automated Cleanup
Instead of manually checking everything, I built a simple system that does it all for me — safely and automatically.
Here’s what my setup includes:
- A PowerShell script that scans for unused resources
- An Azure Logic App that sends me an approval request in Microsoft Teams
- After approval, it deletes the resources
- Every action is logged to Azure Storage and Log Analytics
Step-by-Step: How It Works
Step 1: Detect Unused Resources with PowerShell
# Find Unused Disks
$unusedDisks = Get-AzDisk | Where-Object { -not $_.ManagedBy }
# Find Zombie NICs
$zombieNICs = Get-AzNetworkInterface | Where-Object {
-not $_.VirtualMachine -and $_.ProvisioningState -eq 'Succeeded'
}
# Save the list to JSON
$resources = [PSCustomObject]@{
UnusedDisks = $unusedDisks.Id
ZombieNICs = $zombieNICs.Id
}
$resources | ConvertTo-Json -Depth 5 | Out-File "cleanupResources.json"
This script runs weekly on a schedule and saves the list of cleanup candidates.
Step 2: Trigger a Logic App for Approval
I set up a Logic App that runs whenever the JSON file is uploaded to Blob Storage, or on a timer (e.g. every Sunday).
It reads the list and sends a message to Microsoft Teams using an Adaptive Card.
The card gives me the option to Approve or Reject the cleanup.
Step 3: Delete Resources After Approval
If I approve the cleanup, the Logic App triggers an Azure Automation Runbook, which runs this PowerShell:
param (
[string[]]$DiskIds,
[string[]]$NICIds
)
foreach ($disk in $DiskIds) {
Remove-AzDisk -ResourceId $disk -Force
Write-Output "Deleted Disk: $disk"
}
foreach ($nic in $NICIds) {
Remove-AzNetworkInterface -ResourceId $nic -Force
Write-Output "Deleted NIC: $nic"
}
I’ve granted the automation account Managed Identity access so it can safely delete resources with the right permissions.
Step 4: Log Everything
All deletion actions are logged to:
Azure Log Analytics Workspace for easy querying and alerts
Optionally to Azure Storage Table or a Blob for historical auditing
Write-Output "Deleted: $resourceId at $(Get-Date)"
Best Practice: Use Tags to Protect Critical Resources
- To avoid accidental deletes, I check for tags before including resources in the deletion list.
Example:
- Only delete if AutoDelete = True
- Skip anything tagged with Environment = Production
- You can add this logic inside the detection PowerShell script.
Why This Works
- This automation is simple but powerful.
- Avoid unexpected cost
- Keep my Azure environment clean
- Maintain an approval flow for transparency
Final Take:
- Cleanup isn’t just a FinOps task — it’s hygiene.
- The longer you leave unused resources running, the more they cost you in silence.
So I automated it. And I recommend you do the same.
Need help setting it up? Reach out to me — I’m happy to share the full setup.
– Kasi @ KasdevTech or LinkedIn