Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d9a3562
[aks-preview] Add --enable-backup to az aks create and az aks update
May 25, 2026
d1bb93b
[aks-preview] HISTORY: note --enable-backup under Pending
May 25, 2026
08bc672
[aks-preview] aks_backup: align continuation indent + silence too-man…
May 25, 2026
0b138f9
Merge branch 'main' into feat/aks-enable-backup
anshulahuja98 May 25, 2026
e2739c7
[aks-preview] _params: note backup-strategy enum mirrors dataprotection
May 25, 2026
237444b
[aks-preview] Address CI lint feedback
May 25, 2026
2ce8293
[aks-preview] Extract backup-strategy enum into a shared constant
May 26, 2026
938d597
[aks-preview] aks_backup: prompt to install dataprotection extension …
Jun 1, 2026
b70c8ae
[aks-preview] Add live tests for aks create/update --enable-backup
Jun 1, 2026
59b7722
Merge branch 'main' into feat/aks-enable-backup
anshulahuja98 Jun 1, 2026
cdf039a
[aks-preview] Fix error type and release notes for --enable-backup
Jun 1, 2026
1d6194a
[aks-preview][dataprotection] Auto-install dependent CLI extensions o…
Jun 1, 2026
61d86c3
[aks-preview] Fix HISTORY.rst: --backup-configuration-file -> --backu…
Jun 1, 2026
de88d3e
[aks-preview] Release enable-backup preview support
Jun 2, 2026
7bfd2c0
Merge remote-tracking branch 'upstream/main' into feat/aks-enable-backup
Jun 2, 2026
9e69b8a
[aks-preview] Fix flake8 indentation in backup parameter
Jun 2, 2026
5260826
[dataprotection] Remove redundant prompt import in AKS helper
Jun 2, 2026
0b4a133
Refactor docstring in aks_backup.py to simplify description of AKS ba…
Jun 3, 2026
3e7cf63
Merge remote-tracking branch 'upstream/main' into feat/aks-enable-backup
Jun 3, 2026
b0c335b
[aks-preview] Move pending notes into 21.0.0b4 release
Jun 3, 2026
2e020dd
[aks-preview] Remove dataprotection helper changes
Jun 3, 2026
bed0ec0
Merge branch 'main' into feat/aks-enable-backup
anshulahuja98 Jun 9, 2026
c047a50
Fix AKS backup storage account create payload
Jun 9, 2026
f31e690
Revert "Fix AKS backup storage account create payload"
Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/aks-preview/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ To release a new version, please select a new version number (usually plus 1 to
Pending
+++++++
* `az aks upgrade`: Fix `--node-image-only` to skip Machines mode agent pools, which do not support node image version upgrade.
* `az aks create` and `az aks update`: Add `--enable-backup` (preview) to configure Azure Backup for the AKS cluster in a single command. Supports `--backup-strategy` presets (Week, Month, DisasterRecovery, Custom) and an optional `--backup-configuration` for bring-your-own vault/policy/storage. Requires the `dataprotection` CLI extension.

21.0.0b4
+++++++
Expand Down
6 changes: 6 additions & 0 deletions src/aks-preview/azext_aks_preview/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,8 @@
text: az aks create -g MyResourceGroup -n MyManagedCluster --control-plane-scaling-size H4
- name: Create an automatic cluster with hosted system components enabled.
text: az aks create -g MyResourceGroup -n MyManagedCluster --sku automatic --enable-hosted-system
- name: Create a kubernetes cluster with Azure Backup enabled (default Week strategy). Requires the 'dataprotection' extension. Implicitly waits for cluster creation.
text: az aks create -g MyResourceGroup -n MyManagedCluster --generate-ssh-keys --enable-backup --yes

"""

Expand Down Expand Up @@ -1604,6 +1606,10 @@
text: az aks update -g MyResourceGroup -n MyManagedCluster --safeguards-level Warning --safeguards-excluded-ns ns1,ns2
- name: Enable Azure Monitor logs for a kubernetes cluster
text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-azure-monitor-logs
- name: Enable Azure Backup for a kubernetes cluster (default Week strategy). Requires the 'dataprotection' extension.
text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-backup --yes
- name: Enable Azure Backup with a custom strategy using an existing vault and policy
text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-backup --backup-strategy Custom --backup-configuration @config.json --yes
- name: Disable Azure Monitor logs for a kubernetes cluster
text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-azure-monitor-logs
- name: Update a kubernetes cluster to clear any namespaces excluded from safeguards. Assumes azure policy addon is already enabled
Expand Down
60 changes: 60 additions & 0 deletions src/aks-preview/azext_aks_preview/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
tags_type,
zones_type,
)
from azure.cli.core.commands.validators import validate_file_or_dict
from azext_aks_preview._validators import (
validate_nat_gateway_managed_outbound_ipv6_count,
validate_nat_gateway_v2_params,
Expand Down Expand Up @@ -565,6 +566,10 @@
CONST_UPGRADE_STRATEGY_BLUE_GREEN,
]

# AKS backup strategy presets exposed by --backup-strategy.
# NOTE: must mirror CONST_AKS_BACKUP_STRATEGIES in azext_dataprotection.manual._consts.
aks_backup_strategies = ["Week", "Month", "DisasterRecovery", "Custom"]

node_disruption_policies = [
CONST_NODE_DISRUPTION_POLICY_ALLOW,
CONST_NODE_DISRUPTION_POLICY_BLOCK,
Expand Down Expand Up @@ -1298,6 +1303,34 @@ def load_arguments(self, _):
is_preview=True,
help="Enable continuous control plane and addon monitor for the cluster.",
)
# Backup (delegates to the dataprotection extension)
c.argument(
"enable_backup",
action="store_true",
is_preview=True,
help="Enable Azure Backup for this AKS cluster. Orchestrates the same flow as "
"'az dataprotection enable-backup trigger' (requires the 'dataprotection' extension). "
"Implicitly waits for cluster creation to complete (ignores --no-wait).",
Comment thread
anshulahuja98 marked this conversation as resolved.
)
c.argument(
"backup_strategy",
arg_type=get_enum_type(aks_backup_strategies),
is_preview=True,
help="Backup strategy preset. Week (default, 7-day operational retention), Month "
"(30-day operational retention), DisasterRecovery (7-day operational + 90-day vault "
"retention), Custom (bring your own vault and policy via --backup-configuration). "
"Only valid with --enable-backup.",
)
c.argument(
"backup_configuration_file",
options_list=["--backup-configuration"],
type=validate_file_or_dict,
is_preview=True,
help="Backup configuration as inline JSON string or @file.json. "
"Supports storageAccountResourceId, blobContainerName, backupResourceGroupId, "
"backupVaultId, backupPolicyId, tags. backupVaultId and backupPolicyId are required "
"for Custom strategy. Only valid with --enable-backup.",
)

with self.argument_context("aks update") as c:
# managed cluster paramerters
Expand Down Expand Up @@ -1939,6 +1972,33 @@ def load_arguments(self, _):
is_preview=True,
help="Disable continuous control plane and addon monitor for the cluster.",
)
# Backup (delegates to the dataprotection extension)
c.argument(
"enable_backup",
action="store_true",
is_preview=True,
help="Enable Azure Backup for this AKS cluster. Orchestrates the same flow as "
"'az dataprotection enable-backup trigger' (requires the 'dataprotection' extension).",
)
c.argument(
"backup_strategy",
arg_type=get_enum_type(aks_backup_strategies),
is_preview=True,
help="Backup strategy preset. Week (default, 7-day operational retention), Month "
"(30-day operational retention), DisasterRecovery (7-day operational + 90-day vault "
"retention), Custom (bring your own vault and policy via --backup-configuration). "
"Only valid with --enable-backup.",
)
c.argument(
"backup_configuration_file",
options_list=["--backup-configuration"],
type=validate_file_or_dict,
is_preview=True,
help="Backup configuration as inline JSON string or @file.json. "
"Supports storageAccountResourceId, blobContainerName, backupResourceGroupId, "
"backupVaultId, backupPolicyId, tags. backupVaultId and backupPolicyId are required "
"for Custom strategy. Only valid with --enable-backup.",
)
c.argument(
"control_plane_scaling_size",
options_list=["--control-plane-scaling-size", "--cp-scaling-size"],
Expand Down
84 changes: 84 additions & 0 deletions src/aks-preview/azext_aks_preview/aks_backup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

"""Helpers that delegate AKS backup enablement to the dataprotection extension."""

from knack.log import get_logger
from knack.prompting import prompt_y_n, NoTTYException

from azure.cli.core.azclierror import RequiredArgumentMissingError

DATAPROTECTION_EXTENSION_NAME = "dataprotection"

logger = get_logger(__name__)


def _ensure_dataprotection_extension(cmd, yes):
"""Make ``azext_dataprotection.manual.aks.aks_helper`` importable.

If the ``dataprotection`` extension is not installed, prompt the user to
install it (or install silently when ``yes`` is True). Raises
``RequiredArgumentMissingError`` if the user declines or the install fails.
"""
from azure.cli.core.extension.operations import add_extension_to_path

try:
add_extension_to_path(DATAPROTECTION_EXTENSION_NAME)
from azext_dataprotection.manual.aks.aks_helper import ( # pylint: disable=unused-import,import-error
dataprotection_enable_backup_helper,
)
return
except Exception: # pylint: disable=broad-except
pass

install_msg = (
f"The '{DATAPROTECTION_EXTENSION_NAME}' extension is required for "
"--enable-backup but is not installed. Install it now?"
)
proceed = yes
if not proceed:
try:
proceed = prompt_y_n(install_msg, default="y")
except NoTTYException:
proceed = False
if not proceed:
raise RequiredArgumentMissingError(
f"The '{DATAPROTECTION_EXTENSION_NAME}' extension is required for "
"--enable-backup with 'az aks create' / 'az aks update'.\n"
f"Run `az extension add --name {DATAPROTECTION_EXTENSION_NAME}` "
"and retry, or rerun with --yes to auto-install."
)
Comment thread
anshulahuja98 marked this conversation as resolved.

logger.warning("Installing extension '%s'...", DATAPROTECTION_EXTENSION_NAME)
from azure.cli.core.extension.operations import add_extension
add_extension(cmd=cmd, extension_name=DATAPROTECTION_EXTENSION_NAME)
add_extension_to_path(DATAPROTECTION_EXTENSION_NAME)


def enable_aks_backup(cmd, resource_group_name, cluster_name, # pylint: disable=too-many-positional-arguments
backup_strategy, backup_configuration_file, yes):
"""Enable Azure Backup for an AKS cluster by delegating to the
``dataprotection`` extension.
"""
from azure.cli.core.commands.client_factory import get_subscription_id

_ensure_dataprotection_extension(cmd, yes)

from azext_dataprotection.manual.aks.aks_helper import ( # pylint: disable=import-error
dataprotection_enable_backup_helper,
)

subscription_id = get_subscription_id(cmd.cli_ctx)
datasource_id = (
f"/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}"
f"/providers/Microsoft.ContainerService/managedClusters/{cluster_name}"
)
dataprotection_enable_backup_helper(
cmd,
datasource_id,
backup_strategy or "Week",
backup_configuration_file or {},
yes=yes,
)
8 changes: 8 additions & 0 deletions src/aks-preview/azext_aks_preview/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,10 @@ def aks_create(
control_plane_scaling_size=None,
# health monitor
enable_continuous_control_plane_and_addon_monitor=False,
# backup (delegates to the dataprotection extension)
enable_backup=False,
backup_strategy=None,
backup_configuration_file=None,
):
# DO NOT MOVE: get all the original parameters and save them as a dictionary
raw_parameters = locals()
Expand Down Expand Up @@ -1440,6 +1444,10 @@ def aks_update(
# health monitor
enable_continuous_control_plane_and_addon_monitor=False,
disable_continuous_control_plane_and_addon_monitor=False,
# backup (delegates to the dataprotection extension)
enable_backup=False,
backup_strategy=None,
backup_configuration_file=None,
# node disruption policy
node_disruption_policy=None,
# control plane scaling
Expand Down
34 changes: 32 additions & 2 deletions src/aks-preview/azext_aks_preview/managed_cluster_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ def external_functions(self) -> SimpleNamespace:
external_functions["ensure_container_insights_for_monitoring"] = (
ensure_container_insights_for_monitoring_preview
)
# AKS backup (delegates to the dataprotection extension)
from azext_aks_preview.aks_backup import enable_aks_backup
external_functions["enable_aks_backup"] = enable_aks_backup
self.__external_functions = SimpleNamespace(**external_functions)
return self.__external_functions

Expand Down Expand Up @@ -5366,6 +5369,7 @@ def check_is_postprocessing_required(self, mc: ManagedCluster) -> bool:
"enable_azure_container_storage",
default_value=False
)
enable_backup = self.context.raw_param.get("enable_backup", False)

# pylint: disable=too-many-boolean-expressions
if (
Expand All @@ -5375,7 +5379,8 @@ def check_is_postprocessing_required(self, mc: ManagedCluster) -> bool:
azuremonitormetrics_addon_enabled or
(enable_managed_identity and attach_acr) or
need_grant_vnet_permission_to_cluster_identity or
enable_azure_container_storage
enable_azure_container_storage or
enable_backup
):
return True
return False
Expand Down Expand Up @@ -5633,6 +5638,17 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None:
resolve_assignee=False,
)

# Enable Azure Backup for the AKS cluster (delegates to dataprotection extension)
if self.context.raw_param.get("enable_backup", False):
self.context.external_functions.enable_aks_backup(
self.cmd,
self.context.get_resource_group_name(),
self.context.get_name(),
self.context.raw_param.get("backup_strategy"),
self.context.raw_param.get("backup_configuration_file"),
self.context.raw_param.get("yes", False),
)

def put_mc(self, mc: ManagedCluster) -> ManagedCluster:
etag, match_condition = _get_etag_match_condition(
self.context.get_if_match(), self.context.get_if_none_match()
Expand Down Expand Up @@ -8334,10 +8350,13 @@ def check_is_postprocessing_required(self, mc: ManagedCluster) -> bool:
monitoring_addon_postprocessing_required = self.context.get_intermediate(
"monitoring_addon_postprocessing_required", default_value=False
)
enable_backup = self.context.raw_param.get("enable_backup", False)
# Note: monitoring_addon_disable_postprocessing_required is no longer used - cleanup is done upfront
# pylint: disable=too-many-boolean-expressions
if (enable_azure_container_storage or disable_azure_container_storage) or \
(keyvault_id and enable_azure_keyvault_secrets_provider_addon) or \
(monitoring_addon_postprocessing_required):
(monitoring_addon_postprocessing_required) or \
enable_backup:
return True
return postprocessing_required

Expand Down Expand Up @@ -8570,6 +8589,17 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None:
else:
raise CLIError('Keyvault secrets provider addon must be enabled to attach keyvault.\n')

# Enable Azure Backup for the AKS cluster (delegates to dataprotection extension)
if self.context.raw_param.get("enable_backup", False):
self.context.external_functions.enable_aks_backup(
self.cmd,
self.context.get_resource_group_name(),
self.context.get_name(),
self.context.raw_param.get("backup_strategy"),
self.context.raw_param.get("backup_configuration_file"),
self.context.raw_param.get("yes", False),
)

def put_mc(self, mc: ManagedCluster) -> ManagedCluster:
etag, match_condition = _get_etag_match_condition(
self.context.get_if_match(), self.context.get_if_none_match()
Expand Down
Loading
Loading