Skip to content

Commit

Permalink
[keyvault-preview] az keyvault region add/remove/list/wait: Support…
Browse files Browse the repository at this point in the history
… MHSM multi-region HA (#4131)

* create a new extension for keyvault preview features

* version 1.0.0 for GEO replication

* add `az keyvault region add/remove/list/wait`

* fix

* code owner

* change `--region-names` to `--region-name`

* refine help

* update service_name.json
  • Loading branch information
evelyn-ys authored Dec 5, 2022
1 parent f4033ee commit 86945a3
Show file tree
Hide file tree
Showing 54 changed files with 15,656 additions and 2 deletions.
6 changes: 4 additions & 2 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

/src/eventgrid/ @kalyanaj

/src/storage-preview/ @Juliehzl
/src/storage-preview/ @evelyn_ys @calvinhzy

/src/db-up/ @Juliehzl

Expand Down Expand Up @@ -106,7 +106,7 @@

/src/blueprint/ @fengzhou-msft

/src/storage-blob-preview/ @Juliehzl
/src/storage-blob-preview/ @evelyn_ys @calvinhzy

/src/logic/ @jsntcy @kairu

Expand Down Expand Up @@ -218,6 +218,8 @@

/src/image-gallery/ @zhoxing-ms

/src/keyvault-preview/ @evelyn_ys @calvinhzy

/src/init/ @zhoxing-ms @HuangYT2000

/src/datamigration/ @ashutoshsuman99
Expand Down
8 changes: 8 additions & 0 deletions src/keyvault-preview/HISTORY.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.. :changelog:
Release History
===============

1.0.0
++++++
* Support managed HSM Geo replication.
19 changes: 19 additions & 0 deletions src/keyvault-preview/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Microsoft Azure CLI 'keyvault-preview' Extension #

This package is for the 'keyvault-preview' extension.

## How to use ##

Install this extension using the below CLI command

```sh
az extension add --name keyvault-preview
```

### Included Features ###

#### Feature description ####

```sh
az keyvault XXX
```
43 changes: 43 additions & 0 deletions src/keyvault-preview/azext_keyvault_preview/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core import AzCommandsLoader
from azure.cli.core.profiles import register_resource_type
import azext_keyvault_preview._help # pylint: disable=unused-import
from .profiles import CUSTOM_MGMT_KEYVAULT


class KeyVaultCommandsLoader(AzCommandsLoader):

def __init__(self, cli_ctx=None):
from azure.cli.core.commands import CliCommandType
from ._client_factory import keyvault_mgmt_client_factory
from azure.cli.command_modules.keyvault._command_type import KeyVaultCommandGroup, KeyVaultArgumentContext

register_resource_type('latest', CUSTOM_MGMT_KEYVAULT, '2021-12-01-preview')

keyvault_custom = CliCommandType(
operations_tmpl='azext_keyvault_preview.custom#{}',
client_factory=keyvault_mgmt_client_factory
)

super(KeyVaultCommandsLoader, self).__init__(
cli_ctx=cli_ctx,
resource_type=CUSTOM_MGMT_KEYVAULT,
custom_command_type=keyvault_custom,
command_group_cls=KeyVaultCommandGroup,
argument_context_cls=KeyVaultArgumentContext)

def load_command_table(self, args):
from .commands import load_command_table
load_command_table(self, args)
return self.command_table

def load_arguments(self, command):
from ._params import load_arguments
load_arguments(self, command)


COMMAND_LOADER_CLS = KeyVaultCommandsLoader
31 changes: 31 additions & 0 deletions src/keyvault-preview/azext_keyvault_preview/_client_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from knack.util import CLIError
from .profiles import CUSTOM_MGMT_KEYVAULT


def keyvault_mgmt_client_factory(cli_ctx, **_):
from azure.cli.core.commands.client_factory import get_mgmt_service_client
return get_mgmt_service_client(cli_ctx, CUSTOM_MGMT_KEYVAULT)


def cf_mhsm(cli_ctx, _):
return keyvault_mgmt_client_factory(cli_ctx).managed_hsms


def cf_mhsm_region(cli_ctx, _):
return keyvault_mgmt_client_factory(cli_ctx).mhsm_regions


def is_azure_stack_profile(cmd=None, cli_ctx=None):
cli_ctx = cmd.cli_ctx if cmd else cli_ctx
if not cli_ctx:
raise CLIError("Can't judge profile without cli_ctx!")
return cli_ctx.cloud.profile in [
'2020-09-01-hybrid',
'2019-03-01-hybrid',
'2018-03-01-hybrid',
'2017-03-09-profile'
]
45 changes: 45 additions & 0 deletions src/keyvault-preview/azext_keyvault_preview/_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# coding=utf-8
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from knack.help_files import helps # pylint: disable=unused-import
# pylint: disable=line-too-long, too-many-lines

helps['keyvault region'] = """
type: group
short-summary: Manage MHSM multi-regions.
"""

helps['keyvault region list'] = """
type: command
short-summary: Get regions information associated with the managed HSM Pool.
"""

helps['keyvault region add'] = """
type: command
short-summary: Add regions for the managed HSM Pool.
examples:
- name: Add regions for the managed HSM.
text: |
az keyvault region add --region-name westus2 --hsm-name myhsm --resource-group myrg
"""

helps['keyvault region remove'] = """
type: command
short-summary: Remove regions for the managed HSM Pool.
examples:
- name: Remove regions for the managed HSM.
text: |
az keyvault region remove --region-name westus2 --hsm-name myhsm --resource-group myrg
"""

helps['keyvault region wait'] = """
type: command
short-summary: Place the CLI in a waiting state until a condition of the HSM is met.
examples:
- name: Pause CLI until the regions are updated.
text: |
az keyvault region wait --hsm-name myhsm --updated
"""
42 changes: 42 additions & 0 deletions src/keyvault-preview/azext_keyvault_preview/_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from knack.arguments import CLIArgumentType
from azure.cli.core.commands.parameters import get_enum_type, resource_group_name_type, get_three_state_flag
from ._validators import validate_resource_group_name
from .profiles import CUSTOM_MGMT_KEYVAULT


def load_arguments(self, _):
NetworkRuleBypassOptions, NetworkRuleAction = self.get_models(
'NetworkRuleBypassOptions', 'NetworkRuleAction',
resource_type=CUSTOM_MGMT_KEYVAULT)

hsm_name_type = CLIArgumentType(help='Name of the HSM.',
options_list=['--hsm-name'], id_part=None)

with self.argument_context('keyvault') as c:
c.argument('name', hsm_name_type)
c.argument('resource_group_name', resource_group_name_type, id_part=None, required=False,
help='Proceed only if Key Vault belongs to the specified resource group.',
validator=validate_resource_group_name)

with self.argument_context('keyvault', arg_group='Network Rule', min_api='2018-02-14') as c:
c.argument('bypass', arg_type=get_enum_type(NetworkRuleBypassOptions),
help='Bypass traffic for space-separated uses.')
c.argument('default_action', arg_type=get_enum_type(NetworkRuleAction),
help='Default action to apply when no rule matches.')

with self.argument_context('keyvault update-hsm') as c:
c.argument('enable_purge_protection', options_list=['--enable-purge-protection', '-e'],
arg_type=get_three_state_flag(),
help='Property specifying whether protection against purge is enabled for this managed HSM pool. '
'Setting this property to true activates protection against purge for this managed HSM pool '
'and its content - only the Managed HSM service may initiate a hard, irrecoverable deletion. '
'The setting is effective only if soft delete is also enabled. '
'Enabling this functionality is irreversible.')

with self.argument_context('keyvault region') as c:
c.argument('region_name', options_list=['--region-name', '--region', '-r'],
help='The region name.')
40 changes: 40 additions & 0 deletions src/keyvault-preview/azext_keyvault_preview/_validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from knack.util import CLIError
from azure.cli.command_modules.keyvault._validators import (
_show_vault_only_deprecate_message, _get_resource_group_from_resource_name
)


def validate_resource_group_name(cmd, ns):
"""
Populate resource_group_name, if not provided
"""
if 'keyvault purge' in cmd.name or 'keyvault recover' in cmd.name:
return

vault_name = getattr(ns, 'vault_name', None)
hsm_name = getattr(ns, 'hsm_name', None)
if 'keyvault update-hsm' in cmd.name or 'keyvault region':
hsm_name = getattr(ns, 'name', None)

if vault_name and hsm_name:
raise CLIError('--name/-n and --hsm-name are mutually exclusive.')

if vault_name:
# This is a temporary solution for showing deprecation message only for vaults
_show_vault_only_deprecate_message(ns)

if not ns.resource_group_name:
group_name = _get_resource_group_from_resource_name(cmd.cli_ctx, vault_name, hsm_name)
if group_name:
ns.resource_group_name = group_name
else:
if vault_name:
resource_type = 'Vault'
else:
resource_type = 'HSM'
msg = "The {} '{}' not found within subscription."
raise CLIError(msg.format(resource_type, vault_name if vault_name else hsm_name))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"azext.isPreview": true
}
41 changes: 41 additions & 0 deletions src/keyvault-preview/azext_keyvault_preview/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from azure.cli.core.commands import CliCommandType
from ._client_factory import is_azure_stack_profile, cf_mhsm, cf_mhsm_region
from .profiles import CUSTOM_MGMT_KEYVAULT


def load_command_table(self, _):
managed_hsm_sdk = CliCommandType(
operations_tmpl='azext_keyvault_preview.vendored_sdks.azure_mgmt_keyvault.operations#'
'ManagedHsmsOperations.{}',
client_factory=cf_mhsm,
resource_type=CUSTOM_MGMT_KEYVAULT
)
managed_hsm_custom = CliCommandType(
operations_tmpl='azext_keyvault_preview.custom#{}',
client_factory=cf_mhsm,
resource_type=CUSTOM_MGMT_KEYVAULT
)
mhsm_region_sdk = CliCommandType(
operations_tmpl='azext_keyvault_preview.vendored_sdks.azure_mgmt_keyvault.operations#'
'MHSMRegionsOperations.{}',
client_factory=cf_mhsm,
resource_type=CUSTOM_MGMT_KEYVAULT
)

with self.command_group('keyvault', managed_hsm_sdk, min_api='2021-12-01-preview') as g:
g.generic_update_command('update-hsm', custom_func_name='update_hsm', supports_no_wait=True,
setter_name='update_hsm_setter', setter_type=managed_hsm_custom)

with self.command_group('keyvault region', mhsm_region_sdk, client_factory=cf_mhsm_region,
min_api='2021-12-01-preview', is_preview=True) as g:
g.command('list', 'list_by_resource', client_factory=cf_mhsm_region)

with self.command_group('keyvault region', managed_hsm_sdk, client_factory=cf_mhsm,
min_api='2021-12-01-preview', is_preview=True) as g:
g.custom_command('add', 'add_hsm_region', supports_no_wait=True)
g.custom_command('remove', 'remove_hsm_region', supports_no_wait=True)
g.wait_command('wait')
79 changes: 79 additions & 0 deletions src/keyvault-preview/azext_keyvault_preview/custom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
from knack.log import get_logger
from azure.cli.core.util import sdk_no_wait
from .profiles import CUSTOM_MGMT_KEYVAULT

logger = get_logger(__name__)


def update_hsm_setter(cmd, client, parameters, resource_group_name, name, no_wait=False):
ManagedHsm = cmd.get_models('ManagedHsm', resource_type=CUSTOM_MGMT_KEYVAULT)
return sdk_no_wait(no_wait, client.begin_create_or_update,
resource_group_name=resource_group_name,
name=name,
parameters=ManagedHsm(
sku=parameters.sku,
tags=parameters.tags,
location=parameters.location,
properties=parameters.properties))


def update_hsm(cmd, instance,
enable_purge_protection=None,
bypass=None,
default_action=None):
if enable_purge_protection is not None:
instance.properties.enable_purge_protection = enable_purge_protection

if bypass or default_action and (hasattr(instance.properties, 'network_acls')):
if instance.properties.network_acls is None:
instance.properties.network_acls = _create_network_rule_set(cmd, bypass, default_action)
else:
if bypass:
instance.properties.network_acls.bypass = bypass
if default_action:
instance.properties.network_acls.default_action = default_action
return instance


def _create_network_rule_set(cmd, bypass=None, default_action=None):
NetworkRuleSet = cmd.get_models('NetworkRuleSet', resource_type=CUSTOM_MGMT_KEYVAULT)
NetworkRuleBypassOptions = cmd.get_models('NetworkRuleBypassOptions', resource_type=CUSTOM_MGMT_KEYVAULT)
NetworkRuleAction = cmd.get_models('NetworkRuleAction', resource_type=CUSTOM_MGMT_KEYVAULT)

return NetworkRuleSet(bypass=bypass or NetworkRuleBypassOptions.azure_services.value,
default_action=default_action or NetworkRuleAction.allow.value)


def add_hsm_region(cmd, client, resource_group_name, name, region_name, no_wait=False):
MHSMGeoReplicatedRegion = cmd.get_models('MHSMGeoReplicatedRegion', resource_type=CUSTOM_MGMT_KEYVAULT)

hsm = client.get(resource_group_name=resource_group_name, name=name)
existing_regions = hsm.properties.regions or []
for existing_region in existing_regions:
if region_name == existing_region.name:
logger.warning("{} has already existed".format(region_name))
return hsm
existing_regions.append(MHSMGeoReplicatedRegion(name=region_name))
hsm.properties.regions = existing_regions
return sdk_no_wait(no_wait, client.begin_update,
resource_group_name=resource_group_name,
name=name,
parameters=hsm)


def remove_hsm_region(client, resource_group_name, name, region_name, no_wait=False):
hsm = client.get(resource_group_name=resource_group_name, name=name)
existing_regions = hsm.properties.regions or []
for existing_region in existing_regions:
if region_name == existing_region.name:
existing_regions.remove(existing_region)
hsm.properties.regions = existing_regions
return sdk_no_wait(no_wait, client.begin_update,
resource_group_name=resource_group_name,
name=name, parameters=hsm)
logger.warning("{} doesn't exist".format(region_name))
return hsm
9 changes: 9 additions & 0 deletions src/keyvault-preview/azext_keyvault_preview/profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core.profiles import CustomResourceType

CUSTOM_MGMT_KEYVAULT = CustomResourceType('azext_keyvault_preview.vendored_sdks.azure_mgmt_keyvault',
'KeyVaultManagementClient')
Loading

0 comments on commit 86945a3

Please sign in to comment.