Skip to content

Commit 1542805

Browse files
authored
AzLocal.UpdateManagement v0.8.81: Step.10 KPI fix + shared step-summary helpers (Step.05/06/07/08/09/10) (#87)
Step.10 Fleet Health Status - Fixes the long-standing KPI bug where the legacy top table mixed two axes (cluster bucket counts vs failing-check counts) and produced confusing output like "11 + 8 != 20" when one or more clusters had HealthStatus other than 'Healthy' AND no failing detail rows (e.g. 'In progress', 'Unknown', 'Health check failed'). - Splits the table into two: **Cluster Counts** (Total / Healthy / Unhealthy / Other) and **Failing Checks Breakdown** (Total / Critical / Warning / Distinct Reasons). Adds an OtherClusters bucket so Cluster Counts always sum to Total Clusters in Subscription. - Detailed Results: Title column now appears FIRST (collapsed when identical to the Failure Reason), adds a collapsible Description column with file paths / volume detail (the "which drive is corrupt" surface that was previously hidden), and adds a raw health-check FailureName column for grep/triage. - Emits a new step output: other_clusters. Shared step-summary helpers (Step.05-10) - New private helpers in AzLocal.UpdateManagement/Private/: - Get-AzLocalStatusIconMap.ps1 - host-aware status->icon map (Unicode glyphs for GitHub Actions / Local; Markdown shortcodes for Azure DevOps where the renderer prefers them). - Get-AzLocalClusterPortalLink.ps1 - wraps cluster names in an Azure portal anchor (with ClusterPortalUrl override and -Escape switch). - Get-AzLocalCtrlClickTip.ps1 - single source of truth for the "Hold Ctrl / Cmd / middle-click" tip line. - Step.05 (Export-AzLocalClusterUpdateReadinessReport), Step.06 (Export-AzLocalClusterReadinessGateReport), Step.07 (Add-AzLocalApplyUpdatesStepSummary), Step.08 (Export-AzLocalUpdateRunMonitorReport), Step.09 (Export-AzLocalFleetUpdateStatusReport) and Step.10 (Export-AzLocalFleetHealthStatusReport) now all read icons from the helper rather than inlining glyph constants. This eliminates the literal ✅ / 🔵 rendering on Azure DevOps and the duplicated glyph maps across Public cmdlets. Tests - 1209 Pester tests pass (40-test regression from a PS 5.1 [char] supplementary-plane trap caught and fixed in Get-AzLocalStatusIconMap before merge). - New regression Describe blocks cover: helper registration, host-aware icon output, no key-drift between the two return blocks, Step.05/06/07/08/09/10 all import the helper, Step.10 KPI counting (Cluster Counts vs Failing Checks Breakdown, OtherClusters bucket sums correctly, Title-first ordering, Description column visibility). Docs - CHANGELOG.md, README.md, docs/release-history.md, Automation-Pipeline-Examples/README.md and Automation-Pipeline-Examples/docs/appendix-pipelines.md all updated with the v0.8.81 entry. 22 YAML pipeline templates re-pinned to GENERATED_AGAINST_MODULE_VERSION=0.8.81.
1 parent feade0a commit 1542805

40 files changed

Lines changed: 938 additions & 159 deletions

AzLocal.UpdateManagement/Automation-Pipeline-Examples/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ By the end of this guide you will have:
6464
- **Fleet Connectivity Status** (Step.4, v0.7.79+, enhanced in v0.7.85) - read-only daily snapshot of Arc agent connectivity, physical NIC health, Azure Resource Bridge status, and the node-count reconciliation between cluster `reportedProperties.nodes` and Arc-tagged physical machines. *Scheduled daily 05:30 UTC + manual.*
6565
- **Assess Update Readiness** (Step.5) - pre-flight, report-only readiness + blocking-health snapshot, published as JUnit XML. *Manual only.*
6666
- **Sideload Updates** (Step.6, v0.8.7) - **opt-in, off by default, on-prem self-hosted runner/agent required.** Pre-stages solution-update media onto clusters that cannot pull updates from Azure directly (dark / air-gapped fabrics): Robocopy to the cluster import share, remote SHA256 verify, `Add-SolutionUpdate` import, then flip `UpdateSideloaded=True` for the downstream Step.7 apply. Re-entrant state machine driven by a frequent CRON; the multi-hour copy runs in a detached Scheduled Task. *Inert unless `SIDELOAD_UPDATES=true`. See [sideload.md](docs/sideload.md) and [sideload-robocopy.md](docs/sideload-robocopy.md).*
67-
- **Apply Updates** (Step.7; **v0.8.79 break-glass override**: optional `force_immediate_update` / `forceImmediateUpdate` pipeline parameter (defaults `false`, **manual run / queue only**) bypasses the per-cluster `UpdateStartWindow` / `UpdateExclusionsWindow` maintenance-window gate for emergency / out-of-window patching - the override is unreachable from the scheduled `apply-updates-schedule.yml` configuration file and a high-visibility WARNING banner is emitted into the run log when it fires; **v0.8.78 step-summary UX polish**: ``ScheduleBlocked`` / ``SideloadedBlocked`` / ``ExcludedByTag`` outcomes render as JUnit ``<skipped>`` instead of ``<failure>`` so ``dorny/test-reporter`` no longer flips Step.07 RED on by-design gate-respect outcomes - ``HealthCheckBlocked`` deliberately stays a ``<failure>``; the Readiness KPI table now also surfaces **Already Up to Date** and **Not Ready (needs attention before updating)** rows so operators see the full ring breakdown - eligible, started, skipped, blocked, up-to-date, needs-attention - in one place; GHA-only: ``actions/download-artifact@v6 -> @v7`` silences the Node 20 deprecation warning) - apply updates to a single `UpdateRing` wave at a time, with WhatIf / dry-run support. *Manual only by default - **you must add a schedule** that lines up with your cluster `UpdateStartWindow` tags, see [Step 7 - Apply Updates](docs/appendix-pipelines.md#step-7---apply-updates) and [section 8](#8-scheduling-maintenance-windows-and-change-freeze-periods).*
68-
- **Monitor In-Flight Updates** (Step.8, v0.7.90; v0.7.96 surfaces `Status` + deepest `ErrorMessage` columns, adds the `StepError` stuck-step JUnit type for runs that have hit an error inside a step without crossing the long-running threshold, and renders portal-linked Cluster Name / Update Name cells in the markdown summary; **v0.7.98 UX overhaul**: composite `SeverityScore` sort, per-cell `StateIcon` + `StatusIcon`, horizontal chip stack (`STEP-STUCK` / `RUN-STUCK` / `UNRESOLVED` / `RECENT-FAIL`), `CRITICAL / WARN / OK` fleet status badge at the top of the job summary, collapsible `<details>` Verbose Error block per row, and JUnit `<testsuite time="..">` + `<testcase time="..">` populated with real run elapsed seconds). Operational snapshot during an active wave: lists each cluster whose latest update run is `InProgress`, with current step, progress (`completed/total steps`), elapsed duration, the `Status` column (`Success`/`Error`/`InProgress`/...), and the deepest `errorMessage` walked out of the nested ARM `steps[]` tree; flags long-running runs (default >6h) AND step-errored stuck runs as JUnit failures in the Checks tab. *Scheduled 5x/day at 20:00, 22:00, 00:00, 02:00, 04:00 UTC (every 2h across the typical overnight maintenance window) + manual; default cadence is editable in `monitor-updates.yml` (v0.7.92+).*
69-
- **Fleet Update Status** (Step.9, formerly Step.8; v0.7.96 promotes `NeedsAttention` into the **Update Failed** bucket, adds a new **Action Required** bucket for `PreparationFailed`, and folds `PreparationInProgress` into **Update In Progress**; **v0.7.98** populates JUnit `time=` on each `<testcase>` in the `📜 Update Run History and Error Details` testsuite using `DurationMinutes * 60`). Scheduled daily snapshot of fleet update state, surfaced in the Tests tab. Markdown summary's `📜 Update Run History and Error Details` table now also carries portal-linked Cluster Name / Update Name cells plus the deepest-step `ErrorMessage`. *Scheduled daily 06:00 UTC + manual.*
70-
- **Fleet Health Status** (Step.10, formerly Step.9) - scheduled daily snapshot of 24-hour system health-check failures, surfaced in the Tests tab. *Scheduled daily 07:00 UTC + manual.*
67+
- **Apply Updates** (Step.7; **v0.8.81 step-summary polish**: per-host apply-updates step summary now uses the new shared `Get-AzLocalStatusIconMap` host-aware icon helper (fixes literal `:white_check_mark:` text on Azure DevOps step summaries), the Cluster column wraps in a portal deep-link via `Get-AzLocalClusterPortalLink`, and the standing Ctrl-click tip is single-sourced via `Get-AzLocalCtrlClickTip`; **v0.8.79 break-glass override**: optional `force_immediate_update` / `forceImmediateUpdate` pipeline parameter (defaults `false`, **manual run / queue only**) bypasses the per-cluster `UpdateStartWindow` / `UpdateExclusionsWindow` maintenance-window gate for emergency / out-of-window patching - the override is unreachable from the scheduled `apply-updates-schedule.yml` configuration file and a high-visibility WARNING banner is emitted into the run log when it fires; **v0.8.78 step-summary UX polish**: ``ScheduleBlocked`` / ``SideloadedBlocked`` / ``ExcludedByTag`` outcomes render as JUnit ``<skipped>`` instead of ``<failure>`` so ``dorny/test-reporter`` no longer flips Step.07 RED on by-design gate-respect outcomes - ``HealthCheckBlocked`` deliberately stays a ``<failure>``; the Readiness KPI table now also surfaces **Already Up to Date** and **Not Ready (needs attention before updating)** rows so operators see the full ring breakdown - eligible, started, skipped, blocked, up-to-date, needs-attention - in one place; GHA-only: ``actions/download-artifact@v6 -> @v7`` silences the Node 20 deprecation warning) - apply updates to a single `UpdateRing` wave at a time, with WhatIf / dry-run support. *Manual only by default - **you must add a schedule** that lines up with your cluster `UpdateStartWindow` tags, see [Step 7 - Apply Updates](docs/appendix-pipelines.md#step-7---apply-updates) and [section 8](#8-scheduling-maintenance-windows-and-change-freeze-periods).*
68+
- **Monitor In-Flight Updates** (Step.8, v0.7.90; **v0.8.81 host-aware icons**: the State and Current Step Status columns now use the shared `Get-AzLocalStatusIconMap` helper (fixes literal `:large_blue_circle:` / `:hourglass_flowing_sand:` text on Azure DevOps step summaries); v0.7.96 surfaces `Status` + deepest `ErrorMessage` columns, adds the `StepError` stuck-step JUnit type for runs that have hit an error inside a step without crossing the long-running threshold, and renders portal-linked Cluster Name / Update Name cells in the markdown summary; **v0.7.98 UX overhaul**: composite `SeverityScore` sort, per-cell `StateIcon` + `StatusIcon`, horizontal chip stack (`STEP-STUCK` / `RUN-STUCK` / `UNRESOLVED` / `RECENT-FAIL`), `CRITICAL / WARN / OK` fleet status badge at the top of the job summary, collapsible `<details>` Verbose Error block per row, and JUnit `<testsuite time="..">` + `<testcase time="..">` populated with real run elapsed seconds). Operational snapshot during an active wave: lists each cluster whose latest update run is `InProgress`, with current step, progress (`completed/total steps`), elapsed duration, the `Status` column (`Success`/`Error`/`InProgress`/...), and the deepest `errorMessage` walked out of the nested ARM `steps[]` tree; flags long-running runs (default >6h) AND step-errored stuck runs as JUnit failures in the Checks tab. *Scheduled 5x/day at 20:00, 22:00, 00:00, 02:00, 04:00 UTC (every 2h across the typical overnight maintenance window) + manual; default cadence is editable in `monitor-updates.yml` (v0.7.92+).*
69+
- **Fleet Update Status** (Step.9, formerly Step.8; **v0.8.81 host-aware icons**: the Critical Health Status table, the Primary Status table, and the Version Distribution Support column now use the shared `Get-AzLocalStatusIconMap` helper (fixes literal `:green_circle:` / `:yellow_circle:` / `:information_source:` text on Azure DevOps step summaries); v0.7.96 promotes `NeedsAttention` into the **Update Failed** bucket, adds a new **Action Required** bucket for `PreparationFailed`, and folds `PreparationInProgress` into **Update In Progress**; **v0.7.98** populates JUnit `time=` on each `<testcase>` in the `📜 Update Run History and Error Details` testsuite using `DurationMinutes * 60`). Scheduled daily snapshot of fleet update state, surfaced in the Tests tab. Markdown summary's `📜 Update Run History and Error Details` table now also carries portal-linked Cluster Name / Update Name cells plus the deepest-step `ErrorMessage`. *Scheduled daily 06:00 UTC + manual.*
70+
- **Fleet Health Status** (Step.10, formerly Step.9; **v0.8.81**: fixes the KPI counting bug where Healthy + Unhealthy did not sum to Total - the top KPI table is now split into **Cluster Counts** (Total / Healthy / Unhealthy / **Other**) and **Failing Checks Breakdown**, with a new `other_clusters` step output. Detailed Results columns reordered to put `Title` first and add a collapsible `Description` block that surfaces drive/volume-level detail (e.g. file paths from `Microsoft.Health.FaultType.Volume.FileSystem.Corruption.Correctable` warnings); raw fault-type `Name` preserved for grep/triage) - scheduled daily snapshot of 24-hour system health-check failures, surfaced in the Tests tab. *Scheduled daily 07:00 UTC + manual.*
7171
- An end-to-end "ring-based" rollout pattern: Pilot -> Wave2 -> Production, with each ring gated on the previous wave's success.
7272
- **Optional**: a ServiceNow integration that opens deduped incidents for clusters whose run status indicates the module's own retries cannot recover (failures, blocking health checks, sideloaded payload missing) - see [section 7](#7-optional-open-itsm-tickets-for-clusters-needing-operator-action).
7373

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/apply-updates-schedule-audit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ parameters:
8787
default: false
8888

8989
variables:
90-
GENERATED_AGAINST_MODULE_VERSION: '0.8.80'
90+
GENERATED_AGAINST_MODULE_VERSION: '0.8.81'
9191
REQUIRED_MODULE_VERSION: '${{ parameters.moduleVersion }}'
9292
reportsPath: '$(Build.ArtifactStagingDirectory)/reports'
9393
# v0.8.7 sideload advisor defaults. Override at the pipeline / variable-group

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/apply-updates.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ variables:
129129
# log if the YAML appears stale - prompting you to refresh via
130130
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
131131
- name: GENERATED_AGAINST_MODULE_VERSION
132-
value: '0.8.80'
132+
value: '0.8.81'
133133
# Resolution order for the module version pin (leave all unset to install the latest,
134134
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
135135
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/assess-update-readiness.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ variables:
7070
# the version actually installed and to the latest on PSGallery, and emits a warning
7171
# log if the YAML appears stale - prompting you to refresh via
7272
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
73-
GENERATED_AGAINST_MODULE_VERSION: '0.8.80'
73+
GENERATED_AGAINST_MODULE_VERSION: '0.8.81'
7474
# Resolution order for the module version pin (leave all unset to install the latest,
7575
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
7676
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/authentication-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ variables:
6060
# log if the YAML appears stale - prompting you to refresh via
6161
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
6262
- name: GENERATED_AGAINST_MODULE_VERSION
63-
value: '0.8.80'
63+
value: '0.8.81'
6464
# Resolution order for the module version pin (leave all unset to install the latest,
6565
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
6666
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/fleet-connectivity-status.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ variables:
110110
# the version actually installed and to the latest on PSGallery, and emits a warning
111111
# log if the YAML appears stale - prompting you to refresh via
112112
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
113-
GENERATED_AGAINST_MODULE_VERSION: '0.8.80'
113+
GENERATED_AGAINST_MODULE_VERSION: '0.8.81'
114114
# Resolution order for the module version pin (leave all unset to install the latest,
115115
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
116116
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/fleet-health-status.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ variables:
105105
# the version actually installed and to the latest on PSGallery, and emits a warning
106106
# log if the YAML appears stale - prompting you to refresh via
107107
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
108-
GENERATED_AGAINST_MODULE_VERSION: '0.8.80'
108+
GENERATED_AGAINST_MODULE_VERSION: '0.8.81'
109109
# Resolution order for the module version pin (leave all unset to install the latest,
110110
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
111111
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/fleet-update-status.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ variables:
9393
# the version actually installed and to the latest on PSGallery, and emits a warning
9494
# log if the YAML appears stale - prompting you to refresh via
9595
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
96-
GENERATED_AGAINST_MODULE_VERSION: '0.8.80'
96+
GENERATED_AGAINST_MODULE_VERSION: '0.8.81'
9797
# Resolution order for the module version pin (leave all unset to install the latest,
9898
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
9999
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/inventory-clusters.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ variables:
4343
# log if the YAML appears stale - prompting you to refresh via
4444
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
4545
- name: GENERATED_AGAINST_MODULE_VERSION
46-
value: '0.8.80'
46+
value: '0.8.81'
4747
# Resolution order for the module version pin (leave all unset to install the latest,
4848
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
4949
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).

AzLocal.UpdateManagement/Automation-Pipeline-Examples/azure-devops/manage-updatering-tags.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ variables:
4646
# log if the YAML appears stale - prompting you to refresh via
4747
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
4848
- name: GENERATED_AGAINST_MODULE_VERSION
49-
value: '0.8.80'
49+
value: '0.8.81'
5050
# Resolution order for the module version pin (leave all unset to install the latest,
5151
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
5252
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).

0 commit comments

Comments
 (0)