Skip to content
Merged
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
6 changes: 4 additions & 2 deletions src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ResourceType(Enum): # pylint: disable=too-few-public-methods
DATA_PRIVATE_KEYVAULT = ('azure.cli.command_modules.keyvault.vendored_sdks.azure_keyvault_t1', 'KeyVaultClient')
DATA_KEYVAULT_ADMINISTRATION_BACKUP = ('azure.keyvault.administration', 'KeyVaultBackupClient')
DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL = ('azure.keyvault.administration', 'KeyVaultAccessControlClient')
DATA_KEYVAULT_ADMINISTRATION_SETTING = ('azure.keyvault.administration', 'KeyVaultSettingsClient')
MGMT_EVENTHUB = ('azure.mgmt.eventhub', 'EventHubManagementClient')
MGMT_SERVICEBUS = ('azure.mgmt.servicebus', 'ServiceBusManagementClient')
MGMT_APPSERVICE = ('azure.mgmt.web', 'WebSiteManagementClient')
Expand Down Expand Up @@ -199,10 +200,11 @@ def default_api_version(self):
# The order does make things different.
# Please keep ResourceType.DATA_KEYVAULT_KEYS before ResourceType.DATA_KEYVAULT
ResourceType.DATA_KEYVAULT_KEYS: None,
ResourceType.DATA_KEYVAULT_ADMINISTRATION_SETTING: None,
ResourceType.DATA_KEYVAULT: '7.0',
ResourceType.DATA_PRIVATE_KEYVAULT: '7.2',
ResourceType.DATA_KEYVAULT_ADMINISTRATION_BACKUP: '7.2-preview',
ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL: '7.2-preview',
ResourceType.DATA_KEYVAULT_ADMINISTRATION_BACKUP: '7.4',
ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL: '7.4',
ResourceType.DATA_STORAGE: '2018-11-09',
ResourceType.DATA_STORAGE_BLOB: '2021-06-08',
ResourceType.DATA_STORAGE_FILEDATALAKE: '2021-08-06',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class Clients(str, Enum):
'azure.keyvault.administration._backup_client#KeyVaultBackupClient{obj_name}',
ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL:
'azure.keyvault.administration._access_control_client#KeyVaultAccessControlClient{obj_name}',
ResourceType.DATA_KEYVAULT_ADMINISTRATION_SETTING:
'azure.keyvault.administration._settings_client#KeyVaultSettingsClient{obj_name}',
ResourceType.DATA_KEYVAULT_KEYS:
'azure.keyvault.keys._client#KeyClient{obj_name}',
}
Expand All @@ -59,7 +61,8 @@ def is_mgmt_plane(resource_type):
def get_operations_tmpl(resource_type, client_name):
if resource_type in [ResourceType.DATA_KEYVAULT_ADMINISTRATION_BACKUP,
ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL,
ResourceType.DATA_KEYVAULT_KEYS]:
ResourceType.DATA_KEYVAULT_KEYS,
ResourceType.DATA_KEYVAULT_ADMINISTRATION_SETTING]:
return KEYVAULT_TEMPLATE_STRINGS[resource_type].format(obj_name='.{}')

class_name = OPERATIONS_NAME.get(client_name, '') if is_mgmt_plane(resource_type) else 'KeyVaultClient'
Expand All @@ -73,7 +76,8 @@ def get_operations_tmpl(resource_type, client_name):
def get_docs_tmpl(cli_ctx, resource_type, client_name, module_name='operations'):
if resource_type in [ResourceType.DATA_KEYVAULT_ADMINISTRATION_BACKUP,
ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL,
ResourceType.DATA_KEYVAULT_KEYS]:
ResourceType.DATA_KEYVAULT_KEYS,
ResourceType.DATA_KEYVAULT_ADMINISTRATION_SETTING]:
return KEYVAULT_TEMPLATE_STRINGS[resource_type].format(obj_name='.{}')

api_version = get_api_version(cli_ctx, resource_type, as_sdk_profile=True)
Expand All @@ -93,6 +97,7 @@ def get_docs_tmpl(cli_ctx, resource_type, client_name, module_name='operations')
obj_name='{}')


# pylint: disable=too-many-return-statements
def get_client_factory(resource_type, client_name=''):
if is_mgmt_plane(resource_type):
return keyvault_mgmt_client_factory(resource_type, client_name)
Expand All @@ -104,6 +109,8 @@ def get_client_factory(resource_type, client_name=''):
return data_plane_azure_keyvault_administration_backup_client
if resource_type == ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL:
return data_plane_azure_keyvault_administration_access_control_client
if resource_type == ResourceType.DATA_KEYVAULT_ADMINISTRATION_SETTING:
return data_plane_azure_keyvault_administration_setting_client
if resource_type == ResourceType.DATA_KEYVAULT_KEYS:
return data_plane_azure_keyvault_key_client
raise CLIError('Unsupported resource type: {}'.format(resource_type))
Expand Down Expand Up @@ -216,6 +223,18 @@ def data_plane_azure_keyvault_administration_access_control_client(cli_ctx, comm
vault_url=vault_url, credential=credential, api_version=version)


def data_plane_azure_keyvault_administration_setting_client(cli_ctx, command_args):
from azure.keyvault.administration import KeyVaultSettingsClient

vault_url, credential, _ = _prepare_data_plane_azure_keyvault_client(
cli_ctx, command_args, ResourceType.DATA_KEYVAULT_ADMINISTRATION_SETTING)
command_args.pop('hsm_name', None)
command_args.pop('vault_base_url', None)
command_args.pop('identifier', None)
return KeyVaultSettingsClient(
vault_url=vault_url, credential=credential, api_version='7.4')


def data_plane_azure_keyvault_key_client(cli_ctx, command_args):
from azure.keyvault.keys import KeyClient

Expand Down
28 changes: 28 additions & 0 deletions src/azure-cli/azure/cli/command_modules/keyvault/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,3 +865,31 @@
text: |
az keyvault region wait --hsm-name myhsm --updated
"""

helps['keyvault setting'] = """
type: group
short-summary: Manage MHSM settings.
"""

helps['keyvault setting list'] = """
type: command
short-summary: Get all settings associated with the managed HSM.
"""

helps['keyvault setting show'] = """
type: command
short-summary: Get specific setting associated with the managed HSM.
examples:
- name: Add "AllowKeyManagementOperationsThroughARM" setting of the managed HSM.
text: |
az keyvault setting show --name AllowKeyManagementOperationsThroughARM --hsm-name myhsm
"""

helps['keyvault setting update'] = """
type: command
short-summary: Update specific setting associated with the managed HSM.
examples:
- name: Allow key management operations through ARM for the managed HSM.
text: |
az keyvault setting update --name AllowKeyManagementOperationsThroughARM --value true --type boolean --hsm-name myhsm
"""
13 changes: 13 additions & 0 deletions src/azure-cli/azure/cli/command_modules/keyvault/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -865,3 +865,16 @@ class PrincipalType(str, Enum): # Copied from azure.mgmt.authorization v2018_09
c.argument('name', hsm_name_type)
c.argument('region_name', options_list=['--region-name', '--region', '-r'],
help='The region name.')

for item in ['list', 'show', 'update']:
with self.argument_context(f'keyvault setting {item}', arg_group='Id') as c:
c.extra('hsm_name', hsm_url_type)
c.extra('identifier', options_list=['--id'],
help='Full URI of the HSM. If specified all other \'Id\' arguments should be omitted.',
validator=process_hsm_name)

with self.argument_context('keyvault setting') as c:
c.argument('name', options_list=['--name', '-n'], help='Name of the setting.')
c.argument('value', help='Value of the setting.')
c.argument('setting_type', options_list=['--setting-type', '--type'],
arg_type=get_enum_type(['boolean', 'string']), help='Type of the setting value.')
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ def load_command_table(self, _):
private_data_entity = get_client(self.cli_ctx, ResourceType.DATA_PRIVATE_KEYVAULT)
data_backup_entity = get_client(self.cli_ctx, ResourceType.DATA_KEYVAULT_ADMINISTRATION_BACKUP)
data_access_control_entity = get_client(self.cli_ctx, ResourceType.DATA_KEYVAULT_ADMINISTRATION_ACCESS_CONTROL)
data_setting_entity = get_client(self.cli_ctx, ResourceType.DATA_KEYVAULT_ADMINISTRATION_SETTING)
else:
mgmt_hsms_entity = mgmt_hsms_regions_entity = private_data_entity = data_backup_entity = \
data_access_control_entity = None
data_access_control_entity = data_setting_entity = None

kv_vaults_custom = CliCommandType(
operations_tmpl='azure.cli.command_modules.keyvault.custom#{}',
Expand Down Expand Up @@ -305,6 +306,12 @@ def load_command_table(self, _):
g.keyvault_custom('delete', 'delete_role_definition')
g.keyvault_custom('show', 'show_role_definition')

if not is_azure_stack_profile(self):
with self.command_group('keyvault setting', data_setting_entity.command_type) as g:
g.keyvault_command('list', 'list_settings')
g.keyvault_command('show', 'get_setting')
g.keyvault_custom('update', 'update_hsm_setting')

data_api_version = str(get_api_version(self.cli_ctx, ResourceType.DATA_KEYVAULT)).\
replace('.', '_').replace('-', '_')

Expand Down
116 changes: 42 additions & 74 deletions src/azure-cli/azure/cli/command_modules/keyvault/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1956,18 +1956,19 @@ def _resolve_role_id(client, role, scope):
else:
all_roles = list_role_definitions(client, scope=scope)
for _role in all_roles:
if _role.get('roleName', None) == role:
role_id = _role['id']
if _role.role_name == role:
role_id = _role.id
break
return role_id


def _get_role_dics(role_defs):
return {i['id']: i.get('roleName', None) for i in role_defs}
return {i.id: i.role_name for i in role_defs}


def _get_principal_dics(cli_ctx, role_assignments):
principal_ids = {i.principal_id for i in role_assignments if getattr(i, 'principal_id', None)}
principal_ids = {getattr(i.properties, 'principal_id', None) for i in role_assignments
if getattr(i, 'properties', None)}
if principal_ids:
from azure.cli.command_modules.role import graph_client_factory, GraphError
try:
Expand Down Expand Up @@ -1995,18 +1996,20 @@ def _reconstruct_role_assignment(role_dics, principal_dics, role_assignment):
ret = {
'id': role_assignment.role_assignment_id,
'name': role_assignment.name,
'scope': role_assignment.scope,
'scope': role_assignment.properties.scope,
'type': role_assignment.type
}
role_definition_id = getattr(role_assignment, 'role_definition_id', None)
role_definition_id = getattr(role_assignment.properties, 'role_definition_id', None)\
if getattr(role_assignment, 'properties', None) else None
ret['roleDefinitionId'] = role_definition_id
if role_definition_id:
ret['roleName'] = role_dics.get(role_definition_id)
else:
ret['roleName'] = None # the role definition might have been deleted

# fill in principal names
principal_id = getattr(role_assignment, 'principal_id', None)
principal_id = getattr(role_assignment.properties, 'principal_id', None)\
if getattr(role_assignment, 'properties', None) else None
ret['principalId'] = principal_id
if principal_id:
principal_info = principal_dics.get(principal_id)
Expand Down Expand Up @@ -2051,8 +2054,10 @@ def create_role_assignment(cmd, client, role, scope, assignee_object_id=None,
scope = ''

role_assignment = client.create_role_assignment(
role_scope=scope, role_assignment_name=role_assignment_name,
principal_id=assignee_object_id, role_definition_id=role_definition_id
scope=scope,
definition_id=role_definition_id,
principal_id=assignee_object_id,
name=role_assignment_name,
)

role_defs = list_role_definitions(client)
Expand All @@ -2074,42 +2079,19 @@ def delete_role_assignment(cmd, client, role_assignment_name=None, scope=None, a
if query_scope is None:
query_scope = ''

deleted_role_assignments = []
if ids is not None:
for cnt_id in ids:
cnt_name = cnt_id.split('/')[-1]
deleted_role_assignments.append(
client.delete_role_assignment(role_scope=query_scope, role_assignment_name=cnt_name)
)
client.delete_role_assignment(scope=query_scope, name=cnt_name)
else:
if role_assignment_name is not None:
deleted_role_assignments.append(
client.delete_role_assignment(role_scope=query_scope, role_assignment_name=role_assignment_name)
)
client.delete_role_assignment(scope=query_scope, name=role_assignment_name)
else:
matched_role_assignments = list_role_assignments(
cmd, client, scope=scope, role=role, assignee_object_id=assignee_object_id, assignee=assignee
)
for role_assignment in matched_role_assignments:
deleted_role_assignments.append(
client.delete_role_assignment(
role_scope=query_scope, role_assignment_name=role_assignment.get('name')
)
)

role_defs = list_role_definitions(client)
role_dics = _get_role_dics(role_defs)
principal_dics = _get_principal_dics(cmd.cli_ctx, deleted_role_assignments)

ret = []
for i in deleted_role_assignments:
ret.append(_reconstruct_role_assignment(
role_dics=role_dics,
principal_dics=principal_dics,
role_assignment=i
))

return ret
client.delete_role_assignment(scope=query_scope, name=role_assignment.get('name'))


def list_role_assignments(cmd, client, scope=None, assignee=None, role=None, assignee_object_id=None,
Expand All @@ -2128,18 +2110,18 @@ def list_role_assignments(cmd, client, scope=None, assignee=None, role=None, ass
if role is not None:
role_definition_id = _resolve_role_id(client, role=role, scope=query_scope)

all_role_assignments = client.list_role_assignments(role_scope=query_scope)
all_role_assignments = client.list_role_assignments(scope=query_scope)
matched_role_assignments = []
for role_assignment in all_role_assignments:
if role_definition_id is not None:
if role_assignment.role_definition_id != role_definition_id:
if role_assignment.properties.role_definition_id != role_definition_id:
continue
if scope is not None:
cnt_scope = role_assignment.scope
cnt_scope = role_assignment.properties.scope
if cnt_scope not in [scope, '/' + scope]:
continue
if assignee_object_id is not None:
if role_assignment.principal_id != assignee_object_id:
if role_assignment.properties.principal_id != assignee_object_id:
continue
matched_role_assignments.append(role_assignment)

Expand All @@ -2158,40 +2140,15 @@ def list_role_assignments(cmd, client, scope=None, assignee=None, role=None, ass
return ret


def _reconstruct_role_definition(role_definition):
ret_permissions = []
permissions = role_definition.permissions
for permission in permissions:
ret_permissions.append({
'actions': permission.allowed_actions,
'notActions': permission.denied_actions,
'dataActions': permission.allowed_data_actions,
'notDataActions': permission.denied_data_actions
})

ret = {
'assignableScopes': role_definition.assignable_scopes,
'description': role_definition.description,
'id': role_definition.id,
'name': role_definition.name,
'permissions': ret_permissions,
'roleName': role_definition.role_name,
'roleType': role_definition.role_type,
'type': role_definition.type,
}

return ret


def list_role_definitions(client, scope=None, hsm_name=None, custom_role_only=False): # pylint: disable=unused-argument
""" List role definitions. """
query_scope = scope
if query_scope is None:
query_scope = ''
role_definitions = client.list_role_definitions(role_scope=query_scope)
role_definitions = client.list_role_definitions(scope=query_scope)
if custom_role_only:
role_definitions = [role for role in role_definitions if role.role_type == 'CustomRole']
return [_reconstruct_role_definition(role) for role in role_definitions]
return role_definitions


def create_role_definition(client, hsm_name, role_definition): # pylint: disable=unused-argument
Expand Down Expand Up @@ -2225,10 +2182,10 @@ def _create_update_role_definition(client, role_definition, for_update):
role_name = role_definition.get('roleName', None)
description = role_definition.get('description', None)
permissions = [KeyVaultPermission(
allowed_actions=role_definition.get('actions', None),
denied_actions=role_definition.get('notActions', None),
allowed_data_actions=role_definition.get('dataActions', None),
denied_data_actions=role_definition.get('notDataActions', None)
actions=role_definition.get('actions', None),
not_actions=role_definition.get('notActions', None),
data_actions=role_definition.get('dataActions', None),
not_data_actions=role_definition.get('notDataActions', None)
)]

if for_update:
Expand All @@ -2242,14 +2199,14 @@ def _create_update_role_definition(client, role_definition, for_update):
role_definition_name = _get_role_definition_name(role_definition_name, role_id)

result_role_definition = client.set_role_definition(
role_scope=role_scope,
scope=role_scope,
permissions=permissions,
role_definition_name=role_definition_name,
name=role_definition_name,
role_name=role_name,
description=description
)

return _reconstruct_role_definition(result_role_definition)
return result_role_definition


def _get_role_definition_name(role_definition_name, role_id):
Expand Down Expand Up @@ -2280,7 +2237,7 @@ def show_role_definition(client, hsm_name, role_definition_name=None, role_id=No
role_scope = '/' # Managed HSM only supports '/'
result_role_definition = client.get_role_definition(role_scope, role_definition_name)

return _reconstruct_role_definition(result_role_definition)
return result_role_definition
# endregion


Expand Down Expand Up @@ -2675,3 +2632,14 @@ def remove_hsm_region(client, resource_group_name, name, region_name, no_wait=Fa
logger.warning("%s doesn't exist", region_name)
return hsm
# endregion


# region mhsm settings
def update_hsm_setting(client, name, value, setting_type=None):
# TODO: remove this additional call to `get_setting` after SDK fix the auth issue for `update_setting`
# TODO: For now, we need to call `get_setting` first to make sure client has set credential correctly
client.get_setting(name=name)
from azure.keyvault.administration import KeyVaultSetting
setting = KeyVaultSetting(name=name, value=value, setting_type=setting_type)
return client.update_setting(setting)
# endregion
Loading