Azure Health Check HTML Report for governance, compute, storage, network, Key Vault, Activity Log, SQL and Azure Policy.
This PowerShell script generates a full HTML-based health report across every Azure subscription available in your current context.
It analyses common governance gaps, security misconfigurations, compute/storage/network risks, Key Vault configuration, upcoming expirations, and more.
- Detects Resource Groups without management locks
- Highlights potential accidental-deletion risks
- Lists all VMs not protected by Azure Backup
- Cross-checks across all Recovery Services Vaults
- VMs with legacy disks (HDD / unmanaged)
- VMs without backup
- VMs with high CPU — flags VMs whose P95 CPU utilisation (hourly average over 7 days) exceeds the threshold (default 80%)
- Unattached disks
- Unattached Public IPs
- Stopped VMs — VMs in OS-stopped state (not deallocated). These still incur compute charges.
- TLS version issues (TLS 1.0 / 1.1)
- Blob public access enabled
- Soft delete status (Blob / File)
- Replication tier (GRS, ZRS, etc.)
- Public network access — Storage Accounts with no network firewall (DefaultAction: Allow)
- Subnets without NSG
- NICs without NSG
- Inbound NSG rules exposing RDP/SSH to the Internet
- Vaults without purge protection
- Secrets, certificates and keys expiring within 90 days
- Checks whether subscription-level Activity Log diagnostics are configured
- Supports any destination: Log Analytics, Storage Account, Event Hub, or Partner
- Inventory of all SQL instances across subscriptions:
- Azure SQL logical servers
- Azure SQL Managed Instances
- SQL Server on Azure VMs (SQL IaaS Agent)
- Inventory of all Policy assignments at subscription scope
- Shows display name, scope, and policy definition ID
- Defender plan coverage per subscription (Standard vs Free)
- Free tier means the workload is not actively monitored by Defender
- Resource groups and VMs with no tags
- Or missing required tags if
-RequiredTagsis specified
- App Services with
PublicNetworkAccessnot explicitly set toDisabled - Storage Accounts with no network firewall (
DefaultAction: Allow) - SQL logical servers with
publicNetworkAccessnot set toDisabled - SQL Managed Instances with
publicDataEndpointEnabledset totrue
- Permanent
OwnerandContributorrole assignments for users and groups (i.e. not service principals or managed identities) - These direct permanent assignments should be replaced with PIM-eligible assignments to enforce just-in-time access
- Note: PIM-eligible (not yet activated) assignments are not returned by the Azure RBAC API and are therefore not listed
The script generates a single HTML dashboard with:
- Summary cards
- Donut chart (risk by category)
- Heat map (subscription vs issue type)
- Top 5 subscriptions by risk
- Per-subscription detailed tables
- Export-to-CSV buttons for each section
Install-Script -Name Invoke-AzHealthCheck -ForceInvoke-AzHealthCheck
Invoke-AzHealthCheck -OpenAfterExport| Parameter | Type | Default | Description |
|---|---|---|---|
-OpenAfterExport |
Switch | $false |
Automatically opens the HTML report after generation |
-CpuHighThresholdPercent |
Int | 80 |
CPU P95 threshold (%) above which a VM is flagged as high-CPU |
-CpuTopNPerSubscription |
Int | 20 |
Maximum number of high-CPU VMs reported per subscription |
-RequiredTags |
String[] | @() |
List of required tag names to check (e.g. 'Environment','Owner'). If empty, reports resources with no tags at all |
C:\TEMP\Health Check script\
Azure-HealthCheck_<TenantId>_MULTI_<timestamp>.html
The script is read-only and does not modify any resources.
| Component | Required Role |
|---|---|
| Resources (VMs, RGs, storage, network, App Services, etc.) | Reader |
| Key Vault expiry metadata | Key Vault Secrets User (or custom: secrets/read, keys/read, certificates/read) |
| Recovery Services Vault (backup items) | Reader (or Backup Reader in restricted tenants) |
| CPU metrics | Monitoring Reader (or Microsoft.Insights/metrics/read) |
| Role assignments (Privileged Identity check) | Reader (includes Microsoft.Authorization/roleAssignments/read) |
- Managed Identity gaps — VMs/App Services still using service principal passwords
- Entra ID guest users — subscriptions with high numbers of guests with elevated roles
- Stopped (deallocated) VMs — off for >30 days, still incurring disk costs
- Undersized/idle VMs — VMs running at <5% avg CPU (complement to the existing high CPU check)
- Old snapshots — disk snapshots older than 90 days
- Diagnostic settings on resources — Key Vaults, Storage Accounts, NSGs missing diagnostics
- Azure Advisor integration — pull high/medium severity recommendations into the report
- Email report — send the HTML report via SendGrid/SMTP after generation
- Dark mode UI for HTML report
- Scheduled run via Azure Automation — runbook wrapper to save reports to Blob Storage on a schedule
- PDF export
- Comparison mode — diff two reports to highlight what's new or fixed since the last run
Pull requests are welcome!
Feel free to submit issues or feature requests using GitHub Issues.
João Paulo Costa
Blog: https://getpractical.co.uk
LinkedIn: https://www.linkedin.com/in/jpsantoscosta
This project is licensed under the MIT License.