Skip to content

Commit 0728b05

Browse files
zubairabidZubair AbidCopilot
authored
[Backup] az backup vault deleted-vault: Implementing List and Undelete for Deleted Backup Vaults (#32306)
Co-authored-by: Zubair Abid <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent c62f56b commit 0728b05

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+63927
-68101
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
6+
import json
7+
8+
from azure.cli.core.util import send_raw_request
9+
from azure.cli.core.azclierror import HTTPError, AzureResponseError
10+
11+
12+
class ARGClient: # pylint: disable=too-few-public-methods
13+
"""A lightweight Microsoft ARG API client.
14+
15+
For what ARG is, please see https://learn.microsoft.com/en-us/azure/governance/resource-graph/overview for details.
16+
The reason for directly using this client to request REST is that ARG API does not return "nextLink" data,
17+
so the Python SDK "azure-mgmt-resourcegraph" cannot support paging
18+
19+
"""
20+
21+
def __init__(self, cli_ctx):
22+
self._cli_ctx = cli_ctx
23+
24+
self._endpoint = cli_ctx.cloud.endpoints.resource_manager.rstrip('/')
25+
self._resource_provider_uri = 'providers/Microsoft.ResourceGraph/resources'
26+
self._api_version = '2021-03-01'
27+
self._method = 'post'
28+
29+
def send(self, query_body):
30+
url = f'{self._endpoint}/{self._resource_provider_uri}?api-version={self._api_version}'
31+
32+
if isinstance(query_body, QueryBody):
33+
# Serialize QueryBody object and ignore the None value
34+
query_body = json.dumps(query_body,
35+
default=lambda o: dict((key, value) for key, value in o.__dict__.items() if value))
36+
37+
try:
38+
response = send_raw_request(self._cli_ctx, self._method, url, body=query_body)
39+
except HTTPError as ex:
40+
raise AzureResponseError(ex.response.json()['error']['message'], ex.response) from ex
41+
# Other exceptions like AuthenticationError should not be handled here, so we don't catch CLIError
42+
43+
if response.text:
44+
return response.json()
45+
46+
return response
47+
48+
49+
class QueryBody: # pylint: disable=too-few-public-methods
50+
51+
def __init__(self, query, options=None):
52+
self.query = query
53+
self.options = options

src/azure-cli/azure/cli/command_modules/backup/_client_factory.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ def vaults_cf(cli_ctx, *_):
5757
return _common_client_factory(cli_ctx).vaults
5858

5959

60+
def deleted_vaults_cf(cli_ctx, *_):
61+
return _common_client_factory(cli_ctx).deleted_vaults
62+
63+
6064
def registered_identities_cf(cli_ctx, *_):
6165
return _common_client_factory(cli_ctx).registered_identities
6266

src/azure-cli/azure/cli/command_modules/backup/_help.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,3 +634,42 @@
634634
- name: Delete resource guard mapping of the Recovery Services vault.
635635
text: az backup vault resource-guard-mapping delete --resource-group MyResourceGroup --name MyVault
636636
"""
637+
638+
helps['backup deleted-vault'] = """
639+
type: group
640+
short-summary: Manage soft-deleted Recovery Services vaults.
641+
"""
642+
643+
helps['backup deleted-vault list'] = """
644+
type: command
645+
short-summary: List soft-deleted Recovery Services vaults.
646+
examples:
647+
- name: List soft-deleted vaults in a specific location under the active subscription.
648+
text: az backup deleted-vault list --location eastus
649+
"""
650+
651+
helps['backup deleted-vault get'] = """
652+
type: command
653+
short-summary: Get details of a soft-deleted Recovery Services vault.
654+
examples:
655+
- name: Get details of a soft-deleted vault by name and location.
656+
text: az backup deleted-vault get --location eastus --name deletedVaultName
657+
"""
658+
659+
helps['backup deleted-vault undelete'] = """
660+
type: command
661+
short-summary: Restore a soft-deleted Recovery Services vault.
662+
examples:
663+
- name: Restore a soft-deleted vault by name and location.
664+
text: az backup deleted-vault undelete --name MyVault --location eastus
665+
- name: Restore a soft-deleted vault using its ARM ID.
666+
text: az backup deleted-vault undelete --ids /subscriptions/{subscription-id}/locations/{location}/deletedVaults/{deleted-vault-name}
667+
"""
668+
669+
helps['backup deleted-vault list-containers'] = """
670+
type: command
671+
short-summary: List backup containers in a soft-deleted vault.
672+
examples:
673+
- name: List backup containers in a soft-deleted vault.
674+
text: az backup deleted-vault list-containers --name MyVault
675+
"""

src/azure-cli/azure/cli/command_modules/backup/_params.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def load_arguments(self, _):
108108
c.argument('azure_monitor_alerts_for_job_failures', options_list=['--job-failure-alerts'], arg_type=get_enum_type(enable_disable_options), help='Use this property to specify whether built-in Azure Monitor alerts should be received for every job failure.')
109109
c.argument('immutability_state', arg_type=get_enum_type(allowed_immutability_options), help='Use this parameter to configure immutability settings for the vault. By default, immutability is "Disabled" for the vault. "Unlocked" means that immutability is enabled for the vault and can be reversed. "Locked" means that immutability is enabled for the vault and cannot be reversed.')
110110
c.argument('cross_subscription_restore_state', arg_type=get_enum_type(enable_disable_permadisable_options), help='Use this parameter to configure cross subscription restore settings for the vault. By default, the property is "Enabled" for the vault.')
111-
# TODO Re-add once the new SDK is in place
111+
# TODO May add the soft_delete_retention_period_in_days parameter later. The other will not be exposed.
112112
# c.argument('soft_delete_state', options_list=['--soft-delete-state', '--soft-delete-feature-state'], arg_type=get_enum_type(allowed_softdelete_options), help='Set soft-delete feature state for a Recovery Services Vault.')
113113
# c.argument('soft_delete_retention_period_in_days', type=int, options_list=['--soft-delete-duration'], help='Set soft-delete retention duration time in days for a Recovery Services Vault.')
114114

@@ -119,7 +119,7 @@ def load_arguments(self, _):
119119
c.argument('azure_monitor_alerts_for_job_failures', options_list=['--job-failure-alerts'], arg_type=get_enum_type(enable_disable_options), help='Use this property to specify whether built-in Azure Monitor alerts should be received for every job failure.')
120120
c.argument('immutability_state', arg_type=get_enum_type(allowed_immutability_options), help='Use this parameter to configure immutability settings for the vault. By default, immutability is "Disabled" for the vault. "Unlocked" means that immutability is enabled for the vault and can be reversed. "Locked" means that immutability is enabled for the vault and cannot be reversed.')
121121
c.argument('cross_subscription_restore_state', arg_type=get_enum_type(enable_disable_permadisable_options), help='Use this parameter to configure cross subscription restore settings for the vault. By default, the property is "Enabled" for the vault.')
122-
# TODO Re-add once the new SDK is in place
122+
# TODO Discussion with Rishav once Enhanced Soft Delete is in place. We can only expose the latter, and might have to disable it from vaultconfig API
123123
# c.argument('soft_delete_state', options_list=['--soft-delete-state', '--soft-delete-feature-state'], arg_type=get_enum_type(allowed_softdelete_options), help='Set soft-delete feature state for a Recovery Services Vault.')
124124
# c.argument('soft_delete_retention_period_in_days', type=int, options_list=['--soft-delete-duration'], help='Set soft-delete retention duration time in days for a Recovery Services Vault.')
125125
c.argument('backup_storage_redundancy', arg_type=get_enum_type(['GeoRedundant', 'LocallyRedundant', 'ZoneRedundant']), help='Set backup storage properties for a Recovery Services vault.')
@@ -164,6 +164,15 @@ def load_arguments(self, _):
164164
with self.argument_context('backup vault encryption show') as c:
165165
c.argument('vault_name', vault_name_type, options_list=['--name', '-n'], id_part='name')
166166

167+
# Deleted Vault
168+
with self.argument_context('backup deleted-vault') as c:
169+
c.argument('location', help='Location of the deleted vault.')
170+
171+
for command in ['get', 'undelete', 'list-containers']:
172+
with self.argument_context('backup deleted-vault ' + command) as c:
173+
c.argument('deleted_vault_name', vault_name_type, options_list=['--name', '-n'], help='Name of the deleted vault.')
174+
c.argument('deleted_vault_id', options_list=['--ids', '--deleted-vault-id'], help='ID of the deleted vault.')
175+
167176
# Container
168177
with self.argument_context('backup container') as c:
169178
c.argument('vault_name', vault_name_type, id_part='name')
@@ -414,7 +423,7 @@ def load_arguments(self, _):
414423
c.argument('tenant_id', help='ID of the tenant if the Resource Guard protecting the vault exists in a different tenant.')
415424
c.argument('disk_access_option', arg_type=get_enum_type(allowed_disk_access_options), help='Specify the disk access option for target disks.')
416425
c.argument('target_disk_access_id', help='Specify the target disk access ID when --disk-access-option is set to EnablePrivateAccessForAllDisks')
417-
c.argument('cvm_os_des_id', options_list=['--cvm-os-des-id', '--cvm-os-disk-encryption-set-id'], help='Specify the Disk Encryption Set ID to use for OS disk encryption during restore of a Confidential VM. This is applicable only for Confidential VMs with managed disks. Please ensure that Disk Encryption Set has access to the Key vault.')
426+
c.argument('cvm_os_des_id', help='Disk encryption set ID for the OS disk of confidential VMs. This is used to encrypt the OS disk during restore.')
418427

419428
with self.argument_context('backup restore restore-azurefileshare') as c:
420429
c.argument('resolve_conflict', resolve_conflict_type)

src/azure-cli/azure/cli/command_modules/backup/commands.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
# --------------------------------------------------------------------------------------------
55

66
from azure.cli.core.commands import CliCommandType
7-
from azure.cli.command_modules.backup._client_factory import vaults_cf, backup_protection_containers_cf, \
8-
protection_policies_cf, backup_policies_cf, protected_items_cf, backups_cf, backup_jobs_cf, \
9-
job_details_cf, job_cancellations_cf, recovery_points_cf, restores_cf, backup_storage_configs_non_crr_cf, \
10-
item_level_recovery_connections_cf, backup_protected_items_cf, backup_protectable_items_cf, \
11-
protection_containers_cf, protection_intent_cf, backup_resource_encryption_config_cf, resource_guard_proxy_cf, \
12-
deleted_protection_containers_cf # pylint: disable=unused-variable
7+
from azure.cli.command_modules.backup._client_factory import (
8+
vaults_cf, deleted_vaults_cf, backup_protection_containers_cf, protection_policies_cf,
9+
backup_policies_cf, protected_items_cf, backups_cf, backup_jobs_cf, job_details_cf,
10+
job_cancellations_cf, recovery_points_cf, restores_cf, backup_storage_configs_non_crr_cf,
11+
item_level_recovery_connections_cf, backup_protected_items_cf, backup_protectable_items_cf,
12+
protection_containers_cf, protection_intent_cf, backup_resource_encryption_config_cf,
13+
resource_guard_proxy_cf, deleted_protection_containers_cf) # pylint: disable=unused-variable
1314
from azure.cli.command_modules.backup._exception_handler import backup_exception_handler
1415
from azure.cli.command_modules.backup._format import (
1516
transform_container_list, transform_policy_list, transform_item_list, transform_job_list,
@@ -43,6 +44,12 @@ def load_command_table(self, _):
4344
g.custom_command('encryption update', 'update_encryption')
4445
g.custom_command('encryption show', 'show_encryption', client_factory=backup_resource_encryption_config_cf)
4546

47+
with self.command_group('backup deleted-vault', backup_custom, client_factory=deleted_vaults_cf, exception_handler=backup_exception_handler) as g:
48+
g.custom_command('list', 'list_deleted_vaults')
49+
g.custom_command('get', 'get_deleted_vault')
50+
g.custom_command('undelete', 'undelete_vault')
51+
g.custom_command('list-containers', 'list_deleted_vault_containers')
52+
4653
with self.command_group('backup vault resource-guard-mapping', backup_custom, client_factory=resource_guard_proxy_cf, exception_handler=backup_exception_handler) as g:
4754
g.show_command('update', 'update_resource_guard_mapping')
4855
g.show_command('show', 'show_resource_guard_mapping')

0 commit comments

Comments
 (0)