From 34ded560e86b40df3dbbe2431f2dad97ba979f54 Mon Sep 17 00:00:00 2001 From: katriendg Date: Wed, 1 Jul 2026 11:17:46 +0000 Subject: [PATCH 1/2] feat(iot-ops): upgrade AIO component versions and extend version checker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - bump aio 1.3.105→1.3.137, secret-store 1.4.1→1.5.0, cert-manager 0.12.0→0.13.3 - extend aio-version-checker.py to extract arc-extension (cert-manager) versions - cache Bicep file reads and track per-component local_file in checker - update iotops-version-upgrade prompt and upgrade-aio docs ⬆️ - Generated by Copilot --- .../prompts/iotops-version-upgrade.prompt.md | 43 ++++- docs/getting-started/upgrade-aio.md | 8 +- scripts/aio-version-checker.py | 147 ++++++++++++++++-- .../109-arc-extensions/bicep/types.bicep | 2 +- .../109-arc-extensions/terraform/README.md | 2 +- .../109-arc-extensions/terraform/variables.tf | 2 +- src/100-edge/110-iot-ops/bicep/types.bicep | 4 +- src/100-edge/110-iot-ops/terraform/README.md | 4 +- .../110-iot-ops/terraform/variables.init.tf | 2 +- .../terraform/variables.instance.tf | 2 +- 10 files changed, 188 insertions(+), 28 deletions(-) diff --git a/.github/prompts/iotops-version-upgrade.prompt.md b/.github/prompts/iotops-version-upgrade.prompt.md index f73c6168c..dd344f4f4 100644 --- a/.github/prompts/iotops-version-upgrade.prompt.md +++ b/.github/prompts/iotops-version-upgrade.prompt.md @@ -17,10 +17,23 @@ Use runSubagent tool for complex steps in this prompt like executing scripts, fe ### Components Analyzed by This Prompt This prompt analyzes and updates the following IoT Operations-related components: -- **110-iot-ops**: Core IoT Operations instance, brokers, authentication, and listeners +- **109-arc-extensions**: Arc dependency extensions — `cert-manager` (`certManager`) and container storage. cert-manager is tracked against the AIO manifest; container storage is **not** currently published in the manifest, so leave it unless the manifest reintroduces it. +- **110-iot-ops**: Core IoT Operations instance, brokers, authentication, and listeners — includes the AIO instance version (`iotOperations`) and the Secret Store extension (`secretStore`). - **111-assets**: Azure Device Registry assets and endpoint profiles - **130-messaging**: Dataflow endpoints, profiles, and messaging integration +### Component-to-Manifest Version Map + +The AIO manifests publish component versions under `variables.VERSIONS`/`variables.TRAINS`. These map to codebase variables as follows (kept in sync by `scripts/aio-version-checker.py`): + +| Manifest key | Manifest file | Terraform | Bicep | +|---|---|---|---| +| `certManager` | enablement | `109-arc-extensions/terraform/variables.tf` (`arc_extensions.cert_manager_extension.version`) | `109-arc-extensions/bicep/types.bicep` (`certManagerExtensionDefaults`) | +| `secretStore` | enablement | `110-iot-ops/terraform/variables.init.tf` (`secret_sync_controller`) | `110-iot-ops/bicep/types.bicep` (`secretStoreExtensionDefaults`) | +| `iotOperations` | instance | `110-iot-ops/terraform/variables.instance.tf` (`operations_config`) | `110-iot-ops/bicep/types.bicep` (`aioExtensionDefaults`) | + +If a new manifest key appears (e.g. container storage returns, or a new dependency extension is added), add it to both the codebase and the `aio-version-checker.py` mappings (`TERRAFORM_COMPONENTS`, `BICEP_COMPONENTS`, `BICEP_COMPONENT_FILES`, and any dedicated extractor) so future CI catches drift. + ### Workflow Overview The prompt follows a three-phase approach: @@ -42,7 +55,7 @@ These steps must be completed immediately to gather all necessary information be ### Execution Requirements for Phase 1: - **Sequential Execution**: Complete steps 1-6 in order before proceeding to Phase 2 -- **Component Coverage**: Analyze ALL three components (110-iot-ops, 111-assets, 130-messaging) +- **Component Coverage**: Analyze ALL four components (109-arc-extensions, 110-iot-ops, 111-assets, 130-messaging) - **API Validation**: Cross-validate with REST specifications to catch breaking changes - **Structural Comparison**: Detect ALL differences between JSON and codebase (new, removed, changed) - **Complete Analysis**: Do not skip to planning until all immediate analysis is complete @@ -313,6 +326,27 @@ Check the following files within the `src/100-edge/110-iot-ops/` component: Before moving to the next step: update the plan file. +## 4.5. Arc Extensions Component Analysis (109-arc-extensions) - EXECUTE IMMEDIATELY + +**EXECUTE IMMEDIATELY**: The `cert-manager` (`certManager`) dependency extension lives in `109-arc-extensions`, not `110-iot-ops`. Container storage also lives here but is currently **not** published in the enablement manifest. + +**Analysis Process**: +1. Read the current cert-manager and container storage versions: + - Terraform: `src/100-edge/109-arc-extensions/terraform/variables.tf` (`arc_extensions.cert_manager_extension.version` and `container_storage_extension.version`) + - Bicep: `src/100-edge/109-arc-extensions/bicep/types.bicep` (`certManagerExtensionDefaults`, `containerStorageExtensionDefaults`) +2. Compare the enablement manifest `variables.VERSIONS.certManager` against the codebase cert-manager version. Plan a bump (both Terraform and Bicep) when they differ. +3. For container storage: only plan a change if the manifest reintroduces a container storage key. Otherwise leave it as-is and note this in the plan. +4. Confirm no structural changes — these are version-default-only variables. + + +```markdown +- [ ] Update `certManagerExtensionDefaults.release.version` from "" to "" in `src/100-edge/109-arc-extensions/bicep/types.bicep` (manifest `certManager`). +- [ ] Update `arc_extensions.cert_manager_extension.version` default from "" to "" in `src/100-edge/109-arc-extensions/terraform/variables.tf` (manifest `certManager`). +``` + + +Before moving to the next step: update the plan file. + ## 5. Assets Component Analysis (111-assets) - EXECUTE IMMEDIATELY **EXECUTE IMMEDIATELY**: Extend the same API version analysis to the Assets module to detect any API changes: @@ -349,11 +383,12 @@ Before moving to the next step: update the plan file. - ✅ Target version configuration downloaded - ✅ REST API specifications cross-validated - ✅ Core IoT Operations component (110-iot-ops) analyzed +- ✅ Arc extensions component (109-arc-extensions) analyzed (cert-manager) - ✅ Assets component (111-assets) analyzed - ✅ Messaging component (130-messaging) analyzed **CRITICAL**: Do not proceed to Phase 2 until ALL analysis is complete. The following are common execution pitfalls to avoid: -- ❌ **Don't skip component analysis**: All three components must be analyzed, not just 110-iot-ops +- ❌ **Don't skip component analysis**: All four components must be analyzed (109-arc-extensions, 110-iot-ops, 111-assets, 130-messaging), not just 110-iot-ops - ❌ **Don't defer REST validation**: API specifications must be validated immediately, not marked as "future work" - ❌ **Don't mix analysis with planning**: Complete all discovery before creating implementation plans @@ -679,6 +714,8 @@ Plan validation steps to ensure comprehensive coverage of changes beyond the JSO ```markdown - [ ] VALIDATE: Test all blueprints that use `110-iot-ops` component deploy successfully. - [ ] VALIDATE: Verify CI tests cover new parameters and resource types. +- [ ] VALIDATE: Run `python3 scripts/aio-version-checker.py --release-tag ` and confirm it returns `[]` (no mismatches) for both Terraform and Bicep across 109-arc-extensions and 110-iot-ops. If the checker misses a component you changed (e.g. a newly added manifest key), update its mappings so future CI catches drift. +- [ ] VALIDATE: Update `docs/getting-started/upgrade-aio.md` — set the target AIO release, and the version-matrix row (compatible `azure-iot-ops` CLI version, cert-manager, secret-sync-controller, iotOperations) and `ms.date`. Source the CLI↔AIO↔component mapping from the [IoT Operations versions wiki](https://github.com/Azure/azure-iot-ops-cli-extension/wiki/IoT-Operations-versions). ``` Before moving to the next step: update the plan file. diff --git a/docs/getting-started/upgrade-aio.md b/docs/getting-started/upgrade-aio.md index 486064d18..1bb69b118 100644 --- a/docs/getting-started/upgrade-aio.md +++ b/docs/getting-started/upgrade-aio.md @@ -2,7 +2,7 @@ title: Upgrade Azure IoT Operations description: How to upgrade Azure IoT Operations (AIO) and reconcile the upgrade with the edge-ai Terraform or Bicep deployments author: Edge AI Team -ms.date: 2026-05-05 +ms.date: 2026-07-01 ms.topic: how-to estimated_reading_time: 5 keywords: @@ -28,18 +28,18 @@ The reconciliation steps differ between Terraform (stateful) and Bicep (stateles ## Version matrix -This repository currently targets the **AIO 2605** release. The table below maps `az iot ops` CLI versions to the component versions pinned in edge-ai: +This repository currently targets the **AIO 2606** release. The table below maps `az iot ops` CLI versions to the component versions pinned in edge-ai: | CLI extension (`azure-iot-ops`) | AIO release | cert-manager | secret-sync-controller | iotOperations | |---------------------------------|-------------|--------------|------------------------|---------------| -| 2.5.0 | 2605 | 0.12.0 | 1.4.1 | 1.3.105 | +| 2.7.0 | 2606 | 0.13.3 | 1.5.0 | 1.3.137 | For the full upstream compatibility matrix, see [Supported versions — Azure IoT Operations](https://learn.microsoft.com/azure/iot-operations/deploy-iot-ops/howto-upgrade?tabs=portal#supported-versions). ## Prerequisites - Azure CLI logged in to the target subscription. -- `azure-iot-ops` CLI extension installed (this repo expects version `2.5.0`). +- `azure-iot-ops` CLI extension installed (this repo expects version `2.7.0`). - `` — the resource group containing the AIO instance. - `` — the AIO instance name (for edge-ai blueprints this is typically `iotops-arck---`). diff --git a/scripts/aio-version-checker.py b/scripts/aio-version-checker.py index 66f1318c8..aa6015697 100644 --- a/scripts/aio-version-checker.py +++ b/scripts/aio-version-checker.py @@ -98,7 +98,12 @@ TERRAFORM_VARS_INSTANCE_FILE = ( "./src/100-edge/110-iot-ops/terraform/variables.instance.tf" ) +# cert-manager and container storage extensions live in the 109-arc-extensions component +TERRAFORM_ARC_EXTENSIONS_FILE = ( + "./src/100-edge/109-arc-extensions/terraform/variables.tf" +) BICEP_VARS_FILE = "./src/100-edge/110-iot-ops/bicep/types.bicep" +BICEP_ARC_EXTENSIONS_FILE = "./src/100-edge/109-arc-extensions/bicep/types.bicep" # IaC type definition (discriminated union) IaCType = Literal["terraform", "bicep"] @@ -112,11 +117,17 @@ # Component mappings for Bicep (bicep_name:remote_name) BICEP_COMPONENTS = [ - "aioCertManagerExtensionDefaults:certManager", + "certManagerExtensionDefaults:certManager", "secretStoreExtensionDefaults:secretStore", "aioExtensionDefaults:iotOperations", # Maps to iotOperations in manifest ] +# Bicep variables whose declaration lives outside BICEP_VARS_FILE (110-iot-ops). +# Maps the bicep variable name to the file that declares it. +BICEP_COMPONENT_FILES = { + "certManagerExtensionDefaults": BICEP_ARC_EXTENSIONS_FILE, +} + def parse_args() -> argparse.Namespace: """ @@ -400,6 +411,26 @@ def download_manifests( return manifest_data +def _unwrap_hcl(value: Any) -> Any: + """ + Normalize a value parsed by python-hcl2. + + Recent python-hcl2 releases wrap block bodies (such as a variable's + ``default``) in a single-element list, e.g. ``[{...}]`` instead of ``{...}``. + This helper unwraps that list so callers can treat the result as a dict. + + Args: + value (Any): A value produced by ``hcl2.load``. + + Returns: + Any: The inner dict when ``value`` is a single-element list wrapping a + dict; otherwise ``value`` unchanged. + """ + if isinstance(value, list) and len(value) == 1: + return value[0] + return value + + def extract_tf_variables(tf_file: str) -> list[dict[str, str]]: """ Extract component information from the Terraform variables file using HCL2 parser. @@ -451,7 +482,7 @@ def extract_tf_variables(tf_file: str) -> list[dict[str, str]]: # Check if default exists and contains version/train if isinstance(var_props, dict) and "default" in var_props: - defaults = var_props["default"] + defaults = _unwrap_hcl(var_props["default"]) if isinstance(defaults, dict): version = defaults.get("version", "") train = defaults.get("train", "") @@ -462,6 +493,7 @@ def extract_tf_variables(tf_file: str) -> list[dict[str, str]]: "name": var_name, "version": version, "train": train, + "local_file": tf_file, } ) @@ -510,7 +542,7 @@ def extract_tf_instance_variables(tf_instance_file: str) -> list[dict[str, str]] ops_config = var_item["operations_config"] if isinstance(ops_config, dict) and "default" in ops_config: - defaults = ops_config["default"] + defaults = _unwrap_hcl(ops_config["default"]) if isinstance(defaults, dict): version = defaults.get("version", "") @@ -524,6 +556,7 @@ def extract_tf_instance_variables(tf_instance_file: str) -> list[dict[str, str]] "version": version, "train": train, "namespace": namespace, + "local_file": tf_instance_file, } ) break @@ -531,6 +564,78 @@ def extract_tf_instance_variables(tf_instance_file: str) -> list[dict[str, str]] return variable_blocks +def extract_tf_arc_extension_variables( + tf_arc_file: str, +) -> list[dict[str, str]]: + """ + Extract cert-manager version info from the 109-arc-extensions Terraform variables. + + The arc extensions component nests extension settings inside the + ``arc_extensions`` variable default, e.g.:: + + variable "arc_extensions" { + default = { + cert_manager_extension = { + version = "0.13.3" + train = "stable" + } + } + } + + Only cert-manager is tracked against the AIO manifest; container storage is + intentionally excluded because the manifest no longer publishes a version + for it. + + Args: + tf_arc_file (str): Path to the 109-arc-extensions Terraform variables file. + + Returns: + List[Dict[str, str]]: A list with a single ``cert_manager`` entry when a + version/train is found; otherwise an empty list. + """ + logger.debug(f"Reading Terraform arc-extensions file: {tf_arc_file}") + + try: + with open(tf_arc_file) as f: + parsed = hcl2.load(f) + except OSError as e: + logger.error(f"Failed to read Terraform arc-extensions file: {e}") + return [] + except Exception as e: + logger.error(f"Failed to parse Terraform arc-extensions file: {e}") + sys.exit(1) + + for var_item in parsed.get("variable", []): + if not (isinstance(var_item, dict) and "arc_extensions" in var_item): + continue + + arc_props = var_item["arc_extensions"] + if not (isinstance(arc_props, dict) and "default" in arc_props): + continue + + defaults = _unwrap_hcl(arc_props["default"]) + if not isinstance(defaults, dict): + continue + + cert_manager = _unwrap_hcl(defaults.get("cert_manager_extension", {})) + if not isinstance(cert_manager, dict): + continue + + version = cert_manager.get("version", "") + train = cert_manager.get("train", "") + if version or train: + return [ + { + "name": "cert_manager", + "version": version, + "train": train, + "local_file": tf_arc_file, + } + ] + + return [] + + def extract_bicep_variables(bicep_file: str) -> list[dict[str, str]]: """ Extract component information from the Bicep file using regex pattern matching. @@ -560,12 +665,18 @@ def extract_bicep_variables(bicep_file: str) -> list[dict[str, str]]: """ logger.debug(f"Reading Bicep variables file: {bicep_file}") - try: - with open(bicep_file) as f: - content = f.read() - except OSError as e: - logger.error(f"Failed to read Bicep file: {e}") - sys.exit(1) + # Cache file contents so each file is read at most once. + file_cache: dict[str, str] = {} + + def _read(path: str) -> str: + if path not in file_cache: + try: + with open(path) as f: + file_cache[path] = f.read() + except OSError as e: + logger.error(f"Failed to read Bicep file: {e}") + sys.exit(1) + return file_cache[path] variable_blocks = [] @@ -573,6 +684,11 @@ def extract_bicep_variables(bicep_file: str) -> list[dict[str, str]]: for bicep_component in BICEP_COMPONENTS: bicep_name, remote_name = bicep_component.split(":") + # Some components (e.g. cert-manager) are declared outside the default + # 110-iot-ops types file. + component_file = BICEP_COMPONENT_FILES.get(bicep_name, bicep_file) + content = _read(component_file) + # Find the component definition block var_pattern = f"var {bicep_name} = {{([\\s\\S]*?)}}" var_match = re.search(var_pattern, content) @@ -612,7 +728,12 @@ def extract_bicep_variables(bicep_file: str) -> list[dict[str, str]]: component_name = "azure-iot-operations" variable_blocks.append( - {"name": component_name, "version": version, "train": train} + { + "name": component_name, + "version": version, + "train": train, + "local_file": component_file, + } ) return variable_blocks @@ -641,7 +762,9 @@ def extract_variables(iac_type: IaCType, file_path: str) -> list[dict[str, str]] variables = extract_tf_variables(TERRAFORM_VARS_FILE) instance_variables = extract_tf_instance_variables( TERRAFORM_VARS_INSTANCE_FILE) - return variables + instance_variables + arc_variables = extract_tf_arc_extension_variables( + TERRAFORM_ARC_EXTENSIONS_FILE) + return variables + instance_variables + arc_variables elif iac_type == "bicep": return extract_bicep_variables(file_path) else: @@ -785,7 +908,7 @@ def compare_versions( mismatches.append( { "name": name, - "local_file": file_path, + "local_file": component.get("local_file", file_path), "remote_url": manifest_url, "local_version": local_version, "remote_version": remote_version, diff --git a/src/100-edge/109-arc-extensions/bicep/types.bicep b/src/100-edge/109-arc-extensions/bicep/types.bicep index 505c83a2e..e60b07605 100644 --- a/src/100-edge/109-arc-extensions/bicep/types.bicep +++ b/src/100-edge/109-arc-extensions/bicep/types.bicep @@ -39,7 +39,7 @@ type CertManagerExtension = { var certManagerExtensionDefaults = { enabled: true release: { - version: '0.12.0' + version: '0.13.3' train: 'stable' autoUpgradeMinorVersion: false } diff --git a/src/100-edge/109-arc-extensions/terraform/README.md b/src/100-edge/109-arc-extensions/terraform/README.md index 1b45aca55..4180fc8e4 100644 --- a/src/100-edge/109-arc-extensions/terraform/README.md +++ b/src/100-edge/109-arc-extensions/terraform/README.md @@ -23,7 +23,7 @@ cert-manager and Azure Container Storage (ACSA). | Name | Description | Type | Default | Required | |-------------------------|---------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:| | arc\_connected\_cluster | Arc-connected Kubernetes cluster object containing id, name, and location | ```object({ id = string name = string location = string })``` | n/a | yes | -| arc\_extensions | Combined configuration object for Arc extensions (cert-manager and container storage) | ```object({ cert_manager_extension = optional(object({ enabled = optional(bool) version = optional(string) train = optional(string) auto_upgrade_minor_version = optional(bool) agent_operation_timeout_in_minutes = optional(number) global_telemetry_enabled = optional(bool) })) container_storage_extension = optional(object({ enabled = optional(bool) version = optional(string) train = optional(string) auto_upgrade_minor_version = optional(bool) disk_storage_class = optional(string) fault_tolerance_enabled = optional(bool) disk_mount_point = optional(string) })) })``` | ```{ "cert_manager_extension": { "agent_operation_timeout_in_minutes": 20, "auto_upgrade_minor_version": false, "enabled": true, "global_telemetry_enabled": true, "train": "stable", "version": "0.12.0" }, "container_storage_extension": { "auto_upgrade_minor_version": false, "disk_mount_point": "/mnt", "disk_storage_class": "", "enabled": true, "fault_tolerance_enabled": false, "train": "stable", "version": "2.6.0" } }``` | no | +| arc\_extensions | Combined configuration object for Arc extensions (cert-manager and container storage) | ```object({ cert_manager_extension = optional(object({ enabled = optional(bool) version = optional(string) train = optional(string) auto_upgrade_minor_version = optional(bool) agent_operation_timeout_in_minutes = optional(number) global_telemetry_enabled = optional(bool) })) container_storage_extension = optional(object({ enabled = optional(bool) version = optional(string) train = optional(string) auto_upgrade_minor_version = optional(bool) disk_storage_class = optional(string) fault_tolerance_enabled = optional(bool) disk_mount_point = optional(string) })) })``` | ```{ "cert_manager_extension": { "agent_operation_timeout_in_minutes": 20, "auto_upgrade_minor_version": false, "enabled": true, "global_telemetry_enabled": true, "train": "stable", "version": "0.13.3" }, "container_storage_extension": { "auto_upgrade_minor_version": false, "disk_mount_point": "/mnt", "disk_storage_class": "", "enabled": true, "fault_tolerance_enabled": false, "train": "stable", "version": "2.6.0" } }``` | no | ## Outputs diff --git a/src/100-edge/109-arc-extensions/terraform/variables.tf b/src/100-edge/109-arc-extensions/terraform/variables.tf index 262eaa19a..8bb353c2f 100644 --- a/src/100-edge/109-arc-extensions/terraform/variables.tf +++ b/src/100-edge/109-arc-extensions/terraform/variables.tf @@ -22,7 +22,7 @@ variable "arc_extensions" { default = { cert_manager_extension = { enabled = true - version = "0.12.0" + version = "0.13.3" train = "stable" auto_upgrade_minor_version = false agent_operation_timeout_in_minutes = 20 diff --git a/src/100-edge/110-iot-ops/bicep/types.bicep b/src/100-edge/110-iot-ops/bicep/types.bicep index fd4d2c436..915a71a5d 100644 --- a/src/100-edge/110-iot-ops/bicep/types.bicep +++ b/src/100-edge/110-iot-ops/bicep/types.bicep @@ -27,7 +27,7 @@ type SecretStoreExtension = { @export() var secretStoreExtensionDefaults = { release: { - version: '1.4.1' + version: '1.5.0' train: 'stable' } } @@ -53,7 +53,7 @@ type AioExtension = { @export() var aioExtensionDefaults = { release: { - version: '1.3.105' + version: '1.3.137' train: 'stable' } settings: { diff --git a/src/100-edge/110-iot-ops/terraform/README.md b/src/100-edge/110-iot-ops/terraform/README.md index a8a063135..dfbd46f52 100644 --- a/src/100-edge/110-iot-ops/terraform/README.md +++ b/src/100-edge/110-iot-ops/terraform/README.md @@ -54,9 +54,9 @@ Instance can be created, and after. | mqtt\_broker\_diagnostics\_config | Extended broker diagnostics configuration for metrics, self-check, and distributed tracing | ```object({ metrics = optional(object({ prometheus_port = optional(number) })) self_check = optional(object({ mode = optional(string) interval_seconds = optional(number) timeout_seconds = optional(number) })) traces = optional(object({ mode = optional(string) cache_size_megabytes = optional(number) span_channel_capacity = optional(number) self_tracing = optional(object({ mode = optional(string) interval_seconds = optional(number) })) })) })``` | `null` | no | | mqtt\_broker\_disk\_buffer\_config | Disk-backed message buffer configuration for broker in-memory overflow to disk | ```object({ max_size = string ephemeral_volume_claim_spec = optional(object({ storage_class_name = optional(string) access_modes = optional(list(string)) volume_mode = optional(string) volume_name = optional(string) resources = optional(object({ requests = optional(map(string)) limits = optional(map(string)) })) data_source = optional(object({ api_group = optional(string) kind = string name = string })) data_source_ref = optional(object({ api_group = optional(string) kind = string name = string namespace = optional(string) })) selector = optional(object({ match_labels = optional(map(string)) match_expressions = optional(list(object({ key = string operator = string values = list(string) }))) })) })) persistent_volume_claim_spec = optional(object({ storage_class_name = optional(string) access_modes = optional(list(string)) volume_mode = optional(string) volume_name = optional(string) resources = optional(object({ requests = optional(map(string)) limits = optional(map(string)) })) data_source = optional(object({ api_group = optional(string) kind = string name = string })) data_source_ref = optional(object({ api_group = optional(string) kind = string name = string namespace = optional(string) })) selector = optional(object({ match_labels = optional(map(string)) match_expressions = optional(list(object({ key = string operator = string values = list(string) }))) })) })) })``` | `null` | no | | mqtt\_broker\_persistence\_config | Broker persistence configuration for disk-backed message storage | ```object({ max_size = string encryption_enabled = optional(bool) # Retention Policy retain_policy = optional(object({ mode = string # "All", "None", "Custom" custom_settings = optional(object({ topics = optional(list(string)) dynamic_enabled = optional(bool) })) })) # State Store Policy state_store_policy = optional(object({ mode = string # "All", "None", "Custom" custom_settings = optional(object({ state_store_resources = optional(list(object({ key_type = string # "Pattern", "String", "Binary" keys = list(string) }))) dynamic_enabled = optional(bool) })) })) # Subscriber Queue Policy subscriber_queue_policy = optional(object({ mode = string # "All", "None", "Custom" custom_settings = optional(object({ subscriber_client_ids = optional(list(string)) dynamic_enabled = optional(bool) })) })) # Persistent Volume Claim Specification persistent_volume_claim_spec = optional(object({ storage_class_name = optional(string) access_modes = optional(list(string)) volume_mode = optional(string) volume_name = optional(string) resources = optional(object({ requests = optional(map(string)) limits = optional(map(string)) })) data_source = optional(object({ api_group = optional(string) kind = string name = string })) data_source_ref = optional(object({ api_group = optional(string) kind = string name = string namespace = optional(string) })) selector = optional(object({ match_labels = optional(map(string)) match_expressions = optional(list(object({ key = string operator = string values = list(string) }))) })) })) })``` | `null` | no | -| operations\_config | n/a | ```object({ namespace = string kubernetesDistro = string version = string train = string agentOperationTimeoutInMinutes = number })``` | ```{ "agentOperationTimeoutInMinutes": 120, "kubernetesDistro": "K3s", "namespace": "azure-iot-operations", "train": "stable", "version": "1.3.105" }``` | no | +| operations\_config | n/a | ```object({ namespace = string kubernetesDistro = string version = string train = string agentOperationTimeoutInMinutes = number })``` | ```{ "agentOperationTimeoutInMinutes": 120, "kubernetesDistro": "K3s", "namespace": "azure-iot-operations", "train": "stable", "version": "1.3.137" }``` | no | | registry\_endpoints | List of additional container registry endpoints for pulling custom artifacts (WASM modules, graph definitions, connector templates). MCR (mcr.microsoft.com) is always added automatically with anonymous authentication. The `acr_resource_id` field enables automatic AcrPull role assignment for ACR endpoints using SystemAssignedManagedIdentity authentication. When `should_assign_acr_pull_for_aio` is true and `acr_resource_id` is provided, the AIO extension's identity will be granted AcrPull access to the specified ACR. | ```list(object({ name = string host = string acr_resource_id = optional(string) should_assign_acr_pull_for_aio = optional(bool, false) authentication = object({ method = string system_assigned_managed_identity_settings = optional(object({ audience = optional(string, "https://management.azure.com/") })) user_assigned_managed_identity_settings = optional(object({ client_id = string tenant_id = string scope = optional(string) })) artifact_pull_secret_settings = optional(object({ secret_ref = string })) }) }))``` | `[]` | no | -| secret\_sync\_controller | n/a | ```object({ version = string train = string })``` | ```{ "train": "stable", "version": "1.4.1" }``` | no | +| secret\_sync\_controller | n/a | ```object({ version = string train = string })``` | ```{ "train": "stable", "version": "1.5.0" }``` | no | | should\_assign\_key\_vault\_roles | Whether to assign Key Vault roles to provided Secret Sync identity. | `bool` | `true` | no | | should\_create\_anonymous\_broker\_listener | Whether to enable an insecure anonymous AIO MQ Broker Listener. Should only be used for dev or test environments | `bool` | `false` | no | | should\_deploy\_resource\_sync\_rules | Deploys resource sync rules if set to true | `bool` | `false` | no | diff --git a/src/100-edge/110-iot-ops/terraform/variables.init.tf b/src/100-edge/110-iot-ops/terraform/variables.init.tf index 3c59edc26..cd9fc3337 100644 --- a/src/100-edge/110-iot-ops/terraform/variables.init.tf +++ b/src/100-edge/110-iot-ops/terraform/variables.init.tf @@ -13,7 +13,7 @@ variable "secret_sync_controller" { train = string }) default = { - version = "1.4.1" + version = "1.5.0" train = "stable" } } diff --git a/src/100-edge/110-iot-ops/terraform/variables.instance.tf b/src/100-edge/110-iot-ops/terraform/variables.instance.tf index eba74b814..32432d59b 100644 --- a/src/100-edge/110-iot-ops/terraform/variables.instance.tf +++ b/src/100-edge/110-iot-ops/terraform/variables.instance.tf @@ -18,7 +18,7 @@ variable "operations_config" { default = { namespace = "azure-iot-operations" kubernetesDistro = "K3s" - version = "1.3.105" + version = "1.3.137" train = "stable" agentOperationTimeoutInMinutes = 120 } From dcea87aac59751dea0c3b9fd61aaab9f29d5a480 Mon Sep 17 00:00:00 2001 From: katriendg Date: Wed, 1 Jul 2026 12:13:00 +0000 Subject: [PATCH 2/2] chore: linting --- .github/prompts/iotops-version-upgrade.prompt.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/prompts/iotops-version-upgrade.prompt.md b/.github/prompts/iotops-version-upgrade.prompt.md index dd344f4f4..86bc61af8 100644 --- a/.github/prompts/iotops-version-upgrade.prompt.md +++ b/.github/prompts/iotops-version-upgrade.prompt.md @@ -26,11 +26,11 @@ This prompt analyzes and updates the following IoT Operations-related components The AIO manifests publish component versions under `variables.VERSIONS`/`variables.TRAINS`. These map to codebase variables as follows (kept in sync by `scripts/aio-version-checker.py`): -| Manifest key | Manifest file | Terraform | Bicep | -|---|---|---|---| -| `certManager` | enablement | `109-arc-extensions/terraform/variables.tf` (`arc_extensions.cert_manager_extension.version`) | `109-arc-extensions/bicep/types.bicep` (`certManagerExtensionDefaults`) | -| `secretStore` | enablement | `110-iot-ops/terraform/variables.init.tf` (`secret_sync_controller`) | `110-iot-ops/bicep/types.bicep` (`secretStoreExtensionDefaults`) | -| `iotOperations` | instance | `110-iot-ops/terraform/variables.instance.tf` (`operations_config`) | `110-iot-ops/bicep/types.bicep` (`aioExtensionDefaults`) | +| Manifest key | Manifest file | Terraform | Bicep | +|-----------------|---------------|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------| +| `certManager` | enablement | `109-arc-extensions/terraform/variables.tf` (`arc_extensions.cert_manager_extension.version`) | `109-arc-extensions/bicep/types.bicep` (`certManagerExtensionDefaults`) | +| `secretStore` | enablement | `110-iot-ops/terraform/variables.init.tf` (`secret_sync_controller`) | `110-iot-ops/bicep/types.bicep` (`secretStoreExtensionDefaults`) | +| `iotOperations` | instance | `110-iot-ops/terraform/variables.instance.tf` (`operations_config`) | `110-iot-ops/bicep/types.bicep` (`aioExtensionDefaults`) | If a new manifest key appears (e.g. container storage returns, or a new dependency extension is added), add it to both the codebase and the `aio-version-checker.py` mappings (`TERRAFORM_COMPONENTS`, `BICEP_COMPONENTS`, `BICEP_COMPONENT_FILES`, and any dedicated extractor) so future CI catches drift.