Azure subscriptions get messy fast.
A VM is deleted, but its disk stays. Nobody knows who owns a resource group. A former employee still shows up in the access list as "Identity not found".
None of this breaks anything today. But it costs money, and it makes audits painful.
So I cleaned up the checks I use and shared them on GitHub. This toolkit uses PowerShell 7 and the Az modules.
Repo: azure-governance-powershell-toolkit
The problem it solves
Resources have no tags, so nobody knows who to ask.
Unattached disks and idle public IPs add up on the bill.
Role assignments pile up. Some point to accounts that no longer exist.
Policy shows red, but nobody knows where to start.
These scripts make those checks quick and repeatable.
Highlight scripts
Get-AzMissingTagReport.ps1
Checks every resource group for your required tags. Add -IncludeResources to check every resource too. Then fix them in bulk with Set-AzTagFromCsv.ps1.
./Get-AzMissingTagReport.ps1 -RequiredTag Owner, CostCenter, Environment
Get-AzOrphanedResourceReport.ps1
Lists unattached disks, old snapshots, unused NICs, unused public IPs and empty resource groups. Anything tagged Keep = true is marked so you can skip it.
./Get-AzOrphanedResourceReport.ps1 -SnapshotAgeDays 30
Remove-AzUnattachedDisk.ps1
Deletes old unattached disks. With -SnapshotFirst it takes a cheap snapshot first, just in case. It asks before every disk.
./Remove-AzUnattachedDisk.ps1 -MinAgeDays 60 -SnapshotFirst -WhatIf
Get-AzRoleAssignmentReport.ps1
Lists every role assignment and flags privileged roles, direct user assignments, guests and orphaned entries.
./Get-AzRoleAssignmentReport.ps1 -PrivilegedOnly
Other scripts cover a full resource inventory, removing orphaned role assignments, Azure Policy compliance and Azure Advisor recommendations.
How the scripts are built
- PowerShell 7.4 or later.
- Comment-based help on every script. Run
Get-Help ./script.ps1 -Full. - Reports export to CSV.
- Change scripts support
-WhatIfand-Confirm. - Certificate (app-only) or interactive sign-in for Azure.
- No secrets in the repo. Examples use
contoso.comand placeholder GUIDs.
Please test in a test subscription first. Read each script before you run it.
My usual workflow
Once a month, I run the inventory, the missing tag report and the leftover report.
I send the leftover list to the owners and wait a week. Then I clean up what nobody claims.
Every quarter, I run the access review and keep the CSV for audits.
One tip. Start Azure Policy in Audit mode. Switch to Deny only when the report is clean.
Getting started
Clone the repo:
git clone https://github.com/anthonybuhnerkemper/azure-governance-powershell-toolkit.git
cd azure-governance-powershell-toolkit
Every toolkit ships the same Connect-M365Admin script. Dot-source it, then connect.
# Interactive sign-in
. ./scripts/Connect/Connect-M365Admin.ps1
Connect-M365Admin -Service Azure -Interactive -SubscriptionId 00000000-0000-0000-0000-000000000000
# Or app-only with a certificate
$env:M365_TENANT_ID = '00000000-0000-0000-0000-000000000000'
$env:M365_CLIENT_ID = '00000000-0000-0000-0000-000000000000'
$env:M365_CERT_THUMBPRINT = '0000000000000000000000000000000000000000'
$env:AZ_SUBSCRIPTION_ID = '00000000-0000-0000-0000-000000000000'
Connect-M365Admin -Service Azure
Run a report, check the CSV, then call Disconnect-M365Admin when you are done. The README lists the modules and roles each script needs.
More toolkits
I have more free toolkits that use the same pattern, including azure-backup-dr-powershell-toolkit, entra-id-powershell-toolkit and graph-powershell7-admin-toolkit.
You can find them all on my GitHub profile.
Feedback welcome
Issues, ideas and pull requests are welcome. What would you add? Tell me in the comments.
Top comments (0)