What you will find here: A pipeline-focused history of every release that changed the bundled
Step.*.ymltemplates, theStep.*_<job>job names, or theUpdate-AzLocalPipelineExampleupgrade behaviour. For the module's full release history (cmdlet behaviour, KQL fixes, ARM dedup, ...), see ../../docs/release-history.md.
Latest version: the
GENERATED_AGAINST_MODULE_VERSIONenv / pipeline variable in every bundledStep.*.ymltemplate is bumped on each release; check that value (or theCHANGELOG.md) for the current pin. Releases after v0.7.70 are mechanical pipeline-pin bumps only (no template-shape changes) and are documented in the module CHANGELOG; the per-release entries below capture the last set of releases that materially changed the bundled YAMLs.
The body of this document tracks v0.7.70 behaviour. Older versions are preserved below for reference.
Backward compatible. All v0.7.70 changes are additive over v0.7.69. The Step.0 validation pipeline (
Step.0_authentication-test.yml) is repositioned from a one-shot smoke test to a recurring Authentication Validation and Subscription Scope Report; the existing inline-script structure is preserved underneath. Pipeline pin bumps to'0.7.70'; refresh existing copies withUpdate-AzLocalPipelineExample.
- Step.0 repositioned as a recurring audit (
Step.0_authentication-test.yml, GH + ADO). Renamed from "Authentication Validation Test" to "Step.0 - Authentication Validation and Subscription Scope Report". Now emits a JUnit XML report (Authentication / Subscription Scope / Resource Graph Reachability suites - one testcase per subscription accessible to the pipeline identity), a markdown summary with the subscription count + per-subscription detail table (rendered in the GitHub Checks UI viadorny/test-reporter@v3and the ADO Tests + Summary tabs viaPublishTestResults@2+##vso[task.uploadsummary]), and aauth-reportartifact (XML +subscriptions.json+subscriptions.csv). Operator guidance updated: run the validation periodically (recommended monthly, or after any RBAC change in the tenant) instead of deleting the file after the first green run - drift in subscription scope is the earliest signal that downstream fleet reports are about to under- or over-count clusters. - New cmdlet
Get-AzLocalFleetHealthOverview. ARG-first fleet health summary - one row per cluster, joiningmicrosoft.azurestackhci/clusterswith the cluster'supdateSummariesextensibility resource via a single Azure Resource Graph batch read for fleet-scale performance. 12 columns includingClusterName,ClusterPortalUrl,HealthStatus,UpdateStatus,CurrentVersion,SbeVersion,AzureConnection,LastChecked,HealthResultsAgeDays. Supports-SubscriptionId,-UpdateRingTag(incl. wildcard***+ semicolon-list),-ExportPath,-PassThru. - Step.6 Fleet Update Status pipeline (
Step.6_fleet-update-status.yml, GH + ADO) - new "Update Run History and Error Details" section. A new<testsuite name="Update Run History and Error Details">in the JUnit XML and a matching### Update Run History and Error Detailsmarkdown table in the run summary surface up to 25 of the most recent unresolved Failed update runs across the fleet (last 30 days). Each row links to the Azure portalSingleInstanceHistoryDetailsdeep-link and includes Status / CurrentStep / Duration / LastUpdated / DeepestErrMsg for at-a-glance triage. Sourced fromGet-AzLocalUpdateRunFailures -State Failed -OnlyUnresolved(ARG-first, fleet-scale). - Step.7 Fleet Health Status pipeline (
Step.7_fleet-health-status.yml, GH + ADO) - cluster portal hyperlinks + 3 new detailed columns + new Fleet Health Overview section. Summary and Detailed Results tables now render cluster cells as[ClusterName](portalUrl)markdown links. Detailed Results adds three columns:Failure Remediation(auto-renders as[link](url)when the value starts withhttps://),Target Resource Name, andTarget Resource Type. A new### Fleet Health Overview (fleet rollup)section callsGet-AzLocalFleetHealthOverviewand publishesfleet-health-overview.csv/.jsonto the Reports list. Test-AzLocalApplyUpdatesScheduleCoverageAudit rows now carry aSectiondiscriminator (Schedulefor ring-level rows,Cronfor cron-coverage rows). Default sort is Schedule-section first, then cron coverage.-View Recommendemits a multi-section markdown report with "add these rings to apply-updates-schedule.yml" first, then the cron-paste section.Get-AzLocalFleetHealthFailuresSummary now sorts Critical-first (Severity then ClusterCount desc then FailureCount desc). Detail rows gainTargetResourceName,TargetResourceType, andClusterPortalUrl. Summary rows gainAffectedClusterPortalUrlsaligned 1:1 withAffectedClusters(semicolon-space separator).- New durable Live-Integration Pester suite (
Tests/Live-Integration.Tests.ps1). 16 tests across 4 Describe blocks, all taggedLiveand excluded from the default 565-test unit run. Asserts the v0.7.70 ARG-first projections (Get-AzLocalFleetHealthOverview,Get-AzLocalFleetHealthFailures,Get-AzLocalUpdateRunFailures) against a real Azure Local fleet. Each Describe auto-skips whenazis not logged in or not pointed at the expected subscription, so the suite is safe to leave permanently in the repo. Opt-in via.\Tests\Invoke-Tests.ps1 -IncludeLive(unit + live) or-LiveOnly(live only). Validated end-to-end against the AdaptiveCloudLab 20-cluster fleet (49 unresolved health failures, 9 unresolved Failed update runs) in 1m26s. - Module version pin bumped to 0.7.70 in every production sample YAML. Run
Update-AzLocalPipelineExample(preferred, marker-aware) orCopy-AzLocalPipelineExample -Updateafter upgrading.
Hard break vs v0.7.68. Schema
schemaVersion: 1forapply-updates-schedule.ymlis the first stable version of this file. If you were running an experimental schedule from earlier development builds, regenerate viaNew-AzLocalApplyUpdatesScheduleConfig.
- Ring-aware apply-updates schedule (5 new cmdlets). Day-grain
apply-updates-schedule.yml(schema v1) is now the single source of truth for whichUpdateRingis eligible on a given UTC date. Three independent layers control "what runs when":- This file (day-grain) says WHICH
UpdateRingtag values are eligible TODAY. - The Step.5 cron schedule (intra-day-grain) says HOW OFTEN the apply-updates job wakes up.
- The per-cluster
UpdateWindowtag (minute-grain) says WHEN, during an eligible day, the actual update is allowed to start.
- This file (day-grain) says WHICH
- New cmdlets:
Get-AzLocalApplyUpdatesScheduleConfig(parse + validate),Resolve-AzLocalApplyUpdatesScheduleRing(date -> ring with cycle-week math + union semantics on overlap),Get-AzLocalApplyUpdatesScheduleNextFirings(preview the next N days),New-AzLocalApplyUpdatesScheduleConfig(strawman generator from live fleetUpdateRingtags; every row emitted commented out by design so the apply-updates pipeline hard-stops at the reader until the operator reviews and uncomments at least one row),Update-AzLocalApplyUpdatesScheduleConfig(idempotent recipe-driven migrator; recipes table ships empty for v1). Test-AzLocalApplyUpdatesScheduleCoveragegained-SchedulePath(two-way ring diff). Emits two new status rows:RingMissingFromSchedule(fleet ring with no schedule row) andRingOrphanedInSchedule(schedule ring no cluster carries). Surfaced in the summary table, the JUnit XML failure list, and the Step.3 Markdown summary.Step.5_apply-updates.yml(GH + ADO) now resolves theUpdateRingvalue fromapply-updates-schedule.ymlon every scheduled firing. Manualworkflow_dispatch(GH) / non-ScheduleBuild.Reason(ADO) runs still honour the operator-supplied-UpdateRingValueinput verbatim, so back-compat for ad-hoc maintenance is preserved.- Concurrency on Step.5. GitHub Actions gained a workflow-level
concurrency:block to prevent overlapping cron firings. Azure DevOps has no first-class YAML concurrency primitive; the ADO version documents the equivalent Pipeline Settings -> Triggers -> Limit concurrent runs option in a banner comment. Step.3_apply-updates-schedule-audit.yml(GH + ADO) gained aschedule_path/schedulePathinput (defaulted to the standard layout), adebugtoggle for self-service triage ($VerbosePreference=Continue,$DebugPreference=Continue, plus a one-shot environment snapshot), and surfaces the newRingMissingFromSchedule/RingOrphanedInSchedulecounts in the summary + JUnit failure list. Whenpipeline_pathis empty andschedule_pathis set, the audit runs schedule-file-only.apply-updates-schedule.example.ymlships as documentation only.Copy-AzLocalPipelineExample/Update-AzLocalPipelineExampledo not touch it. Operators runNew-AzLocalApplyUpdatesScheduleConfigto generate a strawman starting from their live fleet'sUpdateRingtag values.- Module version pin bumped to 0.7.69 in every production sample YAML. Run
Update-AzLocalPipelineExample(preferred, marker-aware) orCopy-AzLocalPipelineExample -Updateafter upgrading. - Test suite: 540 passed / 0 failed / 0 skipped. 19 new tests cover reader, resolver, next-firings, generator, and update across the new ring-aware schedule cmdlets.
- ARG-first refactor of every fleet-scale read cmdlet. Seven cmdlets -
Get-AzLocalUpdateSummary,Get-AzLocalAvailableUpdates,Get-AzLocalClusterUpdateReadiness,Test-AzLocalClusterHealth,Get-AzLocalFleetProgress,Get-AzLocalFleetStatusData,New-AzLocalFleetStatusHtmlReport- and the newGet-AzLocalUpdateRunFailuresand the existingGet-AzLocalUpdateRunsare now backed by a single Azure Resource Graph batch query each. No more N-way per-cluster ARM calls, no moreStart-Jobfan-out on the read path, no more 6-hour-runner-window failure mode on 1000+ cluster fleets. Read pipelines on a 1500-cluster fleet that previously consumed the bulk of a runner window complete in seconds. -ThrottleLimitremoved from those nine cmdlets. The value was a silent no-op against an ARG batch read, so the parameter is now explicitly absent and any pipeline still passing it fails parameter binding loudly. All bundled pipeline YAMLs were updated to stop passing-ThrottleLimit. The apply-side cmdletStart-AzLocalClusterUpdateretains its internal job-pool throttle (no operator-facing parameter). See Section 9 for the migration story.- New cmdlet
Get-AzLocalUpdateRunFailures. ARG-only cmdlet that pulls failure rows out ofmicrosoft.azurestackhci/clusters/updates/updaterunswith a 9-deepmv-expandover the nestedactions[]tree, returning a flat row per failed step with full breadcrumb path, error code, and operator-friendly message. Used by Step.5 Apply Updates post-mortem and ad-hoc triage. No equivalent ARM read path; this cmdlet is intentionally ARG-only. Update-AzLocalPipelineExample- marker-aware in-place YAML upgrade. New cmdlet that refreshes a customer's copy of any bundled pipeline YAML to the version shipped with the current module while preserving operator edits insideBEGIN-AZLOCAL-CUSTOMIZE:<region>/END-AZLOCAL-CUSTOMIZE:<region>marker pairs. Customer-side cron schedules inschedule-triggersand ITSM secret bindings initsm-secrets(Step.5 only) survive a module upgrade. Supports-WhatIf,-Force, and-PassThru. Recommended companion toCopy-AzLocalPipelineExample: copy on first install, update on every module bump.Step.N - <description>display-name prefix on every bundled GitHub Actions workflow and Azure DevOps pipeline. Display ordering in the Actions / Pipelines UI now matches the runbook order in this README (Step.0 auth test, Step.1 inventory, Step.2 manage tags, Step.3 schedule audit, Step.4 readiness, Step.5 apply, Step.6 fleet status, Step.7 fleet health). See Section 1.1 for the rationale.- Latent single-element-array unwrap bug fixed in
Get-AzLocalUpdateRunsandGet-AzLocalClusterUpdateReadiness. The per-cluster lookup pattern$x = if (ContainsKey) { @($list) } else { @() }silently unwrapped a single-elementObject[]to its bare element under the if-block's pipeline return semantics on PowerShell 5.1, causing one-update-per-cluster results to be dropped. Replaced with assignment outside the if-block. Invoke-AzResourceGraphQueryretries on 429. The shared helper behind every ARG-first cmdlet now inspects theRetry-Afterresponse header when present and otherwise applies bounded exponential backoff capped at the documented ARG throttling envelope. Large fleet sweeps no longer fall over at the throttling boundary.- Test suite: 511 passed / 0 failed / 0 skipped. Five previously-skipped Describes for the ARG-first read cmdlets were rewritten against the new code paths and now pass deterministically with
InModuleScope+function global:az { ... }shim +Mock Invoke-AzResourceGraphQuery; the five rewritten Describes contribute 10 new tests. The v0.7.68 entry also adds +4 throttle-handling tests againstInvoke-AzResourceGraphQuery(retry-then-succeed on 429, max-retries-exhausted, no-retry on non-throttle errors, diagnostic flags reset per call) and +3Get-AzLocalFleetStatusDataschema-contract tests (top-level shape + types,ValidateNotNullOrEmptyon-ClusterResourceIds,ModuleVersionfield tracks the module-scope constant). - Module version pin bumped to 0.7.68 in every production sample YAML. Run
Update-AzLocalPipelineExample(preferred) orCopy-AzLocalPipelineExample -Updateafter upgrading.
- Fixed (critical) -
Get-AzLocalFleetHealthFailuresfailed JSON parsing on hosted Windows runners when the Azure CLI emitted a cp1252 encoding warning to stderr. Onwindows-latestGitHub Actions runners (and any ADO Windows agent whose console code page iscp1252) the Azure CLI's underlying Python layer can surfaceWARNING: Unable to encode the output with cp1252 encoding. Unsupported characters are discarded.to stderr; the sharedInvoke-AzResourceGraphQueryhelper was capturing2>&1into a single merged stream and feeding it toConvertFrom-Json, which then threwUnexpected character encountered while parsing value: W. Path '', line 0, position 0.The actual fix is the post-capture stream split: stderr lines surface as[System.Management.Automation.ErrorRecord]objects under2>&1, stdout lines as strings, and only the string stream is fed toConvertFrom-Json. (The helper already passed--only-show-errorssince v0.7.2, matching the existingInvoke-AzRestJsonhardening, but the cp1252 encode warning can still leak through on some character paths, hence the belt-and-braces split.) The helper also setsPYTHONIOENCODING=utf-8as cosmetic defence-in-depth only - this is a structural no-op for stockaz.cmdbecauseaz.cmdlaunches Python with-I(isolated) which implies-Eand forces Python to ignore everyPYTHON*env var per Azure/azure-cli#28497 and the v0.7.2 root-cause analysis; it only takes effect on hosts that have manually patchedaz.cmdto remove-I. - Fixed (critical) -
Step.3_apply-updates-schedule-audit.ymldefaultpipeline_pathonly existed in this module's source repo. The shipped default wasAzLocal.UpdateManagement/Automation-Pipeline-Examples(i.e. the in-source path for this repo); every default-trigger run in a consumer repo therefore failed withPipelineYamlPath '...' does not exist on the runnerbefore the schedule advisor could emit its JUnit XML, which then crasheddorny/test-reporterwithNo test report files were found. Defaults are now'.github/workflows'on GH Actions and'.azure-pipelines'on Azure DevOps - the standard consumer locations - and the path-missing error message now lists which common pipeline folders do exist in the checked-out repo so the operator knows what value to pass viaworkflow_dispatch/ queue-time override. - Module version pin bumped to 0.7.66 in every production sample YAML that installs the module from PowerShell Gallery (14 across GitHub Actions and Azure DevOps; the two Step.0 validation YAMLs do not install the module and so are not version-pinned). Run
Copy-AzLocalPipelineExample -Updateafter upgrading to refresh the samples in your repo.
-
Status emojis in the Fleet Update Status summary.
Step.6_fleet-update-status.ymlon both platforms now uses a green tick / red cross / refresh / yellow circle / info glyph in theCritical HealthandPrimary Statussummary tables instead of the legacy[ok] / [fail] / [ready] / [running] / [blocked] / [info]bracket markers. Plain markdown, so it renders identically on the GH Actions step summary, the ADO pipeline run extension, and any markdown viewer. -
Generation timestamp in the Fleet Update Status H2 heading. The summary heading on both platforms is now
## Fleet Update Status Summary _(generated YYYY-MM-DD HH:MM:SS UTC)_. Operators no longer have to cross-reference the pipeline run start time to know when the data was collected. -
Failed clusters appear before passing clusters in the JUnit per-cluster block. The per-cluster
<testcase>ordering inStep.6_fleet-update-status.yml(both platforms) is now bucketed: failed first (sorted alphabetically), then passed (sorted alphabetically). Failing clusters lead the dorny/test-reporter view (GitHub) and the Tests tab (Azure DevOps). -
Every downloadable artifact carries a UTC timestamp suffix. All
actions/upload-artifact(GH) andPublishBuildArtifacts@1/PublishPipelineArtifact@1(ADO) names now followazlocal-<purpose>_yyyyMMdd_HHmmss. Two runs of the same pipeline on the same day produce distinct zip downloads (azlocal-fleet-update-status-report_20260518_140000.zipvsazlocal-fleet-update-status-report_20260518_180000.zip). Each job computes the timestamp once in a dedicatedCompute Artifact Timestampstep. Affected artifact renames:fleet-status-reports->azlocal-fleet-update-status-report;fleet-health-reports->azlocal-fleet-health-status-report;cluster-inventory->azlocal-cluster-inventory;updatering-tag-logs->azlocal-updatering-tag-logs;schedule-coverage-reports/ScheduleCoverageReports->azlocal-apply-updates-schedule-audit-report;readiness-report->azlocal-apply-updates-readiness-report;readiness-assessment->azlocal-readiness-assessment-report;update-logs->azlocal-apply-updates-logs;itsm-results->azlocal-apply-updates-itsm-results. -
Pipeline
UpdateRinginputs accept multi-value lists and the literal***wildcard. Every pipeline that exposesupdate_ring:(GHworkflow_dispatch) orupdateRing:(ADOparameters:) now accepts:- A single ring (unchanged):
Wave1 - A semicolon-delimited list:
Prod;Ring2(whitespace around each ring is trimmed) - The wildcard
***(three stars, deliberate gesture) to match every cluster that has a non-emptyUpdateRingtag. Untagged clusters are excluded so the wildcard preserves the existing opt-in gate. A single*, double**, or quadruple****are all REJECTED by the cmdlet's[ValidatePattern]so a one-character typo can no longer accidentally scope a fleet-wide write.
The ADO
Step.5_apply-updates.ymllost its closedvalues:enum (kepttype: stringso users still get a free-text editor in the ADO run dialog). The cmdlet-side[ValidatePattern]is tightened to^(\*\*\*|[A-Za-z0-9_-]{1,64}(;[A-Za-z0-9_-]{1,64})*)$on 14 cmdlets that take-UpdateRingValueand on the 1 cmdlet (Get-AzLocalFleetHealthFailures) that takes-UpdateRingTag. Hostile/malformed inputs (spaces, embedded quotes,<script>, leading/trailing;) are still rejected at the parameter binder before any Azure call. - A single ring (unchanged):
-
New private helper
ConvertTo-AzLocalUpdateRingKqlFilter. Centralises the KQL clause construction for the three forms above. Returns| where isnotempty(tags['UpdateRing'])for***(matches only tagged clusters), a| where tags['UpdateRing'] =~ 'single'clause for a single value, and a| where tags['UpdateRing'] in~ ('a','b')clause for a list. Embedded single quotes are doubled (KQL string-literal escape). All 12 ARG-query call sites in the public cmdlets now go through this helper.
Test-AzLocalApplyUpdatesScheduleCoveragecmdlet +apply-updates-schedule-auditpipelines (GitHub Actions + Azure DevOps). Read-only weekly audit (Mon 05:00 UTC + manual) that compares the cron schedule(s) in yourapply-updatespipeline to theUpdateRing/UpdateWindowtags actually present on your clusters and flags any pair that no cron will ever reach. Three views (Audit,Matrix,Recommend), per-segment cron generation for multi-window tags (Sat-Sun_02:00-06:00;Mon-Fri_22:00-04:00) and day ranges (Fri-Mon), configurable-LeadTimeMinutes(0-60, default 5). Each pipeline run emits JUnit XML (one<testcase>per(Ring, Window)), three CSV/MD exports (schedule-coverage-audit.csv,schedule-coverage-matrix.csv,schedule-coverage-recommend.md), and a Markdown step summary. Full step-by-step runbook in Section 8.3; reference card in Appendix A.7.Get-AzLocalFleetHealthFailurescmdlet +fleet-health-statuspipelines (GitHub Actions + Azure DevOps). Dedicated entry point for surfacing the in-flight 24-hour system health-check failures across every readable cluster - independent of update activity (clusters that are already "up to date" can still surface Critical / Warning issues that need triage). Daily 07:00 UTC schedule (offset fromfleet-update-statusat 06:00). JUnit XML grouped underCritical Health Failures/Warning Health Failurestestsuites + per-failure-reason and per-cluster CSV exports + Markdown step summary pivoted byFailureReason. Reference card in Appendix A.6.Set-AzLocalClusterUpdateRingTagnow uses the dedicatedMicrosoft.Resources/tags/defaultPATCH endpoint instead ofPATCH-ing the cluster resource. CI/CD service principals scoped to the built-in Tag Contributor role on the cluster (or resource group) can now writeUpdateRing/UpdateWindow/UpdateExclusionstags without needingmicrosoft.azurestackhci/clusters/write. If you scoped your tag-management SP to "Contributor" purely as a workaround, you can safely drop it back to Tag Contributor. The custom-role guidance in Section 4.1 and the per-step RBAC bullets in Section 6.3 and Appendix A.2 have been updated accordingly.- Pester guardrail prevents pipeline-YAML version drift. A new Pester context discovers every
*.ymlfile underAutomation-Pipeline-Examples/that installs the module from PSGallery and asserts that theGENERATED_AGAINST_MODULE_VERSIONconstant in each YAML matches the module manifest version. Supports both the inline GitHub Actions shape and the two-line Azure DevOps shape. Build fails if a sample YAML is forgotten when the manifest is bumped. - Fleet Update Status summary now reconciles with the JUnit pass/fail counts. Two bug fixes ensure the summary table always adds up to Total Clusters:
Up to Datenow counts bothUpToDateandAppliedSuccessfullystates; each cluster is assigned to exactly one primary status via a priority cascade (Update Failed->Health Failure->SBE Prerequisite Blocked->Update In Progress->Ready for Update->Up to Date->Needs Investigation). - JUnit / step-summary ordering changed in both
Step.6_fleet-update-status.ymlandStep.7_fleet-health-status.yml(both platforms): Summary block FIRST, JUnit Test Results SECOND, so the run-extensions / job-summary view leads with the operator-facing numbers. - Module version pin bumped to 0.7.65 in all 13 sample workflow YAMLs (11 pre-existing + the two new
Step.3_apply-updates-schedule-audit.ymlfiles). RunCopy-AzLocalPipelineExample -Updateafter upgrading to refresh the samples in your repo.
- ITSM Connector - Phase 1 (ServiceNow). Apply Updates can now open ServiceNow incidents for clusters that need operator action (
Failed,Error,HealthCheckBlocked,SideloadedBlocked) via the newNew-AzLocalIncidentfunction, with idempotent SHA256 dedupe so re-running the same workflow does not create duplicates. Fully opt-in - pipelines that do not setraise_itsm_ticket=trueare byte-identical to v0.7.3 behaviour. Sample config + Mustache ticket-body template ship at./.itsm/. Setup, secret sourcing, and troubleshooting documented in../ITSM/README.md; design + decisions log in../ITSM/ITSM-Connector-Plan.md. - OAuth 2.0
client_credentialsonly in Phase 1. Secrets resolve from Azure Key Vault (kv://<vault>/<secret>, recommended), environment variables (env://NAME, native-secret fallback), or explicitliteral://...values guarded by-AllowLiteral. The pipeline service principal needsKey Vault Secrets Useron the configured vault; no other new RBAC. - DryRun mode (
-DryRunonNew-AzLocalIncident, or pipeline inputitsm_dry_run=true) resolves secrets, runs the read-only dedupe lookup, builds the full ticket payload, but does not POST. Output CSV + JUnit projection let you validate the trigger matrix and template rendering before pointing at production ServiceNow. Test-AzLocalItsmConnectionruns the OAuth token grant and a one-row read against/api/now/table/incident, matching the least-privilege scope used by ticket creation. Run it manually before flippingraise_itsm_ticket=true.- New JUnit projection (
-ExportJUnitPathonNew-AzLocalIncident) emits per-cluster ITSM actions as a JUnit XML artefact -CreateFailed-><failure>,Skipped/WhatIf-><skipped>, default -> success. Consumed bydorny/test-reporter/PublishTestResults@2so ITSM activity is visible in the Tests tab. - Phase 2 (
Sync-AzLocalIncidentclose-out) and Phase 3 (Teams + Slack mirror) are designed inITSM-Connector-Plan.mdand deferred to a future release. Thelifecycleandnotificationssections of the config schema are parsed and stored but not yet acted on.
- Fleet read paths now work as documented under
-ThrottleLimit > 1. TheStep.6_fleet-update-status.ymlworkflow (and any direct caller ofGet-AzLocalUpdateRuns,Get-AzLocalUpdateSummary, orGet-AzLocalClusterUpdateReadinesswith-ThrottleLimitgreater than 1) previously failed for every cluster withThe term 'Get-AzLocalClusterUpdateRuns' is not recognized...because module-private helpers were not visible insideStart-Jobchild runspaces. Resolved via& $module { ... }dispatch through the loaded module's session state. Action: re-enable-ThrottleLimitin your fleet workflows. - Stray
cp1252warnings no longer break JSON parsing on hosted Windows runners. Defaultwindows-latestGitHub runners and Azure DevOpswindows-2022agents both run with thecp1252console code page; the Azure CLI emittedWARNING: Unable to encode the output with cp1252 encoding...on any ARM response containing non-ASCII characters. Captured via2>&1, that warning was prepended to the JSON body and silently brokeConvertFrom-Json, dropping update runs and available updates from pipeline reports. v0.7.2 passes--only-show-errorsto everyaz restandaz graph queryinvocation. No pipeline configuration change required - upgrade and the runs/summaries are simply complete again. - Full root-cause writeup: main README v0.7.2 entry.
- Sideloaded payload workflow. Two new tags coordinate human-driven sideloaded update payloads with the apply-updates pipeline:
UpdateSideloaded(operator-set,True/False/1/0) gatesStart-AzLocalClusterUpdate. WhenFalse, the apply-updates pipeline skips the cluster withStatus = SideloadedBlocked.UpdateVersionInProgress(module-managed; do not set manually) holds the staged update name.Get-AzLocalUpdateRunsauto-resetsUpdateSideloaded -> Falseand clearsUpdateVersionInProgresswhen the latest run isSucceededand its update name matches. Use-SkipSideloadedReseton read-only paths.
- New public function
Reset-AzLocalSideloadedTagfor explicit-scope manual reset. Three parameter sets:-ClusterNames,-ClusterResourceIds, or-ScopeByUpdateRingTag -UpdateRingValue. Supports-WhatIf/-Confirm. Default behaviour requireslatest run = Succeededand a case-insensitive update-name match againstUpdateVersionInProgress.-Forcebypasses the version-match check (still requiresSucceededstate). Returns one row per cluster withAction/PreviousSideloaded/NewSideloaded/StagedVersion/MatchedRunUpdateName/Message. - No new RBAC - the existing
Microsoft.Resources/tags/read|writepermissions cover it. - Fully opt-in - clusters without the
UpdateSideloadedtag behave exactly as in v0.7.0. - Full runbook: main README sideloaded workflow section.
- Parallel per-cluster operations.
Get-AzLocalClusterUpdateReadiness,Test-AzLocalClusterHealth,Get-AzLocalUpdateSummary,Get-AzLocalAvailableUpdates,Get-AzLocalUpdateRuns, andSet-AzLocalClusterUpdateRingTagnow run per-cluster ARM calls in parallelStart-Jobbatches.Invoke-AzLocalFleetOperation -ThrottleLimitis honoured end-to-end. Expected 5-10x speedup on 1500-cluster runs. -ThrottleLimitis a workflow input on Apply Updates and Fleet Update Status (default 4, range 1-16).- ARG queries paginate. All scope-resolving queries follow the
$skipTokenuntil exhausted (previously capped silently at 1000). -AllClusterscap removed.New-AzLocalFleetStatusHtmlReport -AllClustersandGet-AzLocalFleetStatusData -AllClustersno longer truncate at 100; use-MaxClusters <n>to trim explicitly.- CSV sanitisation. Every CSV field is protected against Excel formula injection (
=,+,-,@, tab leaders neutralised, CR/LF stripped). - Token refresh mid-run.
Invoke-AzRestJsonrefreshes on HTTP 401 so long-running apply jobs no longer die at the 1-hour token boundary. - Schedule-tag parse errors are blocking by default unless
-Force. A malformedUpdateWindow/UpdateExclusionsno longer lets the update sneak through with just a warning.