π Enterprise-grade Azure resource management with advanced discovery, batch operations, and comprehensive safety features
This comprehensive Bash script automates the discovery and safe deletion of Cortex Cloud Azure onboarding resources. It operates across all scopesβSubscription, Management Group, and Tenantβand identifies resources using name patterns and tags. The script includes advanced exclusion options and audit logging capabilities to ensure precise and secure resource management, saving significant time and manual effort.
- π Multi-Scope Discovery: Resources across Subscriptions, Management Groups, and Tenant
- π·οΈ Enhanced Tag Search: Flexible tag-based discovery in three modes
- π Comprehensive Discovery: Searches across all Azure scopes for resources matching name patterns
- π‘οΈ Safety First: Dry-run mode by default with explicit confirmation prompts
- π‘οΈ Smart Exclusion: Protect critical resources from accidental deletion
- ποΈ Safe Deletion: Dependency-aware deletion order to prevent conflicts
- π― Multi-Resource Support: Handles 15+ Azure resource types
- β‘ Edge Case Handling: Manages 'Unknown' role assignments, scope mismatches, and orphaned resources
- β‘ Performance Optimizations: Faster discovery across large environments
- π Audit Logging: Comprehensive logging for compliance and troubleshooting
| Resource Type | Discovery | Deletion |
|---|---|---|
| Resources & Resource Groups | β | β |
| Custom Roles & Role Assignments | β | β |
| Policy Assignments & Definitions | β | β |
| Policy Remediations | β | β |
| Enterprise Applications | β | β |
| Service Principals | β | β |
| Managed Identities | β | β |
| Diagnostic Settings (All levels) | β | β |
| Management Group Deployments | β | β |
| Management Group Role Assignments | β | β |
- Bash: Version 4.0 or higher (5.0+ recommended)
- Azure Cloud Shell-Bash or (Azure CLI: Version 2.0 or higher)
- jq: JSON processor
- column: Table formatting utility (usually pre-installed)
sudo apt-get update
sudo apt-get install -y jq bashbrew install jq
# Bash 5+ comes with modern macOSsudo yum install -y jq
# Or for newer versions:
sudo dnf install -y jq- Owner role is required on the Root Level
- Application Administrator or Global Administrator (for Service Principals & Enterprise Apps)
- Download the script:
curl -fsslO https://raw.githubusercontent.com/naveednawazkhan/Cortex-Cloud/refs/heads/main/azure-cleanup-tool.sh && chmod +x azure-cleanup-tool.sh- Verify prerequisites:
# Check Bash version (If your Bash version is old, scroll down to "Troubleshooting" for installation instructions.)
bash --version
# Should show: GNU bash, version 4.x or 5.x
# Check Azure CLI (Skip this if using Azure Cloud Shell-Bash)
az version
# Should show Azure CLI version 2.x+
# Check jq
jq --version
# Should show: jq-1.6 or similar
# Check column utility on MAC (different methods for different systems)
echo "test1 test2" | column -t 2>/dev/null && echo "β
column is working" || echo "β column not functioning"
# Verify Azure login
az account show- Login to Azure:
az login
# If using specific tenant:
az login --tenant <your-tenant-id># Single pattern search (dry-run by default)
bash azure-cleanup-tool.sh "cortex"
# Single pattern search with --dry-run
bash azure-cleanup-tool.sh "cortex" --dry-run
# Search in specific subscription
bash azure-cleanup-tool.sh "cortex" --subscription 12345-67890 --dry-run
# Actual deletion (use with caution!)
bash azure-cleanup-tool.sh "cortex" --delete# Multi pattern search (dry-run by default)
bash azure-cleanup-tool.sh "cortex,ads"
# Multi pattern search with --dry-run
bash azure-cleanup-tool.sh "cortex,ads" --dry-run
# Search in specific subscription
bash azure-cleanup-tool.sh "cortex,ads" --subscription 12345-67890 --dry-run
# Actual deletion (use with caution!)
bash azure-cleanup-tool.sh "cortex,ads" --delete# Search by tag key only
bash azure-cleanup-tool.sh --tag "managed_by" --dry-run
# Search by exact key-value pair
bash azure-cleanup-tool.sh --tag "managed_by=paloaltonetworks" --dry-run
# Search by tag value only
bash azure-cleanup-tool.sh --tag "paloaltonetworks" --dry-run
# Delete tagged resources
bash azure-cleanup-tool.sh --tag "paloaltonetworks" --delete# Exclude specific resource
bash azure-cleanup-tool.sh "cortex" --dry-run --exclude cortex-scan-platform
# Multiple exclusions with batch operations
bash azure-cleanup-tool.sh "cortex,ads" --dry-run --exclude cortex-scan-platform,production
# Combine all features
bash azure-cleanup-tool.sh -tag "paloaltonetworks" --delete --exclude cortex-scan-platform,production,backup# Create detailed audit log with all execution details by default it's Overwrite mode (new log each time in file)
bash azure-cleanup-tool.sh "cortex,ads" --dry-run --log-file "audit.log"
bash azure-cleanup-tool.sh "cortex,ads" --dry-run --log-file "audit-$(date +%Y%m%d-%H%M%S).log"
# Append mode (Append to existing log file instead of overwriting)
bash azure-cleanup-tool.sh "cortex,ads" --delete --log-file "audit.log" --append-log
# Show help message
bash azure-cleanup-tool.sh --help| Option | Description | Default | Required With | Example |
|---|---|---|---|---|
<resource-name> |
Search pattern (case-insensitive). Use commas for multiple patterns (e.g., cortex,ads,test) |
- | Use either this OR --tag |
"cortex,ads,monitor" |
--tag KEY[=VALUE] |
Search by tag in three ways: β’ KEY - matches tag keyβ’ KEY=VALUE - matches exact key-value pairβ’ VALUE - matches tag value |
- | Use either this OR <resource-name> |
--tag "paloaltonetworks" |
--dry-run |
Only show what would be deleted (no actual deletion) | Enabled | Optional | --dry-run |
--delete |
Actually delete resources (requires confirmation) | Dry-run mode | Optional | --delete |
--subscription |
Limit search to specific subscription ID | All enabled subscriptions | Optional | --subscription "12345-67890" |
--exclude |
Comma-separated patterns/names to exclude from deletion (e.g., production,backup,monitor) |
- | Optional | --exclude "prod,backup" |
--log-file FILE |
Write audit log to file | - | Optional | --log-file audit.log |
--append-log |
continuous log for multiple executions | - | Optional | --append-log |
--help |
Show help message | - | Optional | --help |
- Subscription Enumeration: Discovers all accessible subscriptions
- Multi-Scope Search: Searches resources at Resource, Subscription, Management Group, and Tenant levels
- Pattern Matching: Case-insensitive search across all resource types
- Dependency Mapping: Identifies relationships between resources
The script deletes resources in dependency order to prevent failures:
- π― Management Group Deployments
- π§ Policy Remediations
- π Policy Assignments
- π₯ Role Assignments
- π·οΈ Custom Roles
- π Diagnostic Settings
- π Enterprise Applications
- βοΈ Service Principals
- ποΈ Regular Resources
- π¦ Resource Groups (last)
- Dry-run by default: No accidental deletions
- Explicit confirmation: Required for destructive operations
- Exclusion Patterns: Protect critical resources from accidental deletion
- Color-coded output: Easy to understand status
- Formatted tables: Clear resource summaries
- Error handling: Comprehensive error messages with guidance
- Retry logic: Automatic retries for transient failures
π Azure Resource Cleanup Tool
β
Azure login confirmed
βΉοΈ Mode: DRY-RUN
βΉοΈ Searching for resources matching ANY of these patterns:
βΉοΈ β’ cortex
βΉοΈ β’ ads
--------------------------------------------------------
β Found Resource: cortex-storage (Microsoft.Storage/storageAccounts)
β Found Resource: ads-processor (Microsoft.Web/sites)
β Found Resource Group: cortex-dev-rg
β Found Custom Role: cortex-operator (a1b2c3d4-e5f6-7890-abcd-ef1234567890)
βΉοΈ Applying exclude patterns: Cortex-Cloud-SSO
βΉοΈ Excluded 1 resource(s) from deletion
β οΈ Excluded 1 resource(s) matching patterns: Cortex-Cloud-SSO
=========================================================
Summary Table
=========================================================
β
Found 4 matching resource(s) for deletion
β οΈ Excluded 1 resource(s)
----------------------- ---------------------------- ------------------------- ----------------------------------------
NAME TYPE SCOPE DETAILS
----------------------- ---------------------------- ------------------------- ----------------------------------------
cortex-storage Microsoft.Storage/storageAccounts Subscription A tags: env=test
ads-processor Microsoft.Web/sites Subscription B
cortex-dev-rg ResourceGroup Subscription A
cortex-operator CustomRole Tenant Scopes: 1
βΉοΈ Dry-run completed. No resources were deleted.
βΉοΈ Use --delete to actually delete these resources.Output Example:
==================================================================================
AZURE RESOURCE CLEANUP AUDIT LOG
==================================================================================
Execution Start : 2024-01-15 14:30:25 UTC
User : naveed@hostname
Azure User : naveed.khan@company.com
Tenant ID : 12345678-1234-1234-1234-123456789012
Subscription : All enabled subscriptions
Mode : DRY-RUN
Log Mode : OVERWRITE
Search Type : Tag Filter
Patterns : paloaltonetworks
Exclude Patterns : Cortex-Cloud-SSO,cortex-scan-platform-1001222230132-prod-us
Log File : audit.log
==================================================================================
==================================================================================
[2025-12-08 14:27:23] AUDIT: Audit logging enabled: debug1.log (APPEND mode)
[2025-12-08 14:27:23] INFO: Logging initialized
[2025-12-08 14:27:23] INFO: Mode: DELETE
[2025-12-08 14:27:23] INFO: Log Mode: APPEND
[2025-12-08 14:27:23] INFO: Searching for resources with tag: paloaltonetworks
[2025-12-08 14:27:23] INFO: Exclude patterns/resources:
[2025-12-08 14:27:23] INFO: β’ Cortex-Cloud-SSO
[2025-12-08 14:27:23] INFO: β’ cortex-scan-platform-1001222230132-prod-us
[2025-12-08 14:27:23] INFO: --------------------------------------------------------
[2025-12-08 14:27:23] INFO: Current subscription ID: 12345678-1234-1234-1234-123456789012
[2025-12-08 14:27:23] INFO: Getting all enabled subscriptions...
[2025-12-08 14:27:24] INFO: Searching for resources with tag key or value: paloaltonetworks
[2025-12-08 14:27:24] INFO: Getting all enabled subscriptions...
[2025-12-08 14:27:25] INFO: Searching tagged resources in subscription:
[2025-12-08 14:27:25] DEBUG: Searching for tagged resource groups in subscription:
[2025-12-08 14:27:26] INFO: Searching tagged resources in subscription: Azure subscription 1
[2025-12-08 14:27:27] DEBUG: Searching for tagged resource groups in subscription: Azure subscription 1
[2025-12-08 14:27:28] INFO: Searching tagged resources in subscription: Subscription 2
[2025-12-08 14:27:29] DEBUG: Searching for tagged resource groups in subscription: Subscription 2
[2025-12-08 14:27:30] INFO: Searching tagged resources in subscription: Azure subscription 1
[2025-12-08 14:27:32] DEBUG: Searching for tagged resource groups in subscription: Azure subscription 1
[2025-12-08 14:27:33] INFO: Searching tagged resources in subscription: Subscription 2
[2025-12-08 14:27:33] DEBUG: Searching for tagged resource groups in subscription: Subscription 2
[2025-12-08 14:27:35] INFO: Switched back to original subscription: 7144b1a5-f22f-4e30-a29a-93727748d60e
[2025-12-08 14:27:35] DEBUG: Skipping management group role assignments discovery in pure tag mode
[2025-12-08 14:27:35] DEBUG: Skipping management group deployments discovery in pure tag mode
[2025-12-08 14:27:35] DEBUG: Skipping policy assignments discovery in pure tag mode
[2025-12-08 14:27:35] DEBUG: Skipping policy remediations discovery in pure tag mode
[2025-12-08 14:27:35] DEBUG: Skipping diagnostic settings discovery in pure tag mode
[2025-12-08 14:27:35] DEBUG: Skipping directory diagnostic settings discovery in pure tag mode
[2025-12-08 14:27:35] DEBUG: Skipping custom roles discovery in tag mode
[2025-12-08 14:27:35] DEBUG: Skipping role assignments discovery in tag mode
[2025-12-08 14:27:35] DEBUG: Skipping service principals discovery in tag mode
[2025-12-08 14:27:35] INFO: Discovery phase completed
[2025-12-08 14:27:35] INFO: Applying exclude patterns: Cortex-Cloud-SSO,cortex-scan-platform-1001222230132-prod-us
[2025-12-08 14:27:35] INFO: No resources matched exclude patterns
[2025-12-08 14:27:35] SUCCESS: No matching resources found
==================================================================================
EXECUTION SUMMARY
==================================================================================
Start Time : 2025-12-08 14:27:22 CST
End Time : 2025-12-08 14:27:35 CST
Mode : DELETE
Log Mode : APPEND
Resources Found : 0
Resources Deleted : 0
Resources Excluded : 0
Resource Groups Skipped : 0 (contained excluded resources)
Resources Failed : 0
==================================================================================
==================================================================================
END OF EXECUTION
==================================================================================
"Either name pattern or tag filter is required"
# β Wrong - missing search criteria
bash azure-cleanup-tool.sh --dry-run
# β
Correct - provide search criteria
bash azure-cleanup-tool.sh "cortex" --dry-run
bash azure-cleanup-tool.sh --tag "environment=dev" --dry-run"Subscription not found"
# Verify subscription access
az account list --output table
az account set --subscription "Your-Subscription-Name""Insufficient permissions"
# Check current permissions
az role assignment list --assignee $(az account show --query user.name -o tsv)Missing jq:
# Ubuntu/Debian
sudo apt-get install jq
# macOS
brew install jq
# Windows (WSL)
choco install jqMissing column:
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y bsdmainutils
# macOS
# column should be pre-installed, but if missing:
brew install util-linux
# RHEL/CentOS
sudo yum install -y util-linux-ngBash Version Too Old:
# macOS
brew install bash
echo '/usr/local/bin/bash' >> /etc/shellsFor detailed debugging, run with:
bash -x azure-cleanup-tool.sh "<resource-name>" --dry-runWe welcome contributions! Please feel free to submit issues, feature requests, or pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.
This tool performs destructive operations. Always:
- Run with
--dry-runfirst - Review the discovered resources
- Ensure you have appropriate backups
- Test in non-production environments first
The authors are not responsible for any data loss or unintended deletions.
Happy Cleaning! π§Ή