ClusterTriage / Blog / Azure Local
Did the remediation on your Azure Local cluster actually work?
A change ticket that says "resolved" tells you that someone did something. It does not tell you that the cluster now behaves differently. On Azure Local that gap is wider than on a classic Hyper-V cluster, because the platform has its own idea of how it should be configured, and it quietly puts some of your changes back.
This article is the routine I use to check whether a fix actually worked: the same measurement on every node, the platform's own health checks, the event logs since the change, and one more look after the next update.
1. Closed is not fixed
Every cluster assessment ends with a list of findings, and every list of findings turns into tickets. That is where it usually goes wrong. Not in the fixing, but in the closing. A ticket gets closed when the engineer has made the change, not when someone has measured that the change had the effect it was supposed to have.
Most of the time the two are the same. Sometimes they are not, and on Azure Local there are a few extra reasons why:
- The platform manages part of its own configuration. Network ATC owns the settings of the adapters in its intents, and it corrects drift. A setting you changed by hand on a node can be back to its old value an hour later.
- Updates are applied to the whole stack. A solution update brings the operating system, the agents and often drivers and firmware through the OEM extension. Each of those can reset something you fixed.
- The portal shows a summary, not the node. What Azure shows you is the last result that reached Azure, which is not necessarily the state of every node right now.
So I treat every closed finding as a claim that still needs evidence. That is not distrust of whoever did the work. It is the same thing we do in our own lab: the two-node Azure Local cluster I described in Azure Local on Proxmox: what it took exists precisely so that we never have to guess whether something worked.
2. Write the finding down as a measurement
You can only check a fix if you know what you are checking. "Storage network not optimal" cannot be verified. This can:
- What is wrong: jumbo frames are not set the same on the storage adapters of both nodes.
- Where: node 2, both ports of the storage intent.
- The value found:
*JumboPacket= 1514 (jumbo frames off) on node 2, against 9014 on node 1. Some drivers show these values as text, such as "Disabled" or "9014 Bytes". - The command that produced that value, exactly as it was run.
- The expected value after remediation: 9014 on every storage port of every node, taken from the design, not from memory.
That is how findings are written in a ClusterTriage report, and it is not a matter of style. It is what makes the follow-up measurement objective: the same command, the same scope, and a value that either matches or does not. If your findings come from somewhere else, rewrite them in this form before anyone starts fixing. It takes ten minutes per finding and saves a discussion later.
3. Run the same check again, on every node
The most common mistake I see is checking the node where the change was made and calling it done. A cluster is only as consistent as its least consistent node, so the check runs on all of them, in one go, with the results side by side:
Invoke-Command -ComputerName (Get-ClusterNode).Name {
Get-NetAdapterAdvancedProperty -RegistryKeyword '*JumboPacket' |
Select-Object Name, DisplayValue
} | Sort-Object PSComputerName, Name |
Format-Table PSComputerName, Name, DisplayValue
Compare the output with the expected value from the finding, the value your own design specifies. If one line deviates, the finding is not closed, however green the ticket is.
On Azure Local, also ask Network ATC what it thinks, because for adapters in an intent it is ATC that decides:
Get-NetIntent | Select-Object IntentName, NetAdapterNamesAsList
Get-NetIntentStatus | Select-Object Host, IntentName, ConfigurationStatus, ProvisioningStatus
If the intent status is not Success and Completed on every node, do not trust any adapter value you just read. ATC may still be busy, or it may be about to undo what you changed.
4. Read Azure Local's own health checks, and know their age
Azure Local runs its own health checks: before every update, and on a schedule in between. They are useful, and they are also the check people most often misread. On a node you can see the latest result:
$envState = Get-SolutionUpdateEnvironment
$envState | Select-Object State, HealthState, HealthCheckDate
$envState.HealthCheckResult |
Where-Object Status -ne 'SUCCESS' |
Select-Object Title, Status, Severity, Description, Remediation
Three things to keep in mind when you read it.
The result has an age. HealthCheckDate tells you when the checks last ran. If that was before your fix, the result says nothing about your fix. The update readiness view in Azure Update Manager can lag behind as well. When a finding has just been fixed, start a new run first and wait until HealthState no longer shows InProgress:
Invoke-SolutionUpdatePrecheck -SystemHealth
Get-SolutionUpdateEnvironment | Select-Object HealthState, HealthCheckDate
"Ready to update" is not "finding resolved". The health checks look at what blocks or endangers an update. Many assessment findings are not in that set at all: a CSV with the wrong owner, a backup that does not cover all VMs, a witness that nobody has looked at in a year. A clean readiness result is good news, but it is a different question.
A warning is still a finding. Warnings do not block the update, so they tend to stay. Read them, and decide for each one whether it belongs on your list.
5. Storage and cluster: is everything back where it belongs
Many fixes involve a restart, a drain or a change to storage. After that, the cluster needs time to get back to a steady state, and "the change worked" only counts once it has. This is the short list I run on every remediation, whatever the finding was:
# Storage Spaces Direct: open faults and repair jobs
Get-HealthFault
Get-StorageJob | Where-Object JobState -ne 'Completed'
Get-VirtualDisk | Select-Object FriendlyName, HealthStatus, OperationalStatus
# Cluster: nodes, resources and shared volumes
Get-ClusterNode | Select-Object Name, State
Get-ClusterResource | Where-Object State -ne 'Online'
Get-ClusterSharedVolume | Select-Object Name, State, OwnerNode
What you want to see: no open health faults, no running storage jobs, every virtual disk Healthy and OK, every node Up, no resources offline, and the shared volumes spread over the nodes the way they were before. A repair job that is still running is not an error, but it means you are not done yet. Check again when it has finished.
If a node was drained for the fix, also look at where the workloads ended up. Depending on how the node was resumed, not everything moves back, and a cluster where one node carries all the VMs is technically healthy and practically one failure away from a bad day.
A fix only counts when the same measurement shows a different value. In a ClusterTriage follow-up measurement every closed finding is measured again, on every node, and the delta report shows what changed and what came back.
Schedule an Azure Local cluster assessment intro call →6. Count the events since the change
Some findings are not a setting but a behaviour: CSVs that pause, a node that drops out of membership, storage that goes into redirected mode. Those you cannot check with one value. You check them by counting how often the event occurs after the fix, and comparing that with the period before.
Take the time of the change as the starting point and count per node:
$since = Get-Date '2026-11-02 18:00' # time the fix went in
Invoke-Command -ComputerName (Get-ClusterNode).Name {
Get-WinEvent -FilterHashtable @{
LogName = 'System'
Id = 5120, 1135
StartTime = $using:since
} -ErrorAction SilentlyContinue
} | Group-Object PSComputerName, Id |
Select-Object Count, Name
Event 5120 is a CSV that paused, 1135 is a node removed from the cluster membership. Zero is the answer you hope for, but zero after one quiet day proves little. Let it run through at least one period in which the old problem used to appear: a backup window, a patch night, a busy Monday morning. Only then does zero mean something.
When the count is not zero, read the events and the cluster log around those times before you reopen the finding. Sometimes the cause is new and has nothing to do with the old finding. That is exactly the situation in which two people can look at the same logs and tell two different stories, which I wrote about in One incident, two reports.
7. Measure again after the next update
The real test of a fix on Azure Local is the next solution update. That is when drivers, firmware, agents and Network ATC all get a turn, and that is when a fix that was applied in the wrong place disappears.
The typical example is a setting that Network ATC manages. If you change it on the adapter directly, ATC sees drift and corrects it back to what the intent says. The fix then has to go into the intent itself, as an override, so that ATC enforces your value instead of its own:
$override = New-NetIntentAdapterPropertyOverrides
$override.JumboPacket = 9014 # the value from your design
Set-NetIntent -Name 'storage' -AdapterPropertyOverrides $override # the name of your storage intent, see Get-NetIntent
Get-NetIntentStatus -Name 'storage' |
Select-Object Host, ConfigurationStatus, ProvisioningStatus
The same applies outside ATC. A setting that comes back after a node rebuild belongs in the build. A setting that comes back after an OEM update belongs in the OEM tooling or the hardware baseline. A setting that comes back on its own every few hours usually has a GPO or a script behind it.
That is why I plan one more check after the first update following the fix, as a fixed step in the update runbook, the same way you would plan the post-patch checks in Cluster Aware Updating: runbook and audit trail. Re-run the commands from steps 3 to 6 and compare. And keep the output of every run, with date and node. Before and after, side by side, is the evidence. A screenshot of a green portal is not.
This is also how our own follow-up works. In a ClusterTriage Assurance round every closed finding is measured again with the same commands, and the result lands in a delta report: fixed, still open, or back again. How that report is built is in How we follow up on the cluster assessment.
Want to know whether the fixes on your Azure Local cluster actually hold? An Azure Local cluster assessment from ClusterTriage measures it read-only, on every node, together with storage, network and backup, in one report.
Frequently asked questions
No. The Azure Local health checks look at what blocks or endangers an update, and many findings fall outside that set. The result can also be older than your fix. Use it as one piece of evidence, next to the same measurement that produced the finding, run again on every node.
Three times: right after the change, after the cluster has settled (storage jobs finished, workloads back in place), and after the first solution update that follows. For findings about behaviour rather than settings, also after a period in which the problem used to occur, such as a backup window or a patch night.
Because the finding is about the cluster, not the node. A setting that is correct on one node and different on another is still a finding, and on Azure Local a change on one node can trigger Network ATC or other automation that affects the others.
Then something is enforcing it. On Azure Local that is usually Network ATC, which corrects drift on adapters in an intent; the fix then belongs in the intent as an override. Other usual suspects are a GPO, a node build image and OEM update tooling. Find the source before you fix the value again.