Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -13,6 +13,7 @@ Pending
+++++++
* `az aks create` and `az aks nodepool add`: Add `--enable-osdisk-full-caching` (preview) to enable the full-cache ephemeral OS disk feature for a node pool. Requires AFEC registration `Microsoft.ContainerService/FullCachePreview`. Property is immutable after node pool creation.
* Clean up unused disk driver version constants and remove obsolete CSI driver v2 tests following the removal of `--disk-driver-version` in 21.0.0b1.
* `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-file` for bring-your-own vault/policy/storage. Requires the `dataprotection` CLI extension.

21.0.0b1
++++++
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 @@ -822,6 +822,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 @@ -1563,6 +1565,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
58 changes: 58 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 @@ -1279,6 +1280,35 @@ 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).",
)
c.argument(
"backup_strategy",
# NOTE: must mirror CONST_AKS_BACKUP_STRATEGIES in azext_dataprotection.manual._consts.
arg_type=get_enum_type(["Week", "Month", "DisasterRecovery", "Custom"]),
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 @@ -1918,6 +1948,34 @@ 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",
# NOTE: must mirror CONST_AKS_BACKUP_STRATEGIES in azext_dataprotection.manual._consts.
arg_type=get_enum_type(["Week", "Month", "DisasterRecovery", "Custom"]),
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 delete") as c:
c.argument("if_match")
Expand Down
52 changes: 52 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,52 @@
# --------------------------------------------------------------------------------------------
# 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 CLI extension.

The actual orchestration (vault, policy, storage account, extension install,
trusted access, role assignments, backup instance) lives in the
``dataprotection`` extension. This module is a thin shim that:

* loads that extension's path lazily (so ``az aks`` works without it),
* raises an actionable error if the extension is not installed,
* derives the AKS datasource ARM id from the resource group + cluster name.
"""

from azure.cli.core.azclierror import UnknownError
from azure.cli.core.commands.client_factory import get_subscription_id


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.

Raises ``UnknownError`` if the extension is not installed.
"""
try:
from azure.cli.core.extension.operations import add_extension_to_path
add_extension_to_path("dataprotection")
from azext_dataprotection.manual.aks.aks_helper import (
dataprotection_enable_backup_helper,
)
except ImportError:
raise UnknownError( # pylint: disable=raise-missing-from
"Please add CLI extension `dataprotection` to use --enable-backup with "
"'az aks create' / 'az aks update'.\n"
"Run command `az extension add --name dataprotection`."
)

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 @@ -1185,6 +1185,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 @@ -1439,6 +1443,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,
):
# DO NOT MOVE: get all the original parameters and save them as a dictionary
raw_parameters = locals()
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 @@ -5293,6 +5296,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 @@ -5302,7 +5306,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 @@ -5560,6 +5565,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 @@ -8191,10 +8207,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 @@ -8427,6 +8446,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