What you will find here: Every exported cmdlet, organised first by single-cluster operations and then by fleet-scale (Get-AzLocalFleet*) operations, followed by an API-version reference. Each cmdlet entry shows the supported parameters, the ARM surface it calls, and a minimum-RBAC reminder.
Cross-reference: The main README.md shows only a single-line summary per cmdlet so it stays printable. Open this file for full signatures and examples.
This table is the canonical index of every public cmdlet the module ships. It is the answer to "what does each function actually do, and does it touch Azure?"
Design principle. This module is ARG-first for READs and ARM-fan-out for WRITEs. Read cmdlets converge on a single Azure Resource Graph query no matter how many clusters are in scope, so they finish in seconds and do not need parallelism (no -ThrottleLimit parameter). Write cmdlets still call ARM (or Azure REST) once per cluster - ARG is a read-only plane and cannot mutate resources - so write cmdlets keep -ThrottleLimit to bound concurrent fan-out. Composite cmdlets that only delegate to other module cmdlets inherit the type of the cmdlets they call.
Type legend:
- READ - calls only Azure Resource Graph and/or Azure REST GET. No state changes.
- READ (composite) - calls other READ cmdlets in this module, optionally writes a local report file.
- READ (validation) - evaluates local inputs (schedules, pipeline YAML); no Azure calls.
- WRITE - mutates Azure resources via ARM PUT/POST/PATCH or tag update.
- WRITE (local) - writes only to the local filesystem (scaffolding/templates).
- AUTH - establishes an Azure context; no data-plane calls.
| Cmdlet | Type | Target |
|---|---|---|
Get-AzLocalClusterInfo |
READ | Azure REST (single cluster lookup) |
Get-AzLocalClusterInventory |
READ | Azure Resource Graph |
Get-AzLocalAvailableUpdates |
READ | Azure Resource Graph |
Get-AzLocalUpdateSummary |
READ | Azure Resource Graph |
Get-AzLocalUpdateRuns |
READ | Azure Resource Graph |
Get-AzLocalUpdateRunFailures |
READ | Azure Resource Graph |
Get-AzLocalClusterUpdateReadiness |
READ | Azure Resource Graph |
Get-AzLocalFleetHealthFailures |
READ | Azure Resource Graph |
Get-AzLocalFleetHealthOverview |
READ | Azure Resource Graph |
Get-AzLocalLatestSolutionVersion |
READ | Microsoft public catalog (aka.ms/AzureEdgeUpdates, unauthenticated) |
Get-AzLocalFleetProgress |
READ | Azure Resource Graph |
Test-AzLocalClusterHealth |
READ | Azure Resource Graph |
Test-AzLocalFleetHealthGate |
READ (composite) | Azure (via Test-AzLocalClusterHealth) |
Get-AzLocalFleetStatusData |
READ (composite) | Azure (delegates to four READ cmdlets above) |
New-AzLocalFleetStatusHtmlReport |
READ (composite) | Azure + local HTML report file |
Export-AzLocalFleetState |
READ (composite) | Azure + local JSON state file |
| Cmdlet | Type | Target |
|---|---|---|
Start-AzLocalClusterUpdate |
WRITE | Azure REST (ARM PUT .../updateRuns/{id}) |
Stop-AzLocalFleetUpdate |
WRITE | Azure REST (ARM POST .../updateRuns/{id}/cancel) |
Resume-AzLocalFleetUpdate |
WRITE | Azure REST (ARM POST .../updateRuns/{id}/retry) |
Invoke-AzLocalFleetOperation |
WRITE | Azure REST (generic per-cluster mutation) |
Set-AzLocalClusterUpdateRingTag |
WRITE | Azure tags (UpdateRing, UpdateStartWindow) |
Reset-AzLocalSideloadedTag |
WRITE | Azure tags (UpdateSideloaded, UpdateVersionInProgress) |
| Cmdlet | Type | Target |
|---|---|---|
Test-AzLocalApplyUpdatesScheduleCoverage |
READ (validation) | Local pipeline YAML + tag inputs |
Test-AzLocalUpdateScheduleAllowed |
READ (validation) | Local schedule input |
Connect-AzLocalServicePrincipal |
AUTH | Az PowerShell context (no data-plane calls) |
Copy-AzLocalPipelineExample |
WRITE (local) | Local file scaffold |
Copy-AzLocalItsmSample |
WRITE (local) | Local file scaffold |
| Cmdlet | Type | Target |
|---|---|---|
Get-AzLocalItsmConfig |
READ | Local ITSM config file |
Test-AzLocalItsmConnection |
READ | External ITSM endpoint |
New-AzLocalIncident |
WRITE | External ITSM (creates ticket) |
Copies the bundled Automation-Pipeline-Examples/ folder (GitHub Actions YAML, Azure DevOps Pipelines YAML, ITSM sample config + ticket-body template, plus the step-by-step setup README) out of the module install location into a destination folder you control. The function is read-only relative to the module install and only destructive relative to the destination (and only when -Force is supplied and the target is already populated).
Parameters:
-Destination(Optional, Position 0): Target folder. If missing, it is created. Default is$PWD.-Platform(Optional):All(default),GitHub, orAzureDevOps. Filters which*-actions//*-devops/subfolders are copied. The top-levelREADME.mdand.itsm/sample folder are platform-agnostic and always copied.-Flatten(Optional Switch): Copy contents directly into-Destination(noAutomation-Pipeline-Examplesparent folder).-Force(Optional Switch): Required if the destination already contains pipeline files; overwrites them.-PassThru(Optional Switch): Return the destination[DirectoryInfo].- Supports
-WhatIfand-Confirm.
Returns: [System.IO.DirectoryInfo] when -PassThru is specified. Nothing otherwise. Always prints a short "next steps" summary to the console.
Examples:
# Default (-Platform All): copies the full sample tree into
# .\Automation-Pipeline-Examples\ under the current directory (browse mode)
Copy-AzLocalPipelineExample
# Drop the GitHub Actions workflow YAML files DIRECTLY into .github\workflows\
# (no wrapper folder, no README, no .itsm). This is the canonical layout for a
# GitHub Actions runner, which only scans .github/workflows/*.yml non-recursively.
New-Item -ItemType Directory .\.github\workflows -Force | Out-Null
Copy-AzLocalPipelineExample -Destination .\.github\workflows -Platform GitHub
# Same idea for Azure DevOps (ADO has no fixed-path convention)
New-Item -ItemType Directory .\pipelines -Force | Out-Null
Copy-AzLocalPipelineExample -Destination .\pipelines -Platform AzureDevOps
# Capture the destination and cd into it
$dest = Copy-AzLocalPipelineExample -Destination C:\repos\fleet -PassThru
Set-Location $destBy default the function refuses to overwrite any file that already exists in the destination - all conflicts are listed in the error message and the copy is aborted. To refresh after a module upgrade, pass
-Update: you will be prompted per file (Y/A/N/L/S/?) before each overwrite. Use-Update -Confirm:$falseto bypass the prompts in scripted / CI scenarios, or-Update -WhatIfto preview the changes. Pipeline files are expected to be under git source control sogit diffgives you the post-overwrite safety net.# Interactive refresh - prompt per file Copy-AzLocalPipelineExample -Destination .\.github\workflows -Platform GitHub -Update # Scripted / CI refresh - no prompts, review afterwards with 'git diff' Copy-AzLocalPipelineExample -Destination .\.github\workflows -Platform GitHub -Update -Confirm:$false
Authenticates to Azure using a Service Principal or Managed Identity (MSI) for CI/CD automation scenarios.
Authentication Methods:
- Managed Identity (
-UseManagedIdentity): For Azure-hosted runners, VMs, or containers with an assigned identity - Service Principal (default): Using credentials from parameters or environment variables
Parameters:
-UseManagedIdentity(Optional): Use Managed Identity authentication instead of Service Principal-ManagedIdentityClientId(Optional): Client ID of a user-assigned managed identity. If not specified, system-assigned identity is used.-ServicePrincipalId(Optional): Application (client) ID. Can also useAZURE_CLIENT_IDenvironment variable.-ServicePrincipalSecret(Optional): Client secret as[SecureString](preferred) or[string]. Can also useAZURE_CLIENT_SECRETenvironment variable. A security warning is logged when a plaintext[string]is passed because command-line arguments may be visible to other users/EDR on the host.-TenantId(Optional): Azure AD tenant ID. Can also useAZURE_TENANT_IDenvironment variable.-Force(Optional): Force re-authentication even if already logged in.
Returns: $true if authentication succeeded, $false otherwise.
Examples:
# Using Managed Identity (recommended for Azure-hosted agents/runners)
Connect-AzLocalServicePrincipal -UseManagedIdentity
# Using user-assigned Managed Identity with specific client ID
Connect-AzLocalServicePrincipal -UseManagedIdentity -ManagedIdentityClientId "12345678-1234-1234-1234-123456789012"
# Using environment variables for Service Principal (recommended for CI/CD)
$env:AZURE_CLIENT_ID = 'your-app-id'
$env:AZURE_CLIENT_SECRET = 'your-secret'
$env:AZURE_TENANT_ID = 'your-tenant-id'
Connect-AzLocalServicePrincipal
# Using a SecureString for the secret (preferred when passing via parameter)
$secret = Read-Host -AsSecureString -Prompt 'Service Principal Secret'
Connect-AzLocalServicePrincipal -ServicePrincipalId $appId -ServicePrincipalSecret $secret -TenantId $tenant
# Plaintext [string] still works but logs a security warning - prefer SecureString or env var
Connect-AzLocalServicePrincipal -ServicePrincipalId $appId -ServicePrincipalSecret $secret -TenantId $tenantSecurity note (Service Principal + secret path): internally
Connect-AzLocalServicePrincipalinvokesaz login --service-principal --password <secret>. The secret is materialised as a plaintext command-line argument for the duration of theaz logincall, which means on Windows it can be visible to other processes via theWin32_Process.CommandLineWMI surface (and likewise to EDR, audit, and process-creation logs). The module always tries the safer paths first (OIDC, Managed Identity, and only then SP) and clears the plaintext copy in afinallyblock, but you should still prefer OIDC or Managed Identity for any production runner. If you must use SP + secret, run on an isolated, EDR-aware host, keep secrets inAZURE_CLIENT_SECRET(env var) or a[SecureString]rather than a literal[string], and rotate frequently.
Main function to start updates on one or more Azure Local clusters.
Parameters:
-ClusterNames(Required*): Array of cluster names to update. Use this OR-ClusterResourceIdsOR-ScopeByUpdateRingTag.-ClusterResourceIds(Required*): Array of full Azure Resource IDs for clusters. Use this when clusters are in different resource groups or subscriptions. Resource IDs are validated before processing: the subscription is verified viaaz account set, and the resource is confirmed to exist with the required permissions. Use this OR-ClusterNamesOR-ScopeByUpdateRingTag.-ScopeByUpdateRingTag(Required*): Switch parameter to find clusters by their 'UpdateRing' tag value via Azure Resource Graph. Must be used with-UpdateRingValue. Use this OR-ClusterNamesOR-ClusterResourceIds.-UpdateRingValue(Required*): The value of the 'UpdateRing' tag to match when using-ScopeByUpdateRingTag.-ResourceGroupName(Optional): Resource group containing the clusters (only used with-ClusterNames)-SubscriptionId(Optional): Azure subscription ID (defaults to current, only used with-ClusterNames)-UpdateName(Optional): Specific update name to apply (e.g.,Solution12.2603.1002.15). If not specified, the latest cumulative update is auto-selected by YYMM version from the update name-ApiVersion(Optional): API version (default: "2025-10-01")-Force(Optional): Skip confirmation prompts-WhatIf(Optional): Show what would happen without executing-LogFolderPath(Optional): Folder path for log files. Default:C:\ProgramData\AzLocal.UpdateManagement\-EnableTranscript(Optional): Enable PowerShell transcript recording-ExportResultsPath(Optional): Export results to JSON (.json), CSV (.csv), or JUnit XML (.xml) file
Examples using Resource IDs:
# Update clusters in different resource groups using Resource IDs
$resourceIds = @(
"/subscriptions/xxx/resourceGroups/RG1/providers/Microsoft.AzureStackHCI/clusters/Cluster01",
"/subscriptions/xxx/resourceGroups/RG2/providers/Microsoft.AzureStackHCI/clusters/Cluster02"
)
Start-AzLocalClusterUpdate -ClusterResourceIds $resourceIds -ForceExamples using Tags (Azure Resource Graph):
# Update all clusters tagged with "UpdateRing" = "Wave1" (across all subscriptions)
Start-AzLocalClusterUpdate -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -Force
# Update all production clusters
Start-AzLocalClusterUpdate -ScopeByUpdateRingTag -UpdateRingValue "Production" -Force
# Update clusters with specific UpdateRing and update version
Start-AzLocalClusterUpdate -ScopeByUpdateRingTag -UpdateRingValue "Pilot" -UpdateName "Solution12.2601.1002.38" -ForcePrerequisites for Tag-based Filtering:
Azure CLI
resource-graphextension (required for-ScopeByUpdateRingTag): The module automatically installs this extension if it's missing (usingaz extension add --name resource-graph --yes). This enables fully automated pipeline scenarios without manual intervention.Set up UpdateRing tags on your clusters (if you haven't already): If you want to use
-ScopeByUpdateRingTagbut your clusters don't haveUpdateRingtags yet, use theGet-AzLocalClusterInventoryandSet-AzLocalClusterUpdateRingTagfunctions:# Option 1: Export inventory to CSV, edit in Excel, then import Get-AzLocalClusterInventory -ExportPath "C:\Temp\ClusterInventory.csv" # Edit the CSV in Excel to populate UpdateRing values, then: Set-AzLocalClusterUpdateRingTag -InputCsvPath "C:\Temp\ClusterInventory.csv" # Option 2: Set tags directly using Resource IDs $ring1Clusters = @( "/subscriptions/xxx/resourceGroups/RG1/providers/Microsoft.AzureStackHCI/clusters/Cluster01", "/subscriptions/xxx/resourceGroups/RG2/providers/Microsoft.AzureStackHCI/clusters/Cluster02" ) Set-AzLocalClusterUpdateRingTag -ClusterResourceIds $ring1Clusters -UpdateRingValue "Wave1" # Then, update all Wave1 clusters Start-AzLocalClusterUpdate -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -Force
Assesses update readiness across Azure Local clusters and provides a summary report. This is a "pre-flight check" to help plan update deployments.
Features:
- Shows which clusters are in "Ready" state for updates
- Lists available and ready-to-install updates for each cluster
- Displays health check state and failures for each cluster
- Provides summary statistics with cluster counts per update version
- Identifies the most common applicable update across your fleet
- Shows clusters with health check issues and their failure reasons
- Exports results to CSV for reporting (includes all diagnostic columns)
Parameters:
-ClusterNames,-ClusterResourceIds, or-ScopeByUpdateRingTag/-UpdateRingValue(same asStart-AzLocalClusterUpdate)-ExportPath(Optional): Export results to a CSV file
Output Columns (and CSV Export):
| Column | Description |
|---|---|
ClusterName |
Name of the Azure Local cluster |
ResourceGroup |
Resource group containing the cluster |
SubscriptionId |
Azure subscription ID |
ClusterState |
Cluster connection state (e.g., "ConnectedRecently") |
UpdateState |
Current update state (e.g., "UpdateAvailable", "NeedsAttention") |
HealthState |
Health check state: "Success", "Warning", "Failure", or "InProgress" |
ReadyForUpdate |
Boolean indicating if the cluster is ready for updates |
AllAvailableUpdates |
All available update names (every state - Ready, NotReady, Installed, etc.). Renamed from AvailableUpdates in v0.7.99. |
ReadyUpdates |
List of updates in "Ready" state |
RecommendedUpdate |
The recommended (latest) ready update |
HealthCheckFailures |
Summary of failed health checks with severity |
Examples:
# Assess all clusters with a specific UpdateRing tag value
Get-AzLocalClusterUpdateReadiness -ScopeByUpdateRingTag -UpdateRingValue "Wave1"
# Assess specific clusters and export to CSV
Get-AzLocalClusterUpdateReadiness -ClusterNames @("Cluster01", "Cluster02") -ExportPath "C:\Reports\readiness.csv"
# Assess clusters by UpdateRing tag across all subscriptions
Get-AzLocalClusterUpdateReadiness -ScopeByUpdateRingTag -UpdateRingValue "Production"Sample Output:
========================================
Azure Local Cluster Update Readiness Assessment
========================================
Assessing 5 cluster(s)...
Checking: Cluster01... Ready (Solution12.2601.1002.38)
Checking: Cluster02... Ready (Solution12.2601.1002.38)
Checking: Cluster03... Update In Progress
Checking: Cluster04... NeedsAttention (Failure)
Checking: Cluster05... Ready (Solution12.2601.1002.38)
========================================
Summary
========================================
Total Clusters Assessed: 5
Ready for Update: 3
Up to Date: 0
Not Ready for Update: 2
Update In Progress: 1
Health Check Issues:
Critical Failures: 1
Warnings: 0
Available Update Versions (clusters ready to install):
Solution12.2601.1002.38: 3 cluster(s) (100%)
Most Common Applicable Update: Solution12.2601.1002.38
Detailed Results:
ClusterName ResourceGroup UpdateState HealthState ReadyForUpdate RecommendedUpdate
----------- ------------- ----------- ----------- -------------- -----------------
Cluster01 RG-West UpdateAvailable Success True Solution12.2601.1002.38
Cluster02 RG-West UpdateAvailable Success True Solution12.2601.1002.38
Cluster03 RG-East UpdateInProgress Success False
Cluster04 RG-East NeedsAttention Failure False
Cluster05 RG-North UpdateAvailable Success True Solution12.2601.1002.38
Clusters with Health Check Issues:
Cluster04: [Critical] Test-CauSetup; [Warning] Test-ClusterQuorum
Gets cluster information by name.
$cluster = Get-AzLocalClusterInfo -ClusterName "MyCluster" -SubscriptionId "xxx"Gets the update summary for a cluster.
$summary = Get-AzLocalUpdateSummary -ClusterResourceId $cluster.id
Write-Host "Update State: $($summary.properties.state)"Lists all available updates for a cluster with enriched state information including SBE dependency details.
# Get enriched update objects (default) - includes PackageType, SBEDependency, UpdateState
$updates = Get-AzLocalAvailableUpdates -ClusterResourceId $cluster.id
$updates | Where-Object { $_.UpdateState -eq "Ready" }
# Get raw ARM API objects for programmatic processing
$raw = Get-AzLocalAvailableUpdates -ClusterResourceId $cluster.id -Raw
$raw | Where-Object { $_.properties.state -eq "Ready" }Gets update run history and status for one or more clusters. Returns formatted objects showing the update name, state, duration, step progress, and current/failed step.
Parameters:
-ClusterName(Required*): Single cluster name (original behavior)-ClusterNames,-ClusterResourceIds, or-ScopeByUpdateRingTag/-UpdateRingValue: Multi-cluster mode-ResourceGroupName(Optional): Resource group (only with-ClusterName/-ClusterNames)-UpdateName(Optional): Filter runs for a specific update-Latest(Optional): Return only the most recent update run per cluster-Raw(Optional): Return raw API response objects instead of formatted output-ExportPath(Optional): Export results to CSV, JSON, or JUnit XML
Output Properties:
| Property | Description |
|---|---|
UpdateName |
The update package name (e.g., Solution12.2603.1002.500) |
State |
Current state: InProgress, Succeeded, Failed, etc. |
StartTime |
When the update run started |
Duration |
How long the update took, human-readable (e.g., 2 hours 30 minutes, 45 minutes (running)) |
Progress |
Step completion (e.g., 3/5 steps) |
CurrentStep |
Currently executing or failed step name |
Examples:
# Get all update runs for a cluster
Get-AzLocalUpdateRuns -ClusterName "MyCluster" -ResourceGroupName "MyRG"
# Get only the latest update run
Get-AzLocalUpdateRuns -ClusterName "MyCluster" -Latest
# Get raw API response for programmatic processing
Get-AzLocalUpdateRuns -ClusterName "MyCluster" -Raw
# Multi-cluster: Get latest run for all clusters in an update ring
Get-AzLocalUpdateRuns -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -Latest
# Export update run history to CSV
Get-AzLocalUpdateRuns -ScopeByUpdateRingTag -UpdateRingValue "Production" -Latest -ExportPath "C:\Reports\runs.csv"Sample Output:
UpdateName State StartTime EndTime Duration Progress CurrentStep
---------- ----- --------- ------- -------- -------- -----------
Solution12.2603.1002.500 InProgress 2026-04-09 16:50 1 hour 12 minutes 3/5 steps DownloadSBE
Solution12.2602.1002.501 Succeeded 2026-03-15 09:00 2026-03-15 11:30 2 hours 30 minutes 5/5 steps
Validates cluster health before applying updates by checking for blocking health check failures. Critical failures prevent updates from being applied.
Parameters:
-ClusterResourceIds,-ClusterNames, or-ScopeByUpdateRingTag/-UpdateRingValue: Target clusters-BlockingOnly(Optional): Show only Critical severity failures (the ones that block updates)-ExportPath(Optional): Export results to CSV, JSON, or JUnit XML
Examples:
# Check health for a single cluster
Test-AzLocalClusterHealth -ClusterResourceIds @("/subscriptions/.../clusters/Seattle")
# Check only update-blocking issues for an update ring
Test-AzLocalClusterHealth -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -BlockingOnly
# Export health results to CSV
Test-AzLocalClusterHealth -ClusterNames @("Cluster01", "Cluster02") -ExportPath "C:\Reports\health.csv"Sample Output:
========================================
Health Validation Summary
========================================
Total Clusters: 1
Passed: 0 (no critical failures)
Blocked: 1 (critical failures present)
Health Check Failures:
ClusterName Severity CheckName Description
----------- -------- --------- -----------
Seattle Critical Test-CauSetup CAU is not configured correctly
Seattle Critical Test-StoragePool Storage pool health degraded
Remediation for Critical (Update-Blocking) Failures:
Seattle - Test-CauSetup: Run Test-CauSetup on the cluster to validate CAU configuration
Seattle - Test-StoragePool: Check storage pool health using Get-StoragePool
HEALTH VALIDATION FAILED - Critical health issues must be resolved before updates can proceed
Note:
Start-AzLocalClusterUpdateautomatically runs this check (Step 3b) before applying updates. If Critical failures are found, the cluster is skipped with detailed diagnostics.
Gets an inventory of all Azure Local clusters with their UpdateRing tag status. This function supports both CSV and JSON export formats for different workflows.
Features:
- Queries all Azure Local clusters across all accessible subscriptions using Azure Resource Graph
- Shows the current UpdateRing tag value for each cluster (or indicates if tag doesn't exist)
- Retrieves subscription names for better readability
- Provides summary statistics showing UpdateRing distribution
- CSV export: For editing in Excel and re-importing with
Set-AzLocalClusterUpdateRingTag - JSON export: For CI/CD pipelines, API integrations, dashboards, and CMDB systems
Parameters:
-SubscriptionId(Optional): Limit query to a specific Azure subscription-ExportPath(Optional): Export inventory to CSV or JSON file. Format is auto-detected from file extension (.csvor.json)-PassThru(Optional): Return inventory objects even when exporting. Useful for CI/CD pipelines that need both the file artifact and objects for processing.
Output Columns:
| Column | Description |
|---|---|
ClusterName |
Name of the Azure Local cluster |
ResourceGroup |
Resource group containing the cluster |
SubscriptionId |
Azure subscription ID |
SubscriptionName |
Human-readable subscription name |
UpdateRing |
Current UpdateRing tag value (empty if not set) |
HasUpdateRingTag |
"Yes" or "No" indicator |
ResourceId |
Full Azure Resource ID |
CSV Workflow (for Excel editing):
# Step 1: Export inventory to CSV
Get-AzLocalClusterInventory -ExportPath "C:\Temp\ClusterInventory.csv"
# Step 2: Open the CSV in Excel and populate the 'UpdateRing' column with values like:
# - "Wave1", "Wave2", "Wave3" for wave-based deployments
# - "Pilot", "Production" for environment-based rings
# - "Ring1", "Ring2" for ring-based deployments
# Step 3: Import the CSV and apply tags
Set-AzLocalClusterUpdateRingTag -InputCsvPath "C:\Temp\ClusterInventory.csv"
# Step 4: Update clusters by their UpdateRing tag
Start-AzLocalClusterUpdate -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -ForceJSON Export (for CI/CD and integrations):
# Export to JSON for API integrations, dashboards, or CMDB systems
Get-AzLocalClusterInventory -ExportPath "C:\Reports\inventory.json"
# Export to JSON AND return objects for pipeline processing
$inventory = Get-AzLocalClusterInventory -ExportPath "./artifacts/inventory.json" -PassThru
Write-Host "Total clusters: $($inventory.Count)"CI/CD Pipeline Example (export both formats):
# Export both CSV and JSON in CI/CD pipelines
# CSV: For human review and Excel editing workflow
Get-AzLocalClusterInventory -ExportPath "./artifacts/inventory.csv"
# JSON: For dashboard integrations and programmatic processing
$inventory = Get-AzLocalClusterInventory -ExportPath "./artifacts/inventory.json" -PassThru
$withoutTag = ($inventory | Where-Object { $_.HasUpdateRingTag -eq 'No' }).Count
Write-Host "Clusters needing UpdateRing tag: $withoutTag"Sample Output:
========================================
Azure Local Cluster Inventory
========================================
Querying Azure Resource Graph for all Azure Local clusters...
Querying across all accessible subscriptions
Found 5 Azure Local cluster(s)
Retrieving subscription details...
Inventory Summary:
Total Clusters: 5
Clusters with UpdateRing tag: 3
Clusters without UpdateRing tag: 2
UpdateRing Distribution:
Wave1: 2 cluster(s)
Wave2: 1 cluster(s)
Inventory exported to CSV: C:\Temp\ClusterInventory.csv
Next Steps (CSV export):
1. Open the CSV in Excel
2. Populate the 'UpdateRing' column with values (e.g., 'Wave1', 'Wave2', 'Pilot')
3. Save the CSV file
4. Run: Set-AzLocalClusterUpdateRingTag -InputCsvPath 'C:\Temp\ClusterInventory.csv'
Sets or updates the "UpdateRing" tag on Azure Local clusters for organizing update deployment waves.
Features:
- NEW: Accepts CSV file input for bulk tag operations (
-InputCsvPath) - Validates that Resource IDs are valid
microsoft.azurestackhci/clustersresources - Checks if clusters already have an "UpdateRing" tag before applying
- Steady-state clusters (existing tag value already matches the target) are logged as
AlreadyInSync/NoChangeand skipped silently - no warning, no ARM PATCH - Clusters whose existing
UpdateRingtag value differs from the target are warned and skipped unless-Forceis specified -Forceshort-circuits when all managed tags already match desired state (no wasted ARM PATCH on already-clean fleets)- Logs previous tag values when updating with
-Force - Outputs results to a timestamped CSV log file; opt-in
-PassThrureturns the same rows as objects for pipeline consumption
Parameters:
-InputCsvPath(Required*): Path to CSV file with ResourceId and UpdateRing columns. Use with output fromGet-AzLocalClusterInventory.-ClusterResourceIds(Required*): Array of full Azure Resource IDs for clusters to tag. Use this OR-InputCsvPath.-UpdateRingValue(Required*): Value to assign to the "UpdateRing" tag (required when using-ClusterResourceIds)-Force(Optional): Overwrite existing "UpdateRing" tags (logs previous value)-LogFolderPath(Optional): Folder path for log files. Default:C:\ProgramData\AzLocal.UpdateManagement\-WhatIf(Optional): Preview changes without applying
Output Columns (CSV Log: UpdateRingTag_YYYYMMDD_HHmmss.csv):
| Column | Description |
|---|---|
ClusterName |
Name of the Azure Local cluster |
ResourceGroup |
Resource group containing the cluster |
SubscriptionId |
Azure subscription ID |
ResourceId |
Full Azure Resource ID |
Action |
Action taken: Created, Updated, NoChange, Skipped, or Error |
PreviousTagValue |
Previous tag value (if updating existing tag) |
NewTagValue |
New tag value being set |
Status |
Result status: Success, AlreadyInSync, Failed, Skipped, or WhatIf |
Message |
Detailed status message |
Examples:
# Import tags from a CSV file (preferred workflow)
Get-AzLocalClusterInventory -ExportPath "C:\Temp\ClusterInventory.csv"
# Edit CSV in Excel to set UpdateRing values, then:
Set-AzLocalClusterUpdateRingTag -InputCsvPath "C:\Temp\ClusterInventory.csv"
# Set UpdateRing tag on multiple clusters directly
$resourceIds = @(
"/subscriptions/xxx/resourceGroups/RG1/providers/Microsoft.AzureStackHCI/clusters/Cluster01",
"/subscriptions/xxx/resourceGroups/RG2/providers/Microsoft.AzureStackHCI/clusters/Cluster02",
"/subscriptions/xxx/resourceGroups/RG3/providers/Microsoft.AzureStackHCI/clusters/Cluster03"
)
Set-AzLocalClusterUpdateRingTag -ClusterResourceIds $resourceIds -UpdateRingValue "Wave1"
# Preview changes without applying
Set-AzLocalClusterUpdateRingTag -InputCsvPath "C:\Temp\ClusterInventory.csv" -WhatIf
# Force update existing tags (logs previous values)
Set-AzLocalClusterUpdateRingTag -InputCsvPath "C:\Temp\ClusterInventory.csv" -Force
# Use with Start-AzLocalClusterUpdate for wave-based deployments
# Step 1: Tag clusters for Wave1
Set-AzLocalClusterUpdateRingTag -ClusterResourceIds $wave1Clusters -UpdateRingValue "Wave1"
# Step 2: Update only Wave1 clusters
Start-AzLocalClusterUpdate -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -ForceSample Output:
========================================
Azure Local Cluster UpdateRing Tag Management
========================================
Log file: C:\ProgramData\AzLocal.UpdateManagement\UpdateRingTag_20260129_091500.log
CSV log: C:\ProgramData\AzLocal.UpdateManagement\UpdateRingTag_20260129_091500.csv
Input mode: CSV file
CSV path: C:\Temp\ClusterInventory.csv
Found 3 row(s) with UpdateRing values to process
Force mode: False
Clusters to process: 3
Azure CLI authentication verified
----------------------------------------
Processing: /subscriptions/xxx/resourceGroups/RG1/providers/Microsoft.AzureStackHCI/clusters/Cluster01
Target UpdateRing: Wave1
----------------------------------------
Cluster: Cluster01
Resource Group: RG1
Subscription: xxx
Verifying cluster exists and retrieving current tags...
Cluster verified: Cluster01
No existing UpdateRing tag - will create new tag
Applying UpdateRing tag with value: 'Wave1'...
Successfully created UpdateRing tag
----------------------------------------
Processing: /subscriptions/xxx/resourceGroups/RG2/providers/Microsoft.AzureStackHCI/clusters/Cluster02
Target UpdateRing: Wave2
----------------------------------------
Cluster: Cluster02
Existing UpdateRing tag found with value: 'Wave1'
Skipping cluster - UpdateRing differs from target; use -Force to overwrite existing tag
----------------------------------------
Processing: /subscriptions/xxx/resourceGroups/RG3/providers/Microsoft.AzureStackHCI/clusters/Cluster03
Target UpdateRing: Wave1
----------------------------------------
Cluster: Cluster03
Existing UpdateRing tag found with value: 'Wave1'
All managed tags already match desired state - no action needed
========================================
Summary
========================================
Total clusters processed: 3
Tags created: 1
Tags updated: 0
Already in sync (no change needed): 1
Skipped (UpdateRing differs, no -Force): 1
Failed: 0
ClusterName Action PreviousTagValue NewTagValue Status
----------- ------ ---------------- ----------- ------
Cluster01 Created Wave1 Success
Cluster02 Skipped Wave1 Wave2 Skipped
Cluster03 NoChange Wave1 Wave1 AlreadyInSync
The following six functions enable enterprise-scale update management across fleets of 1000-3000+ clusters with batching, throttling, retry logic, health gates, and state management. Originally introduced in v0.5.6 and extended through subsequent releases (parallelism + paginated ARG in v0.7.0, sideloaded payload workflow in v0.7.1, ThrottleLimit-safe private-helper dispatch in v0.7.2).
Orchestrates fleet-wide update operations with enterprise-scale features including batch processing, throttling, and automatic retry with exponential backoff.
Features:
- Batch Processing: Process clusters in configurable batches (default: 50 clusters)
- Throttling: Control parallel execution (default: 10 concurrent operations)
- Retry Logic: Automatic retries with exponential backoff (default: 3 retries)
- State Management: Checkpoint/resume capability via state files
- Progress Tracking: Real-time status updates during execution
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-Operation |
String | No | ApplyUpdate |
Operation to perform: ApplyUpdate, CheckReadiness, or GetStatus |
-ScopeByUpdateRingTag |
Switch | Yes* | - | Target clusters by UpdateRing tag |
-UpdateRingValue |
String | Yes* | - | The UpdateRing tag value to filter by |
-ClusterResourceIds |
String[] | Yes* | - | Explicit list of cluster resource IDs |
-UpdateName |
String | No | - | Specific update version to apply |
-BatchSize |
Int | No | 50 |
Clusters per batch (1-500) |
-ThrottleLimit |
Int | No | 10 |
Max parallel operations per batch (1-50) |
-DelayBetweenBatchesSeconds |
Int | No | 30 |
Seconds to wait between batches (0-600) |
-MaxRetries |
Int | No | 3 |
Retry attempts per cluster (0-10) |
-RetryDelaySeconds |
Int | No | 30 |
Base delay between retries - uses exponential backoff (5-300) |
-StateFilePath |
String | No | - | Path to save state for resume capability |
-Force |
Switch | No | - | Skip confirmation prompts |
-PassThru |
Switch | No | - | Return the fleet state object |
*One of -ScopeByUpdateRingTag/-UpdateRingValue OR -ClusterResourceIds is required.
Examples:
# Start updates on all Wave1 clusters with default settings
Invoke-AzLocalFleetOperation -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -Force
# Large fleet with increased batching and parallelism
Invoke-AzLocalFleetOperation -ScopeByUpdateRingTag -UpdateRingValue "Production" `
-BatchSize 100 -ThrottleLimit 20 -DelayBetweenBatchesSeconds 60 -Force
# Save state for resume capability
$state = Invoke-AzLocalFleetOperation -ScopeByUpdateRingTag -UpdateRingValue "Ring1" `
-StateFilePath "C:\Logs\ring1-state.json" -Force -PassThru
# Check readiness across fleet (no updates applied)
Invoke-AzLocalFleetOperation -ScopeByUpdateRingTag -UpdateRingValue "Canary" `
-Operation CheckReadiness
# Apply specific update version
Invoke-AzLocalFleetOperation -ScopeByUpdateRingTag -UpdateRingValue "Pilot" `
-UpdateName "Solution12.2601.1002.38" -ForceSample Output:
========================================
Fleet Operation: ApplyUpdate
Run ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
========================================
Configuration:
Batch Size: 50
Throttle Limit: 10
Delay Between Batches: 30 seconds
Max Retries: 3
Querying clusters with UpdateRing = 'Wave1'...
Total clusters to process: 150
========================================
Batch 1 of 3 (50 clusters)
========================================
Processing: Cluster001
✓ Cluster001 - Succeeded
Processing: Cluster002
✓ Cluster002 - Succeeded
...
========================================
Fleet Operation Complete
========================================
Run ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Duration: 45.2 minutes
Total Clusters: 150
Succeeded: 147
Failed: 3
Gets real-time progress of a fleet-wide update operation with aggregated statistics and optional per-cluster details.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-State |
PSCustomObject | No | - | Fleet operation state object from Invoke-AzLocalFleetOperation |
-ScopeByUpdateRingTag |
Switch | Yes* | - | Query clusters by UpdateRing tag |
-UpdateRingValue |
String | Yes* | - | The UpdateRing tag value to filter by |
-Detailed |
Switch | No | - | Include per-cluster status in output |
*Either -State OR -ScopeByUpdateRingTag/-UpdateRingValue is required.
Output Properties:
| Property | Description |
|---|---|
Timestamp |
When the progress was checked |
TotalClusters |
Total clusters in scope |
Completed |
Clusters that have finished (succeeded + up to date) |
ProgressPercent |
Completion percentage |
Succeeded |
Clusters where update succeeded |
UpToDate |
Clusters already up to date |
InProgress |
Clusters with updates currently running |
Failed |
Clusters where update failed |
NotStarted |
Clusters not yet processed |
ClusterStatuses |
Per-cluster details (when -Detailed used) |
Examples:
# Check progress during an operation
Get-AzLocalFleetProgress -State $fleetState
# Check progress for all Production clusters
Get-AzLocalFleetProgress -ScopeByUpdateRingTag -UpdateRingValue "Production"
# Get detailed per-cluster status
Get-AzLocalFleetProgress -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -Detailed
# Monitor in a loop
while ($true) {
$progress = Get-AzLocalFleetProgress -ScopeByUpdateRingTag -UpdateRingValue "Wave1"
if ($progress.InProgress -eq 0) { break }
Start-Sleep -Seconds 60
}Sample Output:
========================================
Fleet Update Progress Check
========================================
Checking status of 150 cluster(s)...
Progress Summary:
Total Clusters: 150
Completed: 135 (90%)
- Succeeded: 130
- Up to Date: 5
In Progress: 10
Failed: 3
Not Started: 2
Evaluates fleet health to determine if it's safe to proceed with additional update waves. Acts as a "gate" in CI/CD pipelines to prevent cascading failures.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-State |
PSCustomObject | No | - | Fleet operation state to evaluate |
-ScopeByUpdateRingTag |
Switch | Yes* | - | Evaluate clusters by UpdateRing tag |
-UpdateRingValue |
String | Yes* | - | The UpdateRing tag value to filter by |
-MaxFailurePercent |
Int | No | 5 |
Maximum allowed failure percentage (0-100) |
-MinSuccessPercent |
Int | No | 90 |
Minimum required success percentage (0-100) |
-WaitForCompletion |
Switch | No | - | Wait for in-progress updates to complete before evaluating |
-WaitTimeoutMinutes |
Int | No | 120 |
Maximum wait time for completion (minutes) |
-PollIntervalSeconds |
Int | No | 60 |
How often to check status while waiting |
*Either -State OR -ScopeByUpdateRingTag/-UpdateRingValue is required.
Output Properties:
| Property | Description |
|---|---|
Passed |
Boolean - did the gate pass? |
Reason |
Explanation of pass/fail |
TotalClusters |
Total clusters evaluated |
Succeeded |
Clusters that succeeded |
Failed |
Clusters that failed |
InProgress |
Clusters still in progress |
SuccessPercent |
Calculated success rate |
FailurePercent |
Calculated failure rate |
Examples:
# Basic health gate check
Test-AzLocalFleetHealthGate -ScopeByUpdateRingTag -UpdateRingValue "Canary"
# Strict gate for production (max 2% failure, min 95% success)
Test-AzLocalFleetHealthGate -ScopeByUpdateRingTag -UpdateRingValue "Wave1" `
-MaxFailurePercent 2 -MinSuccessPercent 95
# Wait for completion before evaluating
Test-AzLocalFleetHealthGate -ScopeByUpdateRingTag -UpdateRingValue "Wave1" `
-WaitForCompletion -WaitTimeoutMinutes 180
# CI/CD pipeline integration
$gate = Test-AzLocalFleetHealthGate -ScopeByUpdateRingTag -UpdateRingValue "Wave1" `
-MaxFailurePercent 2 -WaitForCompletion
if (-not $gate.Passed) {
Write-Error "Health gate failed: $($gate.Reason)"
exit 1
}
# Proceed to Wave2...Sample Output (Passed):
========================================
Fleet Health Gate Check
========================================
Criteria: MaxFailure=5%, MinSuccess=90%
✓ HEALTH GATE PASSED
Success Rate: 97.3% (min: 90%)
Failure Rate: 2% (max: 5%)
Sample Output (Failed):
========================================
Fleet Health Gate Check
========================================
Criteria: MaxFailure=5%, MinSuccess=90%
✗ HEALTH GATE FAILED
- Failure rate (8%) exceeds maximum (5%)
- 2 cluster(s) have critical health failures
Success Rate: 85% (min: 90%)
Failure Rate: 8% (max: 5%)
Exports the current fleet operation state to a JSON file for resume capability, audit trails, and progress tracking across sessions.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-State |
PSCustomObject | No | - | State object to export. Uses current in-memory state if not provided. |
-Path |
String | No | Auto-generated | File path for the JSON state file |
Returns: The path where the state was saved.
State File Contents:
- Run ID and timestamps
- Operation configuration (batch size, throttle limit, etc.)
- Total/completed/failed/pending cluster counts
- Per-cluster status with attempt history and errors
Examples:
# Export current state to default location
Export-AzLocalFleetState
# Export to specific path
Export-AzLocalFleetState -Path "C:\Logs\fleet-state.json"
# Export state from operation
$state = Invoke-AzLocalFleetOperation -ScopeByUpdateRingTag -UpdateRingValue "Wave1" -PassThru
Export-AzLocalFleetState -State $state -Path "C:\Logs\wave1-checkpoint.json"Sample State File:
{
"RunId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"Operation": "ApplyUpdate",
"StartTime": "2026-01-29T10:00:00Z",
"TotalClusters": 150,
"CompletedCount": 75,
"SucceededCount": 73,
"FailedCount": 2,
"PendingCount": 75,
"BatchSize": 50,
"Clusters": [
{
"ClusterName": "Cluster001",
"ResourceId": "/subscriptions/.../clusters/Cluster001",
"Status": "Succeeded",
"Attempts": 1
},
...
]
}Resumes a previously interrupted fleet update operation from a saved state file. Enables recovery from pipeline timeouts, network interruptions, or manual cancellations.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-StateFilePath |
String | Yes* | - | Path to the saved state file |
-State |
PSCustomObject | Yes* | - | State object loaded via Import-AzLocalFleetState |
-RetryFailed |
Switch | No | - | Also retry clusters that previously failed (not just pending) |
-MaxRetries |
Int | No | 3 |
Maximum additional retry attempts for failed clusters (0-10) |
-Force |
Switch | No | - | Skip confirmation prompts |
-PassThru |
Switch | No | - | Return the updated state object |
*Either -StateFilePath OR -State is required.
Examples:
# Resume from state file (process only pending clusters)
Resume-AzLocalFleetUpdate -StateFilePath "C:\Logs\fleet-state.json" -Force
# Resume and retry failed clusters
Resume-AzLocalFleetUpdate -StateFilePath "C:\Logs\fleet-state.json" -RetryFailed -Force
# Load state manually and resume
$state = Import-AzLocalFleetState -Path "C:\Logs\fleet-state.json"
Resume-AzLocalFleetUpdate -State $state -RetryFailed -MaxRetries 5 -ForceSample Output:
========================================
Resuming Fleet Operation
Original Run ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
========================================
State Summary:
Pending: 75
Failed: 2
Succeeded: 73
Clusters to process: 77
Gracefully stops an in-progress fleet update operation after the current batch completes and saves state for later resumption.
Note: This function signals the operation to stop but does NOT cancel individual cluster updates that are already in progress. For emergency cancellation, use Azure Portal or
azCLI to cancel individual update runs.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-SaveState |
Switch | No | - | Save the current state to a file before stopping |
-StateFilePath |
String | No | Auto-generated | Path to save the state file |
Examples:
# Stop and save state
Stop-AzLocalFleetUpdate -SaveState
# Stop and save to specific location
Stop-AzLocalFleetUpdate -SaveState -StateFilePath "C:\Logs\fleet-stopped.json"Sample Output:
========================================
Stopping Fleet Operation
========================================
State saved to: C:\Logs\fleet-stopped.json
Use Resume-AzLocalFleetUpdate to continue later.
Operation Status at Stop:
Run ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Total: 150
Completed: 75
Succeeded: 73
Failed: 2
Pending: 75
Fleet operation marked for stop.
Note: Updates already in progress on individual clusters will continue.
Master gate that evaluates whether an update is allowed against the UpdateStartWindow (maintenance schedule) and UpdateExclusionsWindow (blackout periods; renamed from UpdateExclusions in v0.7.90) tag values. Exclusions take priority over windows. Returns a structured result with Allowed, Reason, WindowOpen, ExclusionActive, and Details. Used internally by Start-AzLocalClusterUpdate and exposed as a public function so pipelines can pre-flight a wave before triggering the apply step.
Fail-closed behaviour: malformed
UpdateStartWindow/UpdateExclusionsWindowtag values cause this function to throw rather than swallow - this is intentional so the calling apply path can block the update unless-Forceis supplied.
Note: the separate
UpdateExcludedtag (operator hard override, new in v0.7.90) is evaluated byStart-AzLocalClusterUpdatedirectly in a step BEFORE the schedule gate, not by this function. See theStart-AzLocalClusterUpdateentry for theExcludedByTagstatus.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-UpdateStartWindow |
String | No | (none) | The UpdateStartWindow tag value (e.g. Mon-Fri_22:00-02:00;Sat-Sun_02:00-06:00). Empty/null = no window restriction. |
-UpdateExclusionsWindow |
String | No | (none) | The UpdateExclusionsWindow tag value (e.g. 2026-12-20/2027-01-03;2027-04-05). Empty/null = no exclusion restriction. Renamed from -UpdateExclusions in v0.7.90. |
-TestTime |
DateTime | No | (Get-Date).ToUniversalTime() |
UTC time to test against. Local/Unspecified inputs are normalised to UTC automatically. |
Examples:
# Pre-flight a wave: would now be allowed?
$gate = Test-AzLocalUpdateScheduleAllowed `
-UpdateStartWindow 'Sat-Sun_02:00-06:00' `
-UpdateExclusionsWindow '2026-12-20/2027-01-03'
if (-not $gate.Allowed) {
Write-Host "Wave blocked: $($gate.Reason) - $($gate.Details)"
exit 1
}
# Test a specific UTC point in time (e.g. when the pipeline will run tonight)
Test-AzLocalUpdateScheduleAllowed `
-UpdateStartWindow 'Mon-Fri_22:00-02:00' `
-TestTime ([DateTime]::UtcNow.AddHours(6))Explicit, scope-required entry point for the same auto-reset logic that Get-AzLocalUpdateRuns runs by default. Use it to: (a) manually reset the sideloaded tags after an out-of-band update where Get-AzLocalUpdateRuns was not executed (or was run with -SkipSideloadedReset), or (b) force-clear a stuck UpdateSideloaded=True tag with -Force.
For each in-scope cluster the function fetches the latest update run and applies the same decision matrix as the auto-reset path - see the action-values table in section 7a for the full meaning of Reset / OrphanCleared / NoTag / RunNotSucceeded / Skipped. Scope must be explicit - there is no implicit -AllClusters. Supports -WhatIf / -Confirm (ConfirmImpact = Medium).
Parameter sets:
| Parameter set | Required | Use when |
|---|---|---|
ByName |
-ClusterNames <string[]> (+ optional -ResourceGroupName) |
Resetting one or a small list of named clusters. |
ByResourceId |
-ClusterResourceIds <string[]> |
Resetting by full ARM resource IDs (e.g. piped from another query). |
ByTag |
-ScopeByUpdateRingTag + -UpdateRingValue <string> |
Bulk reset across an UpdateRing (e.g. all of Wave1). |
Common parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-SubscriptionId |
String | No | Current az subscription |
Override subscription context. |
-ApiVersion |
String | No | Module default | ARM api-version for the cluster + tag PATCH calls. |
-Force |
Switch | No | - | Bypasses the UpdateVersionInProgress match check. Still requires the cluster's latest run state to be Succeeded - this prevents flipping the gate while an in-flight update is still running. |
Returns: PSCustomObject[] - one row per cluster with ClusterName, Action, PreviousSideloaded, NewSideloaded, StagedVersion, MatchedRunUpdateName, Message.
Examples:
# Inspect a single cluster (no changes)
Reset-AzLocalSideloadedTag -ClusterNames 'cl-01' -ResourceGroupName 'rg-fleet' -WhatIf
# Bulk reset across an UpdateRing, default behaviour: only succeeded runs get reset
Reset-AzLocalSideloadedTag -ScopeByUpdateRingTag -UpdateRingValue 'Wave1'
# Force-clear a stuck cluster (operator abandoned the staged payload).
# -Force still requires latest run = Succeeded; it just bypasses the version-match check.
Reset-AzLocalSideloadedTag -ClusterNames 'cl-stuck' -Force -Confirm:$falseNo new RBAC required. Uses the same
Microsoft.Resources/tags/read+Microsoft.Resources/tags/writealready required bySet-AzLocalClusterUpdateRingTag.
Single-pass data collector that gathers everything New-AzLocalFleetStatusHtmlReport needs, in one structured object. Returns a PSCustomObject with SchemaVersion, Timestamp, ModuleVersion, Scope, Readiness, ClusterDetails, LatestRuns, HealthResults. Use this when you want to:
- Decouple data collection from rendering (collect once in a CI/CD job, render in another).
- Pass data between pipeline stages as a JSON artifact (
-ExportPath). - Avoid the redundant API calls that happen if you call the readiness / inventory / health functions separately.
Honours -ThrottleLimit 1-8 for parallel collection; default 4.
Parameter sets: ByResourceId / ByName / ByTag / All (mirrors the other fleet functions).
Common parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
-IncludeUpdateRuns |
Switch | - | Collect latest update run history per cluster. |
-IncludeHealthDetails |
Switch | - | Collect detailed health-check failure data per cluster. |
-ThrottleLimit |
Int 1-8 | 4 | Parallel workers. Set 1 for sequential / debugging. |
-MaxClusters |
Int 0-100000 | 0 (no cap) | Optional cap when -AllClusters is used. |
-ExportPath |
String | - | JSON path for the artifact. |
Examples:
# One-step: collect + render
$data = Get-AzLocalFleetStatusData -AllClusters -ThrottleLimit 4 -IncludeUpdateRuns -IncludeHealthDetails
New-AzLocalFleetStatusHtmlReport -StatusData $data -OutputPath 'C:\Reports\fleet.html'
# CI/CD: collect in job 1, render in job 2 from the artifact
Get-AzLocalFleetStatusData -ScopeByUpdateRingTag -UpdateRingValue 'Wave1' `
-IncludeUpdateRuns -IncludeHealthDetails `
-ExportPath '$(Pipeline.Workspace)/fleet-data.json'Renders a self-contained HTML report (executive summary, progress bar, cluster status table, optional health-details and update-run-history sections, embedded CSS). UTF-8 without BOM; safe to email or host on SharePoint. Supports -WhatIf / -Confirm.
Two ways to drive it:
- Self-collecting (default): pass a scope (
-AllClusters/-ClusterNames/-ScopeByUpdateRingTag/-ClusterResourceIds) and the function will callGet-AzLocalClusterInventory,Get-AzLocalClusterUpdateReadiness,Get-AzLocalUpdateSummary,Get-AzLocalAvailableUpdates(and optionallyGet-AzLocalUpdateRuns/Test-AzLocalClusterHealth) itself. - From pre-collected data: pass
-StatusData $data(the object returned byGet-AzLocalFleetStatusData) and the function skips all API calls, going straight to rendering. Use this in CI/CD to avoid double-billing yourself for ARM reads.
Common parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-OutputPath |
String | Yes | - | Destination .html / .htm file. Validated. |
-StatusData |
PSCustomObject | No | - | Pre-collected payload from Get-AzLocalFleetStatusData. |
-IncludeUpdateRuns |
Switch | No | - | Add the Recent Update Run History section (now includes End Time column - v0.7.1). |
-IncludeHealthDetails |
Switch | No | - | Add the detailed health-check failure section. |
-Title |
String | No | Auto | Custom report title (auto-derived from scope if omitted). |
-MaxClusters |
Int 0-100000 | No | 0 (no cap) | Optional cap when -AllClusters is used. |
-ThrottleLimit |
Int 1-8 | No | 4 | Parallel workers (only relevant when self-collecting). |
-PassThru |
Switch | No | - | Also return the HTML string (useful for emailing). |
Examples:
# Whole fleet, full detail
New-AzLocalFleetStatusHtmlReport -AllClusters `
-OutputPath 'C:\Reports\fleet.html' `
-IncludeUpdateRuns -IncludeHealthDetails
# Wave-scoped, capture HTML for email body
$html = New-AzLocalFleetStatusHtmlReport `
-ScopeByUpdateRingTag -UpdateRingValue 'Wave1' `
-OutputPath 'C:\Reports\wave1.html' -PassThru
# Two-stage CI/CD pattern (no double API calls)
$data = Get-AzLocalFleetStatusData -AllClusters -IncludeUpdateRuns -IncludeHealthDetails
New-AzLocalFleetStatusHtmlReport -StatusData $data -OutputPath 'C:\Reports\fleet.html'Added in v0.7.65.
Surfaces the in-flight 24-hour system health-check failures across every Azure Local cluster the caller can read. The 24-hour system health checks continue to run on the cluster even when no update is in flight, which means clusters that are already "up to date" can still surface Critical or Warning health issues that administrators need to triage. This cmdlet is the dedicated entry point for that workflow.
Under the covers it executes a single Azure Resource Graph query against the extensibilityresources table (paging transparently for fleets larger than 1000 entries), mv-expands properties.healthCheckResult so each failing entry becomes its own row, and projects to a stable output schema.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-SubscriptionId |
String | No | All accessible | Optional. Limit the query to a single subscription. |
-Severity |
String | No | All |
Critical, Warning, or All (Critical + Warning). Informational entries are always excluded. |
-View |
String | No | Detail |
Detail (one row per (cluster, failing check)) or Summary (aggregated by FailureReason + Severity, ordered "most widespread first"). |
-UpdateRingTag |
String | No | - | Optional. Narrow to clusters whose UpdateRing tag matches. Accepts a single ring (Wave1), a semicolon-delimited list (Prod;Ring2), or the literal *** wildcard for every cluster with a non-empty UpdateRing tag (untagged clusters are excluded). Single *, double **, and quadruple **** are deliberately rejected. Validated ^(\*\*\*|[A-Za-z0-9_-]{1,64}(;[A-Za-z0-9_-]{1,64})*)$. |
-ExportPath |
String | No | - | Optional .csv or .json path; format auto-detected from extension. |
-PassThru |
Switch | No | - | Emit objects to the pipeline even when -ExportPath is used. |
Detail view columns: ClusterName, ResourceGroup, SubscriptionId, Severity, FailureReason, FailureName, Description, Remediation, LastOccurrence, ClusterResourceId. Rows are ordered Critical-before-Warning, then by ClusterName, then by FailureReason.
Summary view columns: FailureReason, Severity, ClusterCount, FailureCount, AffectedClusters (semicolon-separated), LatestOccurrence, Description, Remediation. Rows are ordered by ClusterCount desc, Critical-before-Warning, FailureCount desc so the highest-impact issues are at the top.
Examples:
# Per-cluster detail across the entire fleet (default view)
Get-AzLocalFleetHealthFailures
# Pivot by failure reason - "what should we fix first?" - and export
Get-AzLocalFleetHealthFailures -View Summary -ExportPath .\fleet-health-summary.csv
# Critical issues only, narrowed to one ring, for a focussed report
Get-AzLocalFleetHealthFailures -Severity Critical -UpdateRingTag 'Wave1'
# CI/CD pipeline: detail for JUnit emission, summary for the markdown summary
$detail = Get-AzLocalFleetHealthFailures -View Detail -ExportPath .\reports\fleet-health-detail.csv -PassThru
$summary = Get-AzLocalFleetHealthFailures -View Summary -ExportPath .\reports\fleet-health-summary.csv -PassThruCI/CD: the bundled
fleet-health-status.ymlpipeline samples (GitHub Actions and Azure DevOps) wire this cmdlet into a daily-scheduled run that emits JUnit XML, CSV/JSON exports, and a Markdown step summary. See Automation-Pipeline-Examples/README.md.
Required permissions (read-only):
Microsoft.AzureStackHCI/clusters/readMicrosoft.AzureStackHCI/clusters/updateSummaries/readMicrosoft.ResourceGraph/resources/read
Reader on the cluster (or the containing resource group / subscription) is sufficient.
Added in v0.7.65.
Read-only schedule-coverage advisor. Compares the cron schedule(s) declared in your apply-updates.yml pipeline (GitHub Actions and/or Azure DevOps) to the UpdateStartWindow tag values actually present on your clusters, and flags every (UpdateRing, UpdateStartWindow) pair that no cron in the pipeline will ever reach. Never edits cluster tags. Never edits pipeline YAML. It is the safety net that closes the loop between section 8 of Automation-Pipeline-Examples/README.md (the UpdateStartWindow tag is a gate, not a trigger) and Test-AzLocalUpdateScheduleAllowed (the runtime per-cluster gate inside Start-AzLocalClusterUpdate).
Under the covers it pre-scans the pipeline YAML file(s) with a regex (no powershell-yaml dependency), runs a single Azure Resource Graph query against resources for clusters with UpdateStartWindow / UpdateRing tags, parses each tag value with the same ConvertFrom-AzLocalUpdateWindow helper used by the runtime gate, then enumerates every cron fire time over a reference week and compares it to each parsed window (with a configurable lead-time buffer).
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-SubscriptionId |
String | No | All accessible | Optional. Limit the ARG query to a single subscription. |
-View |
String | No | Audit |
Audit (one row per (Ring, Window) pair with Covered / Uncovered / PartiallyCovered / MalformedTag / NoWindowTag / UnparseableCron status + remediation), Matrix (every distinct (Ring, Window) pair with the cron expression the advisor would generate for it), or Recommend (ready-to-paste GH Actions + Azure DevOps cron blocks). |
-PipelineYamlPath |
String | Audit only | - | Path to apply-updates.yml file(s) or a folder containing them. Required when -View Audit. |
-Platform |
String | No | Both |
GitHubActions, AzureDevOps, or Both. Filters which YAML files are scanned and which cron blocks the Recommend view emits. |
-LeadTimeMinutes |
Int | No | 5 |
Range 0-60. How many minutes the cron should fire before the window opens (so cluster enumeration + auth completes before Test-AzLocalUpdateScheduleAllowed evaluates). |
-UpdateRingTag |
String[] | No | - | Optional. Narrow the audit to one or more UpdateRing tag values. |
-IncludeUntagged |
Switch | No | - | Include clusters that have no UpdateStartWindow tag in the Audit view (Status = NoWindowTag). |
-ExportPath |
String | No | - | Optional .csv / .json / .md path; format auto-detected from extension. .md emits the YAML snippet for Recommend, a markdown table for Audit/Matrix. |
-PassThru |
Switch | No | - | Emit objects to the pipeline even when -ExportPath is used. |
Audit view columns: UpdateRing, UpdateStartWindow, ClusterCount, Status, Issue, Recommendation, MatchingCrons, RequiredCronUTC. Rows are ordered by Status (Uncovered first), then ClusterCount desc.
Matrix view columns: UpdateRing, UpdateStartWindow, ClusterCount, RequiredCronUTC, Segment, Days.
Recommend view columns: Platform, CronExpression, WindowsServed, ClustersServed, Comment.
Examples:
# Audit the in-repo pipeline samples against the live fleet (default view)
Test-AzLocalApplyUpdatesScheduleCoverage `
-PipelineYamlPath .\AzLocal.UpdateManagement\Automation-Pipeline-Examples
# Audit only the GitHub Actions sample, with a 10-minute lead time
Test-AzLocalApplyUpdatesScheduleCoverage `
-PipelineYamlPath .\.github\workflows\apply-updates.yml `
-Platform GitHubActions `
-LeadTimeMinutes 10
# Just emit the recommended cron block(s), no comparison required
Test-AzLocalApplyUpdatesScheduleCoverage -View Recommend -Platform GitHubActions
# Inventory every (Ring, Window) pair with its required cron, export to CSV
Test-AzLocalApplyUpdatesScheduleCoverage -View Matrix -ExportPath .\windows.csv
# CI/CD pipeline: emit all three artefacts for the schedule audit pipeline
$audit = Test-AzLocalApplyUpdatesScheduleCoverage -View Audit -PipelineYamlPath .\.github\workflows -ExportPath .\schedule-coverage-audit.csv -PassThru
$matrix = Test-AzLocalApplyUpdatesScheduleCoverage -View Matrix -ExportPath .\schedule-coverage-matrix.csv -PassThru
$rec = Test-AzLocalApplyUpdatesScheduleCoverage -View Recommend -ExportPath .\schedule-coverage-recommend.md -PassThruCI/CD: the bundled
apply-updates-schedule-audit.ymlpipeline samples (GitHub Actions and Azure DevOps) wire this cmdlet into a weekly-scheduled run (Mon 05:00 UTC) that emits JUnit XML, three CSV/MD exports, and a Markdown step summary. Full end-to-end runbook inAutomation-Pipeline-Examples/README.mdsection 8.3.
Required permissions (read-only):
Microsoft.Resources/subscriptions/resourceGroups/readMicrosoft.AzureStackHCI/clusters/readMicrosoft.ResourceGraph/resources/read
Reader on the cluster scope (or the containing resource group / subscription) is sufficient. No write actions are ever taken.
The module includes comprehensive logging capabilities for tracking update operations.
By default, log files are created in C:\ProgramData\AzLocal.UpdateManagement\ which is accessible across different user profiles. This folder is automatically created if it doesn't exist.
When you run Start-AzLocalClusterUpdate, the following files are automatically created:
| File | Description |
|---|---|
AzureLocalUpdate_YYYYMMDD_HHmmss.log |
Main log file with timestamped entries |
AzureLocalUpdate_YYYYMMDD_HHmmss_errors.log |
Separate error log (only created if errors occur) |
AzureLocalUpdate_YYYYMMDD_HHmmss_Update_Skipped.csv |
CSV of clusters where updates were skipped (not in Ready state) |
AzureLocalUpdate_YYYYMMDD_HHmmss_Update_Started.csv |
CSV of clusters where updates were successfully started |
AzureLocalUpdate_YYYYMMDD_HHmmss_transcript.log |
Full PowerShell transcript (if -EnableTranscript is used) |
The Update_Skipped.csv and Update_Started.csv files provide a quick summary for reporting.
| Column | Description |
|---|---|
ClusterName |
Name of the Azure Local cluster |
ResourceGroup |
Resource group containing the cluster |
SubscriptionId |
Azure subscription ID |
Message |
Status message (e.g., "Update Started: Solution12.2601.1002.38") |
The skipped CSV includes additional diagnostic columns to help understand why clusters were not updated:
| Column | Description |
|---|---|
ClusterName |
Name of the Azure Local cluster |
ResourceGroup |
Resource group containing the cluster |
SubscriptionId |
Azure subscription ID |
Message |
Status message explaining why the update was skipped |
UpdateState |
Current update state (e.g., "NeedsAttention", "UpdateFailed", "UpdateInProgress") |
HealthState |
Health check state: "Success", "Warning", "Failure", "InProgress", or "Unknown" |
HealthCheckFailures |
Summary of failed health checks with severity (e.g., "[Critical] StoragePool; [Warning] ClusterQuorum") |
LastUpdateErrorStep |
The specific step that failed in the last update run (if applicable) |
LastUpdateErrorMessage |
Error message from the last failed update step (truncated to 500 chars) |
This diagnostic information is sourced from:
healthCheckResultproperty in the cluster's updateSummaries resourceprogress.stepsproperty in the cluster's failed updateRuns (nested step traversal)
"ClusterName","ResourceGroup","SubscriptionId","Message","UpdateState","HealthState","HealthCheckFailures","LastUpdateErrorStep","LastUpdateErrorMessage"
"Cluster01","RG-West","12345-abcd","Update Not started as Cluster NOT in Ready state (Current state: NeedsAttention)","NeedsAttention","Failure","[Critical] Test-CauSetup; [Warning] Test-VMNetAdapter","DownloadSBE","Failed to download SBE package from storage account."
"Cluster02","RG-East","12345-abcd","Update Not started as Cluster NOT in Ready state (Current state: UpdateInProgress)","UpdateInProgress","Success","","",""
"Cluster03","RG-North","12345-abcd","Update skipped by user","UpdateAvailable","Success","","",""# Basic logging (logs created in default folder: C:\ProgramData\AzLocal.UpdateManagement\)
Start-AzLocalClusterUpdate -ClusterNames "MyCluster" -Force
# Custom log folder location (auto-creates folder if needed)
Start-AzLocalClusterUpdate -ClusterNames "MyCluster" -LogFolderPath "D:\Logs\Updates" -Force
# Enable transcript recording for complete console output capture
Start-AzLocalClusterUpdate -ClusterNames "MyCluster" -EnableTranscript -Force
# Export results to JSON for automation/reporting
Start-AzLocalClusterUpdate -ClusterNames @("Cluster01", "Cluster02") -ExportResultsPath "C:\Logs\results.json" -Force
# Export results to CSV for Excel analysis
Start-AzLocalClusterUpdate -ClusterNames @("Cluster01", "Cluster02") -ExportResultsPath "C:\Logs\results.csv" -Force
# Full logging with all options
Start-AzLocalClusterUpdate -ClusterNames @("Cluster01", "Cluster02") `
-LogFolderPath "D:\Logs\Updates" `
-EnableTranscript `
-ExportResultsPath "D:\Logs\Updates\results.json" `
-ForceEach log entry includes a timestamp and severity level:
[2026-01-28 14:30:45] [Info] Processing cluster: MyCluster01
[2026-01-28 14:30:46] [Success] Found cluster: /subscriptions/.../clusters/MyCluster01
[2026-01-28 14:30:47] [Warning] No updates in 'Ready' state for cluster 'MyCluster01'
[2026-01-28 14:30:48] [Error] Failed to start update on cluster 'MyCluster01'
JSON Export includes summary statistics:
{
"Timestamp": "2026-01-28 14:30:45",
"TotalClusters": 3,
"Succeeded": 2,
"Failed": 0,
"Skipped": 1,
"Results": [...]
}CSV Export includes one row per cluster with columns: ClusterName, Status, UpdateName, Duration, Message, StartTime, EndTime
The functions use the Azure Stack HCI REST API (version 2025-10-01):
| Operation | HTTP Method | Endpoint |
|---|---|---|
| List Clusters | GET | /subscriptions/{sub}/providers/Microsoft.AzureStackHCI/clusters |
| Get Cluster | GET | /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.AzureStackHCI/clusters/{cluster} |
| Get Update Summary | GET | ...clusters/{cluster}/updateSummaries/default |
| List Updates | GET | ...clusters/{cluster}/updates |
| Get Update | GET | ...clusters/{cluster}/updates/{updateName} |
| Apply Update | POST | ...clusters/{cluster}/updates/{updateName}/apply |
| List Update Runs | GET | ...clusters/{cluster}/updates/{updateName}/updateRuns |