Turning on Boot Diagnostics for Every VM in Your Azure Tenant
- Shannon
- 3 minutes ago
- 6 min read
Like a lot of my posts lately, this one kicked off with a customer question. An innocent question at that, but the kind that makes you pause and realize plenty of others are probably wondering the same thing. We were chatting about their Azure environment when someone asked “How do I turn on boot diagnostics for all my VMs? Not just one or two, but across the entire tenant.”
Their story? A classic (and familiar) tale. Cloud adoption by click-ops, blissful ignorance of boot diagnostics, and then one day…a VM faceplanted and no one could access the environment from the Azure portal. Then the doom of what other VMs don't have boot diagnostics enabled.
Turns out boot diagnostics is one of those features you only think about when things go wrong and it recently did in this instance. When a VM refuses to start and you are left staring at the portal wondering what happened, having a screenshot or console log can make all the difference. Without it, you are troubleshooting blind and often times recreating the VM or restoring from backup.
The problem is scale. Turning on boot diagnostics is easy when you create a single VM. It is also easy to click into one VM and enable it. But if you have dozens, hundreds, or thousands of VMs across multiple subscriptions, clicking through the portal is not a realistic solution.
Just like most things these days...you need automation...and quick!
That's where PowerShell steps into the limelight.
The PowerShell Script
This script does the following (and like all my code examples, it's fast and quick to give you a something that can be easily expanded upon in your own environment so it's a bit more "enterprise grade"):
Connects to Azure and pulls all subscriptions in the tenant
Loops through every VM in every subscription
Checks whether boot diagnostics are already enabled
Creates or reuses a storage account to hold the logs and screenshots
Enables boot diagnostics if they are missing
Here is the script you can run (requires the Az PowerShell modules):
<#
.SYNOPSIS
Enables Boot Diagnostics for all Virtual Machines across all
subscriptions in a single Azure tenant.
.DESCRIPTION
This script connects to Azure, iterates through all subscriptions
visible to the signed-in account, enumerates all Virtual Machines, and
ensures Boot Diagnostics are enabled. If Boot Diagnostics are not
already enabled, the script provisions (or reuses) a storage account
in the VM's region and enables Boot Diagnostics for that VM.
.PARAMETER None
No input parameters are required. The script will operate on all
accessible subscriptions for the currently authenticated account.
.NOTES
Author: Shannon Eldridge-Kuehn
Version: 1.1
Date: 2025-08-19
Requirements:
- PowerShell 7 recommended
- Az PowerShell modules
- Contributor or higher privileges on all target subscriptions
Important:
- Storage account names must be globally unique. The script uses a
combination of "bootdiag", region, and part of the subscription
ID to reduce collisions. Modify as needed to align with
organizational naming standards.
.EXAMPLE
PS C:\> .\enableAzBootDiagAll.ps1
Runs the script interactively. Prompts for Azure login and enables
Boot Diagnostics for all VM in all subscriptions accessible to the
account.
#>
# Ensure Az module is loaded
if (-not (Get-Module -ListAvailable -Name Az.Accounts)) {
Write-Error "Az PowerShell modules are required. Install-Module Az `
-Scope CurrentUser"
exit 1
}
# Connect to Azure
try {
Connect-AzAccount -ErrorAction Stop
} catch {
Write-Error "Failed to authenticate to Azure: $_"
exit 1
}
# Retrieve all subscriptions
$subscriptions = Get-AzSubscription
if (-not $subscriptions) {
Write-Warning "No subscriptions found for the current tenant."
exit 0
}
foreach ($sub in $subscriptions) {
Write-Host "===== Processing Subscription: $($sub.Name) =====" `
-ForegroundColor Cyan
try {
Set-AzContext -Subscription $sub.Id -ErrorAction Stop
} catch {
Write-Warning "Failed to set context for subscription $($sub.Name). ` Skipping."
continue
}
# Retrieve all VMs in the subscription
$vms = Get-AzVM
if (-not $vms) {
Write-Host "No VMs found in subscription $($sub.Name)." `
-ForegroundColor DarkGray
continue
}
foreach ($vm in $vms) {
Write-Host "Processing VM: $($vm.Name) in Resource Group: ` $($vm.ResourceGroupName)" -ForegroundColor Yellow
# Skip if already enabled
if ($vm.DiagnosticsProfile.BootDiagnostics.Enabled) {
Write-Host " -> Boot Diagnostics already enabled." `
-ForegroundColor Green
continue
}
# Build storage account name
$region = $vm.Location.Replace(" ", "").ToLower()
$storageAcctName = ("bootdiag" + $region + ` $sub.Id.Substring(0,6)).ToLower()
# Check if storage account exists
$storageAcct = Get-AzStorageAccount -ResourceGroupName `
$vm.ResourceGroupName -ErrorAction SilentlyContinue | `
Where-Object { $_.StorageAccountName -eq $storageAcctName }
if (-not $storageAcct) {
Write-Host " -> Creating storage account $storageAcctName in $region" `
-ForegroundColor Cyan
try {
$storageAcct = New-AzStorageAccount -ResourceGroupName $vm.ResourceGroupName `
-Name $storageAcctName `
-SkuName Standard_LRS `
-Location $vm.Location `
-Kind StorageV2 -ErrorAction Stop
} catch {
Write-Warning " -> Failed to create storage account for VM ` $($vm.Name). Skipping VM."
continue
}
}
$storageUri = $storageAcct.PrimaryEndpoints.Blob
# Enable boot diagnostics
try {
Set-AzVMBootDiagnostic -ResourceGroupName $vm.ResourceGroupName `
-VMName $vm.Name `
-Enable `
-StorageUri $storageUri -ErrorAction Stop
Write-Host " -> Boot diagnostics enabled for $($vm.Name)" `
-ForegroundColor Green
} catch {
Write-Warning " -> Failed to enable boot diagnostics for VM ` $($vm.Name). Error: $_"
continue
}
}
}
If it's easier to pull the code from GitHub, here is the actual code in a format that should be seamless to copy/paste: click me.
Things to Watch Out For
Storage account naming: Storage accounts must be globally unique. My script uses a combination of bootdiag, the region, and part of the subscription ID. This usually works, but you may want to adapt it to match your organization’s naming standards.
Permissions: You need Contributor rights or higher across all subscriptions where you want this to run.
Performance: If you have a very large environment, looping through every VM will take some time. PowerShell 7 lets you speed this up using ForEach-Object -Parallel.
Centralized storage: Some organizations prefer a dedicated resource group and a single storage account per subscription or region for diagnostics. The script can be tweaked if you want more centralization.
Fixing Today and Preventing Tomorrow with Azure Policy
The PowerShell script is excellent for cleaning up what already exists. But what about the future? You do not want new VMs showing up without boot diagnostics enabled. That is where Azure Policy helps. Azure Policy gives you guardrails. You can start by auditing what is missing, and then move to enforcement.
Audit Policy
Audit is a safe starting point. It shows you which VMs are missing boot diagnostics without blocking anything.
{
"properties": {
"displayName": "Audit VMs without boot diagnostics",
"policyType": "Custom",
"mode": "All",
"description": "Audits all VMs that do not have boot diagnostics enabled.",
"metadata": {
"version": "1.0.0",
"category": "Compute"
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"anyOf": [
{
"field": "Microsoft.Compute/virtualMachines/diagnosticsProfile.bootDiagnostics.enabled",
"exists": "false"
},
{
"field": "Microsoft.Compute/virtualMachines/diagnosticsProfile.bootDiagnostics.enabled",
"equals": "false"
}
]
}
]
},
"then": {
"effect": "Audit"
}
}
}
}
With Audit, you can see where the gaps are, fix them with the script, and prepare teams for stricter enforcement later.
If it's easier to pull the code from GitHub, here is the actual code in a format that should be seamless to copy/paste: click me.
Deny Policy
Once you are confident that your templates and pipelines are setting boot diagnostics correctly, move to Deny. This prevents non-compliant VMs from being created in the first place.
{
"properties": {
"displayName": "Deny VMs without boot diagnostics enabled",
"policyType": "Custom",
"mode": "All",
"description": "Blocks creation of any VM that does not have boot diagnostics enabled.",
"metadata": {
"version": "1.0.0",
"category": "Compute"
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"anyOf": [
{
"field": "Microsoft.Compute/virtualMachines/diagnosticsProfile.bootDiagnostics.enabled",
"exists": "false"
},
{
"field": "Microsoft.Compute/virtualMachines/diagnosticsProfile.bootDiagnostics.enabled",
"equals": "false"
}
]
}
]
},
"then": {
"effect": "Deny"
}
}
}
}
Deny ensures that nothing slips through. Instead of cleaning up after the fact, you block misconfigured resources before they ever land in your subscription.
If it's easier to pull the code from GitHub, here is the actual code in a format that should be seamless to copy/paste: click me.
Wrapping Up
Boot diagnostics is one of those features you never appreciate until you need it. With a PowerShell script, you can catch up and enable it everywhere today. With Azure Policy, you can make sure you never fall behind again.
Start with Audit so you know where you stand. Once you are confident that your pipelines and templates are compliant, switch to Deny to enforce consistency.
The combination of remediation and governance is the real win here. Troubleshooting a broken VM will never be fun, but at least now you will not be flying blind.