Skip to content

Commit ba3f2bc

Browse files
authored
AzLocal.UpdateManagement v0.8.79: Step.07 Force Immediate Update break-glass override (#85)
Adds an operator-only override that bypasses the per-cluster Step 3c maintenance-window gate (UpdateStartWindow / UpdateExclusionsWindow) for emergency / out-of-window patching. Defaults to OFF. Cannot be reached from the scheduled apply-updates-schedule.yml configuration file. Cmdlet surface: - Start-AzLocalClusterUpdate -IgnoreScheduleTags (switch). When set, the entire Step 3c block is skipped (tag lookup still runs so the values can be logged, but Test-AzLocalUpdateScheduleAllowed is not called). A Warning-level log entry per cluster records the bypassed tag values. - Invoke-AzLocalReadinessGatedClusterUpdate -ForceImmediateUpdate (switch). Forwards -IgnoreScheduleTags to Start-AzLocalClusterUpdate for every cluster in the readiness CSV and emits a host-aware high-visibility WARNING banner at the top of the apply run. Pipeline surface (both apply-updates.yml templates): - GitHub Actions: new force_immediate_update workflow_dispatch choice input (default 'false'), with a 'WARNING:'-prefixed description that also calls out 'MANUAL RUNS ONLY'. - Azure DevOps: new forceImmediateUpdate boolean parameters: entry (default false), with a 'WARNING:'-prefixed displayName that calls out 'MANUAL QUEUE ONLY'. Anti-injection design (two layers of defence to prevent a scheduled cron firing from honouring the flag): 1. GitHub Actions YAML expression collapses to 'false' for any non-workflow_dispatch event: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.force_immediate_update || 'false' }} 2. Azure DevOps runtime guard requires $(Build.Reason) -eq 'Manual' before forwarding the flag; scheduled / CI / PR runs log a ##vso[task.logissue type=warning] explaining the override was ignored. The override is deliberately unreachable from apply-updates-schedule.yml: no forceImmediateUpdate field exists on New-AzLocalApplyUpdatesScheduleConfig, Resolve-AzLocalPipelineUpdateRing, or Get-AzLocalApplyUpdatesScheduleAudit. A Pester anti-leak sweep asserts this. The UpdateExcluded operator hard-override (AzureLocalManagement.UpdateExcluded=true) is INTENTIONALLY still respected - the break-glass override is about time-window gates, not per-cluster opt-outs. Other readiness gates (connectivity, health, sideload status) also continue to apply. Tests: - Parameter-presence regressions for both new switches. - New 'v0.8.79 Step.7 break-glass' Context with 8 It blocks covering: GH input declaration + WARNING: GUI label + MANUAL RUNS ONLY text; ADO parameter declaration + WARNING: displayName + MANUAL QUEUE ONLY text; GHA workflow_dispatch anti-injection collapse pattern; ADO Build.Reason -eq 'Manual' runtime guard; pwsh forwarding of -ForceImmediateUpdate on both hosts; anti-leak sweep across the three schedule-config code paths. - Version assertion bumped 0.8.78 -> 0.8.79. Bundled pipeline templates: all 22 bump GENERATED_AGAINST_MODULE_VERSION from '0.8.78' to '0.8.79'. Bodies for the two apply-updates.yml files (GH + ADO) carry the new feature. Docs: README, CHANGELOG, docs/release-history.md, Automation-Pipeline-Examples/README.md, and Automation-Pipeline-Examples/docs/appendix-pipelines.md all bumped. Prior v0.8.78 What's New demoted from README main body to Release History per the long-standing 'exactly one ## What's New' README convention. No new exports (still 60). No API breaking changes. PSGallery ReleaseNotes char count: 7511 / 10000. Pester: 1161 passed, 0 failed, 1 skipped (Live-Integration, expected).
1 parent a2b5e2f commit ba3f2bc

33 files changed

Lines changed: 279 additions & 53 deletions

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ 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.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).*
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).*
6868
- **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+).*
6969
- **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.*
7070
- **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.*

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.78'
90+
GENERATED_AGAINST_MODULE_VERSION: '0.8.79'
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: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ parameters:
8585
type: boolean
8686
default: true
8787

88+
# v0.8.79 break-glass override. Only honoured when $(Build.Reason) -eq 'Manual'
89+
# (operator-initiated queue from the Azure DevOps UI / CLI). Schedule-triggered
90+
# runs deliberately ignore this flag even if a YAML edit ever sets the default
91+
# to true - the apply step rechecks $(Build.Reason) at runtime.
92+
- name: forceImmediateUpdate
93+
displayName: 'WARNING: FORCE IMMEDIATE UPDATE - bypass UpdateStartWindow / UpdateExclusionsWindow tags and apply updates immediately. Break-glass override for emergency / out-of-window patching. MANUAL QUEUE ONLY - schedule-triggered runs ignore this flag.'
94+
type: boolean
95+
default: false
96+
8897
# --- ITSM Connector (ServiceNow) ---
8998
# Opt-in ticket creation. Defaults preserve the previous pipeline behaviour
9099
# (no ITSM calls). Secrets are wired via the variable group referenced below
@@ -120,7 +129,7 @@ variables:
120129
# log if the YAML appears stale - prompting you to refresh via
121130
# Copy-AzLocalPipelineExample -Update. See Automation-Pipeline-Examples/README.md section 5.
122131
- name: GENERATED_AGAINST_MODULE_VERSION
123-
value: '0.8.78'
132+
value: '0.8.79'
124133
# Resolution order for the module version pin (leave all unset to install the latest,
125134
# which is the default "fix-forward" behaviour): queue-time parameter > pipeline variable
126135
# 'REQUIRED_MODULE_VERSION' overridden at queue time > empty (latest).
@@ -366,6 +375,11 @@ stages:
366375
# clusters with no matching Ready update are skipped with
367376
# status 'NotInAllowList'.
368377
RESOLVED_ALLOWED_UPDATE_VERSIONS: $(resolvedAllowedUpdateVersions)
378+
# v0.8.79 break-glass override - parameter value + Build.Reason guard.
379+
# The runtime check below collapses force=true on Schedule/CI runs to
380+
# force=false so the override only ever takes effect on a Manual queue.
381+
FORCE_IMMEDIATE_UPDATE_PARAM: ${{ parameters.forceImmediateUpdate }}
382+
BUILD_REASON: $(Build.Reason)
369383
inputs:
370384
azureSubscription: 'AzureLocal-ServiceConnection' # Update with your service connection name
371385
scriptType: 'pscore'
@@ -380,6 +394,14 @@ stages:
380394
OutputDirectory = "$(Build.ArtifactStagingDirectory)"
381395
}
382396
if ([System.Convert]::ToBoolean("${{ parameters.dryRun }}")) { $params['DryRun'] = $true }
397+
if ($env:FORCE_IMMEDIATE_UPDATE_PARAM -eq 'True') {
398+
if ($env:BUILD_REASON -eq 'Manual') {
399+
$params['ForceImmediateUpdate'] = $true
400+
}
401+
else {
402+
Write-Host "##vso[task.logissue type=warning]forceImmediateUpdate=True ignored: Build.Reason='$env:BUILD_REASON' (override is honoured only for Manual queue runs)."
403+
}
404+
}
383405
Invoke-AzLocalReadinessGatedClusterUpdate @params
384406
385407
# compute a UTC timestamp variable so both update-logs and itsm-results share a unique per-run suffix.

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.78'
73+
GENERATED_AGAINST_MODULE_VERSION: '0.8.79'
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.78'
63+
value: '0.8.79'
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.78'
113+
GENERATED_AGAINST_MODULE_VERSION: '0.8.79'
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.78'
108+
GENERATED_AGAINST_MODULE_VERSION: '0.8.79'
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.78'
96+
GENERATED_AGAINST_MODULE_VERSION: '0.8.79'
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.78'
46+
value: '0.8.79'
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.78'
49+
value: '0.8.79'
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)