AzLocal.UpdateManagement
0.7.63
PowerShell module to manage Azure Local (formerly Azure Stack HCI) cluster updates using Azure Update Manager APIs. Provides functions to start updates, check update status, list available updates, and monitor update runs. Renamed from AzStackHci.ManageUpdates in v0.7.3 to align with the Azure Local product name.
Minimum PowerShell version
5.1
Installation Options
Owners
Copyright
(c) Microsoft. All rights reserved.
Package Details
Author(s)
- Neil Bird Microsoft
Tags
Azure AzureLocal AzureStackHCI Updates UpdateManager HCI Automation CICD Pipeline ServiceNow ITSM Incident
Functions
Connect-AzureLocalServicePrincipal Start-AzureLocalClusterUpdate Get-AzureLocalClusterUpdateReadiness Get-AzureLocalClusterInventory Get-AzureLocalClusterInfo Get-AzureLocalUpdateSummary Get-AzureLocalAvailableUpdates Get-AzureLocalUpdateRuns Set-AzureLocalClusterUpdateRingTag Invoke-AzureLocalFleetOperation Get-AzureLocalFleetProgress Test-AzureLocalFleetHealthGate Export-AzureLocalFleetState Resume-AzureLocalFleetUpdate Stop-AzureLocalFleetUpdate Test-AzureLocalClusterHealth Get-AzureLocalFleetStatusData New-AzureLocalFleetStatusHtmlReport Test-AzureLocalUpdateScheduleAllowed Reset-AzureLocalSideloadedTag Get-AzureLocalItsmConfig Test-AzureLocalItsmConnection New-AzureLocalIncident Copy-AzureLocalPipelineExample Copy-AzureLocalItsmSample
PSEditions
Dependencies
This module has no dependencies.
Release Notes
## Version 0.7.63 - PowerShell 7 ParserError fix in fleet-update-status pipeline samples
### Fixed (critical)
- fleet-update-status.yml samples (GitHub Actions and Azure DevOps)
failed on the Create Status Summary step under pwsh 7 (default
shell on GH-hosted Windows runners) with
"ParserError: The Unicode escape sequence is not valid". Inside
the PS double-quoted here-string that builds the Markdown summary,
Markdown code-span backticks before file names like
update-summaries.csv and update-runs.csv were interpreted as the
PS 7 `u{xxxx} Unicode escape (which expects `{` next); PS 5.1 had
silently swallowed the backtick. Other file refs in the same block
had latent corruption under both shells (`r -> CR, `a -> BEL,
`c -> dropped backtick). All Markdown code-span backticks in the
affected blocks have been doubled, which renders as a literal
backtick in the output string under both PS 5.1 and PS 7. No
module code paths are affected; only the sample YAMLs.
### Pipeline migration
If you have copied fleet-update-status.yml into your repo, refresh
the samples via:
Copy-AzureLocalPipelineExample -Destination <path> -Platform GitHub -Update
Copy-AzureLocalPipelineExample -Destination <path> -Platform AzureDevOps -Update
## Version 0.7.62 - Apply-updates pipeline now consumes the readiness CSV; health gate, JUnit Status mapping, tag-write RBAC fixes
### Fixed (critical)
- Start-AzureLocalClusterUpdate Step 3b critical-health gate was being
silently bypassed. The caller invoked Test-AzureLocalClusterHealth
without -PassThru; the function logs to the host stream only and
returns $null without -PassThru, so the predicate
`$healthResults[0].CriticalCount -gt 0` always evaluated false even
when the function had just logged "BLOCKED (N critical)". Apply
would then write "No critical health issues found - cluster is
eligible for update" and proceed to PATCH the update despite
critical health failures. Two additional call sites in
Get-AzureLocalUpdateRuns had the same omission. All three now pass
-PassThru.
- Set-AzLocalClusterTagsMerge (used by Start-AzureLocalClusterUpdate to
stamp UpdateVersionInProgress, by Reset-AzureLocalSideloadedTag, and
by the sideloaded auto-reset path) now writes tags via the ARM tags
subresource (PATCH .../providers/Microsoft.Resources/tags/default,
api-version=2021-04-01) instead of the full cluster resource. This
narrows the required RBAC from `microsoft.azurestackhci/clusters/write`
to `Microsoft.Resources/tags/write` (built-in Tag Contributor),
matching the documented behaviour. Two PATCHes are emitted when
needed: operation=Merge for keys being set, operation=Delete for
keys whose input value is $null. Idempotent: skips keys whose value
already matches and Delete keys that are not present.
- Export-ResultsToJUnitXml previously rendered Status values NotReady,
NotConnected, NoUpdatesAvailable, NoReadyUpdates as <system-out>
(passed) instead of <skipped>, producing misleading "all green" CI
summaries when apply had actually skipped clusters. UpdateNotFound
now renders as <error type="UpdateNotFound"> instead of <system-out>.
The summary <testsuite tests/failures/errors/skipped/> counts and
the per-testcase element now agree with the apply-updates reality.
- Get-HealthCheckFailureSummary now emits Critical-severity entries
before Warning before the top-5 truncation; previously a Critical
hidden behind 5+ Warnings was dropped, and the readiness gate
silently failed to downgrade ReadyForUpdate. Informational entries
remain excluded.
### Changed
- apply-updates pipeline samples (GitHub Actions + Azure DevOps) now
consume the readiness CSV from the check-readiness job instead of
re-discovering clusters by UpdateRing tag. The apply step downloads
the readiness-report artifact, filters rows where
ReadyForUpdate=True, and invokes Start-AzureLocalClusterUpdate
-ClusterResourceIds @(...) against that exact list. Apply still
re-validates each cluster (Step 1b connectivity, Step 3b health,
Step 3c schedule, Step 3b1 sideloaded) as defence in depth.
This guarantees the readiness gate's decision is ENFORCED rather
than advisory: a cluster flagged Blocked in readiness will not be
touched by apply even if its tag still matches the ring.
- Get-AzureLocalClusterUpdateReadiness output (and the readiness CSV)
gains a ClusterResourceId column - the full ARM resource ID - so
the apply step can pass it straight to
Start-AzureLocalClusterUpdate -ClusterResourceIds without a second
Resource Graph query. Populated on every row, including NotFound/
Error rows (set from the input cluster's ResourceId where known).
### Pipeline migration
If you have copied apply-updates.yml into your repo, refresh it via:
Copy-AzureLocalPipelineExample -Destination <path> -Platform GitHub -Update
Copy-AzureLocalPipelineExample -Destination <path> -Platform AzureDevOps -Update
The install step's drift detector will also emit a ::notice/warning
log pointing at this once you bump REQUIRED_MODULE_VERSION to 0.7.62.
## Version 0.7.61 - Readiness gates: ClusterState + Critical health checks now block ReadyForUpdate
### Changed
- Get-AzureLocalClusterUpdateReadiness and Get-AzureLocalFleetStatusData
now downgrade ReadyForUpdate to $false when either of these is true,
even if ARM otherwise reports a Ready update:
- ClusterState is not 'ConnectedRecently' (e.g. NotConnectedRecently,
Disconnected) - ARM cannot reliably push an update to a cluster it
has not heard from recently.
- HealthCheckFailures contains at least one [Critical] severity entry.
Both functions emit a new BlockingReasons CSV column listing the
reason(s) (e.g. "NotConnectedRecently", "CriticalHealthCheck",
"CriticalHealthCheck; NotConnectedRecently") so operators can see why
an otherwise-ready cluster was held back.
- Start-AzureLocalClusterUpdate gains a connectivity gate (Step 1b)
immediately after cluster lookup: clusters whose properties.status is
not 'ConnectedRecently' are skipped with Status='NotConnected' and a
row written to Update_Skipped.csv before any update is attempted.
Complements the existing Step 3b critical-health gate.
- Console summary on Get-AzureLocalClusterUpdateReadiness now reports
"Blocked by Readiness Gate: N" alongside SBE-prereq blocks. The
per-cluster console line shows "Blocked (<reason>)" in red.
### Fixed
- Get-AzureLocalClusterUpdateReadiness JUnit XML export was emitting
Status='Skipped' for every Ready cluster due to a boolean/string
comparison bug (`$_.ReadyForUpdate -eq 'Yes'` against a [bool]).
Status now correctly reports 'Ready' / 'Blocked' / 'Failed' / 'Skipped'.
## Version 0.7.60 - GitHub Actions samples refreshed for Node 24 + checks:write fix on apply-updates
Summary: all five GitHub Actions sample workflows bumped to Node 24-
compatible action majors (actions/checkout v5, actions/upload-artifact v6,
azure/login v3, dorny/test-reporter v3) to clear the "Node.js 20 actions
are deprecated" banner ahead of the Sept 2026 hard-removal. apply-updates
GH Actions sample also gained `checks: write` in both jobs, fixing
`HttpError: Resource not accessible by integration` from dorny/test-reporter
on workflow_dispatch runs (sibling workflows already had it). Full notes
in CHANGELOG.md.
## Version 0.7.50 - Pipelines install from PSGallery + Copy-AzureLocalPipelineExample gains -Update + new Copy-AzureLocalItsmSample
Summary: pipeline examples (5 GitHub Actions + 5 Azure DevOps YAMLs)
now install the module from PSGallery at runtime instead of importing
a vendored copy (default latest, optional pin via REQUIRED_MODULE_VERSION).
Copy-AzureLocalPipelineExample reshaped: -Flatten and -Force removed
(neither survived first real-world use), replaced by -Update for
controlled refresh with per-file ShouldContinue prompts and
-Confirm:$false bypass for unattended use. New public function
Copy-AzureLocalItsmSample copies the bundled ITSM connector sample
(azurelocal-itsm.yml + templates/incident-body.md) into a user-chosen
destination (default .\.itsm, matching the workflow defaults). Not
flagged as breaking: the v0.7.4 -Flatten/-Force surface had not been
adopted at removal time. Full notes in CHANGELOG.md.
## Version 0.7.41 - Hotfix: parallel fleet reads broken by v0.7.3 NestedModules refactor
Summary: hotfix for every fleet read/write function that dispatched
through Invoke-FleetJobsInParallel (and for
New-AzureLocalFleetStatusHtmlReport / Get-AzureLocalFleetStatusData).
Under -ThrottleLimit > 1 against PSGallery-installed v0.7.4, per-batch
Start-Job scriptblocks could not see module-private helpers, returning
State=Error: "Cannot use '&' to invoke in the context of module ...
because it is not imported." Inline (-ThrottleLimit 1) was unaffected.
Centralised resolution in a new private helper
Get-AzLocalModuleRootManifestPath; added regression Pester tests for
the parallel path. Full notes in CHANGELOG.md.
## Versions 0.7.4 and earlier
Highlights: ITSM ticketing (ServiceNow) phase 1 in 0.7.4, opt-in via
raise_itsm_ticket=true plus ./.itsm/azurelocal-itsm.yml. Module renamed
from AzStackHci.ManageUpdates to AzLocal.UpdateManagement in v0.7.3,
monolithic .psm1 split into Public/Private NestedModules. Earlier:
parallel fleet read paths fixed for -ThrottleLimit > 1, EndTime column
on Get-AzureLocalUpdateRuns, UpdateSideloaded auto-reset workflow, ARG
pagination beyond 1000 results, mid-run token refresh, CSV-injection
sanitisation. Full notes in CHANGELOG.md.
For full release notes on this and previous versions, see:
https://github.com/NeilBird/Azure-Local/blob/main/AzLocal.UpdateManagement/CHANGELOG.md
FileList
- AzLocal.UpdateManagement.nuspec
- Public\Copy-AzureLocalItsmSample.ps1
- Public\Get-AzureLocalFleetStatusData.ps1
- Public\Resume-AzureLocalFleetUpdate.ps1
- Automation-Pipeline-Examples\azure-devops\apply-updates.yml
- Automation-Pipeline-Examples\github-actions\fleet-update-status.yml
- ITSM\README.md
- Private\Format-AzLocalDurationHuman.ps1
- Private\Get-CurrentStepPath.ps1
- Private\Invoke-AzLocalItsmHttp.ps1
- Private\Invoke-FleetOpClusterAction.ps1
- Private\Test-AzLocalUpdateSideloadedAllowed.ps1
- Public\Copy-AzureLocalPipelineExample.ps1
- Public\Get-AzureLocalItsmConfig.ps1
- Public\Set-AzureLocalClusterUpdateRingTag.ps1
- AzLocal.UpdateManagement.psd1
- Automation-Pipeline-Examples\azure-devops\assess-update-readiness.yml
- Automation-Pipeline-Examples\github-actions\inventory-clusters.yml
- Private\ConvertFrom-AzLocalUpdateExclusion.ps1
- Private\Format-AzLocalIncidentBody.ps1
- Private\Get-ExportFormat.ps1
- Private\Invoke-AzLocalServiceNowAdapter.ps1
- Private\Resolve-AzLocalItsmSecret.ps1
- Private\Test-AzLocalUpdateVersionInProgressMatch.ps1
- Public\Export-AzureLocalFleetState.ps1
- Public\Get-AzureLocalUpdateRuns.ps1
- Public\Start-AzureLocalClusterUpdate.ps1
- AzLocal.UpdateManagement.psm1
- Automation-Pipeline-Examples\azure-devops\auth-smoke-test.yml
- Automation-Pipeline-Examples\github-actions\manage-updatering-tags.yml
- Private\ConvertFrom-AzLocalUpdateSideloaded.ps1
- Private\Format-AzLocalUpdateRun.ps1
- Private\Get-HealthCheckFailureSummary.ps1
- Private\Invoke-AzLocalSideloadedAutoReset.ps1
- Private\Resolve-SafeOutputPath.ps1
- Private\Test-AzLocalUpdateWindow.ps1
- Public\Get-AzureLocalAvailableUpdates.ps1
- Public\Get-AzureLocalUpdateSummary.ps1
- Public\Stop-AzureLocalFleetUpdate.ps1
- CHANGELOG.md
- Automation-Pipeline-Examples\azure-devops\fleet-update-status.yml
- docs\images\apply-updates-summary.png
- Private\ConvertFrom-AzLocalUpdateWindow.ps1
- Private\Get-AzLocalClusterUpdateRuns.ps1
- Private\Get-LastUpdateRunErrorSummary.ps1
- Private\Invoke-AzLocalSideloadedAutoResetForCluster.ps1
- Private\Resolve-WildcardDate.ps1
- Private\Test-ExportPathWritable.ps1
- Public\Get-AzureLocalClusterInfo.ps1
- Public\Invoke-AzureLocalFleetOperation.ps1
- Public\Test-AzureLocalClusterHealth.ps1
- example-update-request.json
- Automation-Pipeline-Examples\azure-devops\inventory-clusters.yml
- docs\images\auth-smoke-test-validate-oidc.png
- Private\ConvertTo-AzLocalAdditionalProperties.ps1
- Private\Get-AzLocalItsmDedupeKey.ps1
- Private\Get-LatestUpdateByYYMM.ps1
- Private\Invoke-AzResourceGraphQuery.ps1
- Private\Resolve-WildcardDateRange.ps1
- Private\Write-Log.ps1
- Public\Get-AzureLocalClusterInventory.ps1
- Public\New-AzureLocalFleetStatusHtmlReport.ps1
- Public\Test-AzureLocalFleetHealthGate.ps1
- README.md
- Automation-Pipeline-Examples\azure-devops\manage-updatering-tags.yml
- docs\images\inventory-clusters-run-output.png
- Private\ConvertTo-SafeCsvCollection.ps1
- Private\Get-AzLocalItsmTriggerDecision.ps1
- Private\Get-TagValue.ps1
- Private\Invoke-AzRestJson.ps1
- Private\Set-AzLocalClusterTagsMerge.ps1
- Private\Write-UpdateCsvLog.ps1
- Public\Get-AzureLocalClusterUpdateReadiness.ps1
- Public\New-AzureLocalIncident.ps1
- Public\Test-AzureLocalItsmConnection.ps1
- Automation-Pipeline-Examples\README.md
- Automation-Pipeline-Examples\github-actions\apply-updates.yml
- docs\images\README.md
- Private\ConvertTo-SafeCsvField.ps1
- Private\Get-AzLocalModuleRootManifestPath.ps1
- Private\Import-AzureLocalFleetState.ps1
- Private\Invoke-AzureLocalUpdateApply.ps1
- Private\Test-AzCliAvailable.ps1
- Private\Write-Utf8NoBomFile.ps1
- Public\Get-AzureLocalFleetProgress.ps1
- Public\Reset-AzureLocalSideloadedTag.ps1
- Public\Test-AzureLocalUpdateScheduleAllowed.ps1
- Automation-Pipeline-Examples\.itsm\azurelocal-itsm.yml
- Automation-Pipeline-Examples\github-actions\assess-update-readiness.yml
- ITSM\ITSM-Config-Reference.md
- Private\ConvertTo-ScrubbedCliOutput.ps1
- Private\Get-AzLocalRunEndTime.ps1
- Private\Install-AzGraphExtension.ps1
- Private\Invoke-FleetJobsInParallel.ps1
- Private\Test-AzLocalUpdateExclusion.ps1
- Public\Connect-AzureLocalServicePrincipal.ps1
- Automation-Pipeline-Examples\.itsm\templates\incident-body.md
- Automation-Pipeline-Examples\github-actions\auth-smoke-test.yml
- ITSM\ITSM-Connector-Plan.md
- Private\Export-ResultsToJUnitXml.ps1
Version History
| Version | Downloads | Last updated |
|---|---|---|
| 0.7.63 (current version) | 0 | 5/16/2026 |
| 0.7.60 | 1 | 5/15/2026 |
| 0.7.50 | 9 | 5/15/2026 |
| 0.7.41 | 4 | 5/13/2026 |
| 0.7.3 | 3 | 5/13/2026 |