Skip to content

AzLocal.UpdateManagement v0.8.79: Step.07 Force Immediate Update break-glass override#85

Merged
NeilBird merged 1 commit into
mainfrom
users/nebird/v0.8.79
Jun 14, 2026
Merged

AzLocal.UpdateManagement v0.8.79: Step.07 Force Immediate Update break-glass override#85
NeilBird merged 1 commit into
mainfrom
users/nebird/v0.8.79

Conversation

@NeilBird

Copy link
Copy Markdown
Owner

AzLocal.UpdateManagement v0.8.79

Summary

Adds an operator-only Force Immediate Update break-glass override to the Step.07 apply-updates pipeline that bypasses the per-cluster UpdateStartWindow / UpdateExclusionsWindow Step 3c maintenance-window gate. Intended for emergency / out-of-window patching driven by an on-call operator. Defaults to OFF, cannot be reached from the scheduled apply-updates-schedule.yml configuration file, and a high-visibility WARNING banner is emitted in the run log when it fires.

Cmdlet surface

  • Start-AzLocalClusterUpdate -IgnoreScheduleTags (new [switch]). When set, the entire Step 3c block is skipped: the per-cluster UpdateStartWindow and UpdateExclusionsWindow tag lookups still execute (so the tag values can be logged), but Test-AzLocalUpdateScheduleAllowed is NOT called and the cluster is admitted regardless of the current UTC time. A Warning-level log entry per cluster records the bypassed tag values.
  • Invoke-AzLocalReadinessGatedClusterUpdate -ForceImmediateUpdate (new [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'). The description: rendered in the Run Workflow GUI starts WARNING: and explicitly notes MANUAL RUNS ONLY.
  • Azure DevOps: new forceImmediateUpdate boolean parameters: entry (default false). The displayName: rendered in the Queue Build GUI starts WARNING: and explicitly notes MANUAL QUEUE ONLY.

Anti-injection design (defence in depth)

Two layers prevent a scheduled cron firing from honouring the flag:

  1. GitHub Actions YAML expression collapse - any schedule / push / pull_request / repository_dispatch event sees 'false' even if a malicious upstream tries to inject the input:
    INPUT_FORCE_IMMEDIATE_UPDATE: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.force_immediate_update || 'false' }}
  2. Azure DevOps runtime guard - the apply step rechecks $(Build.Reason) -eq 'Manual' before flipping the switch. Scheduled / CI / PR runs log a ##vso[task.logissue type=warning] explaining the parameter was ignored.

The override is deliberately not surfaced by New-AzLocalApplyUpdatesScheduleConfig / Resolve-AzLocalPipelineUpdateRing / Get-AzLocalApplyUpdatesScheduleAudit — there is no path to enable it from apply-updates-schedule.yml. A Pester sweep asserts this.

The UpdateExcluded operator hard-override (AzureLocalManagement.UpdateExcluded=true) is INTENTIONALLY still respected by Force Immediate Update — 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.

Scope of change

  • Source / manifest: Start-AzLocalClusterUpdate.ps1, Invoke-AzLocalReadinessGatedClusterUpdate.ps1, AzLocal.UpdateManagement.psd1 (ModuleVersion + ReleaseNotes), AzLocal.UpdateManagement.psm1 ($script:ModuleVersion).
  • Pipelines: bodies of apply-updates.yml (GitHub Actions + Azure DevOps); 22 bundled templates bump GENERATED_AGAINST_MODULE_VERSION from '0.8.78' to '0.8.79'.
  • Docs: CHANGELOG.md, README.md (new What's New section, prior v0.8.78 demoted to Release History per the long-standing one-What's-New convention), docs/release-history.md (v0.8.78 archived entry), Automation-Pipeline-Examples/README.md (Step 7 callout updated), Automation-Pipeline-Examples/docs/appendix-pipelines.md (v0.8.79 Behaviour change block).
  • Tests: parameter-presence regressions for both new switches, plus a 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 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.

Verification

  • Pester (full suite): 1161 passed, 0 failed, 1 skipped (Live-Integration, expected).
  • Import-PowerShellDataFile against the manifest: ModuleVersion 0.8.79, ReleaseNotes char count 7,511 / 10,000.
  • Get-CmdletErrors clean on all source / manifest / YAML edits.

Compatibility

  • No new exports (still 60), no breaking API changes, no readiness-gate behaviour change, no schedule-config behaviour change.
  • Existing operators who do nothing see identical behaviour: both Start-AzLocalClusterUpdate and Invoke-AzLocalReadinessGatedClusterUpdate default the new switches to $false, both pipeline templates default the new inputs to 'false', and a scheduled run cannot honour the flag even if a downstream consumer hand-edits their apply-updates.yml to forward a truthy value.

…k-glass override

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).
@NeilBird
NeilBird merged commit ba3f2bc into main Jun 14, 2026
2 checks passed
@NeilBird
NeilBird deleted the users/nebird/v0.8.79 branch June 14, 2026 18:51
@NeilBird
NeilBird restored the users/nebird/v0.8.79 branch June 14, 2026 18:51
@NeilBird
NeilBird deleted the users/nebird/v0.8.79 branch June 14, 2026 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant