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/azure-cli-core/azure/cli/core/azclierror.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ class UnclassifiedUserFault(UserFault):
Avoid using this class unless the error can not be classified into
the UserFault related specific error types.
"""

def print_error(self):
from azure.cli.core.azlogging import CommandLoggerContext
with CommandLoggerContext(logger):
Expand Down
3 changes: 3 additions & 0 deletions src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class ResourceType(Enum): # pylint: disable=too-few-public-methods
MGMT_APIMANAGEMENT = ('azure.mgmt.apimanagement', 'ApiManagementClient')
MGMT_KUSTO = ('azure.mgmt.kusto', 'KustoManagementClient')
MGMT_KEYVAULT = ('azure.mgmt.keyvault', 'KeyVaultManagementClient')
MGMT_PRIVATE_KEYVAULT = ('azure.cli.command_modules.keyvault.vendored_sdks.azure_mgmt_keyvault',
'KeyVaultManagementClient')
MGMT_STORAGE = ('azure.mgmt.storage', 'StorageManagementClient')
MGMT_COMPUTE = ('azure.mgmt.compute', 'ComputeManagementClient')
MGMT_NETWORK = ('azure.mgmt.network', 'NetworkManagementClient')
Expand Down Expand Up @@ -157,6 +159,7 @@ def default_api_version(self):
ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2019-06-01-preview',
ResourceType.MGMT_NETWORK_DNS: '2018-05-01',
ResourceType.MGMT_KEYVAULT: '2020-04-01-preview',
ResourceType.MGMT_PRIVATE_KEYVAULT: '2020-04-01-preview',
ResourceType.MGMT_AUTHORIZATION: SDKProfile('2020-04-01-preview', {
'classic_administrators': '2015-06-01',
'role_definitions': '2018-01-01-preview',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class Clients(str, Enum):
KEYVAULT_TEMPLATE_STRINGS = {
ResourceType.MGMT_KEYVAULT:
'azure.mgmt.keyvault{api_version}.{module_name}#{class_name}{obj_name}',
ResourceType.MGMT_PRIVATE_KEYVAULT:
'azure.cli.command_modules.keyvault.vendored_sdks.azure_mgmt_keyvault{api_version}.{module_name}'
'#{class_name}{obj_name}',
ResourceType.DATA_KEYVAULT:
'azure.keyvault{api_version}.key_vault_client#{class_name}{obj_name}',
ResourceType.DATA_PRIVATE_KEYVAULT:
Expand All @@ -42,7 +45,7 @@ class Clients(str, Enum):


def is_mgmt_plane(resource_type):
return resource_type == ResourceType.MGMT_KEYVAULT
return resource_type in [ResourceType.MGMT_KEYVAULT, ResourceType.MGMT_PRIVATE_KEYVAULT]


def get_operations_tmpl(resource_type, client_name):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,7 @@ def attributes_argument(self, name, attr_class, create=False, ignore=None):
if 'not_before' not in ignore:
self.extra('not_before', default=None, type=datetime_type,
help='Key not usable before the provided UTC datetime (Y-m-d\'T\'H:M:S\'Z\').')

if name == 'key':
self.extra('exportable', arg_type=get_three_state_flag(), is_preview=True,
help='Set "exportable" field in KeyAttributes.')
26 changes: 21 additions & 5 deletions src/azure-cli/azure/cli/command_modules/keyvault/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

from azure.cli.command_modules.keyvault._completers import (
get_keyvault_name_completion_list, get_keyvault_version_completion_list)
from azure.cli.command_modules.keyvault._client_factory import is_azure_stack_profile

from azure.cli.command_modules.keyvault._validators import (
datetime_type, certificate_type,
get_vault_base_url_type, get_hsm_base_url_type,
Expand All @@ -26,7 +28,7 @@
secret_text_encoding_values, secret_binary_encoding_values, validate_subnet,
validate_vault_or_hsm, validate_key_id, validate_sas_definition_id, validate_storage_account_id,
validate_storage_disabled_attribute, validate_deleted_vault_or_hsm_name, validate_encryption, validate_decryption,
validate_vault_name_and_hsm_name, set_vault_base_url,
validate_vault_name_and_hsm_name, set_vault_base_url, process_release_policy,
process_hsm_name, KeyEncryptionDataType)

# CUSTOM CHOICE LISTS
Expand All @@ -37,14 +39,17 @@

# pylint: disable=too-many-locals, too-many-branches, too-many-statements, line-too-long
def load_arguments(self, _):
(JsonWebKeyOperation, KeyAttributes, JsonWebKeyType, JsonWebKeyCurveName, SasTokenType,
(JsonWebKeyOperation, JsonWebKeyType, JsonWebKeyCurveName, SasTokenType,
SasDefinitionAttributes, SecretAttributes, CertificateAttributes, StorageAccountAttributes,
JsonWebKeyEncryptionAlgorithm) = self.get_models(
'JsonWebKeyOperation', 'KeyAttributes', 'JsonWebKeyType', 'JsonWebKeyCurveName', 'SasTokenType',
'JsonWebKeyOperation', 'JsonWebKeyType', 'JsonWebKeyCurveName', 'SasTokenType',
'SasDefinitionAttributes', 'SecretAttributes', 'CertificateAttributes', 'StorageAccountAttributes',
'JsonWebKeyEncryptionAlgorithm',
resource_type=ResourceType.DATA_KEYVAULT)

resource_type = ResourceType.DATA_KEYVAULT if is_azure_stack_profile(self) else ResourceType.DATA_PRIVATE_KEYVAULT
KeyAttributes = self.get_models('KeyAttributes', resource_type=resource_type)

class CLIJsonWebKeyOperation(str, Enum):
encrypt = "encrypt"
decrypt = "decrypt"
Expand All @@ -70,6 +75,7 @@ class CLIJsonWebKeyType(str, Enum):
class CLIKeyTypeForBYOKImport(str, Enum):
ec = "EC" #: Elliptic Curve.
rsa = "RSA" #: RSA (https://tools.ietf.org/html/rfc3447)
oct = 'oct'

(KeyPermissions, SecretPermissions, CertificatePermissions, StoragePermissions,
NetworkRuleBypassOptions, NetworkRuleAction) = self.get_models(
Expand Down Expand Up @@ -339,6 +345,15 @@ class CLIKeyTypeForBYOKImport(str, Enum):
type=datetime_type)
c.argument('not_before', default=None, type=datetime_type,
help='Key not usable before the provided UTC datetime (Y-m-d\'T\'H:M:S\'Z\').')
c.argument('exportable', arg_type=get_three_state_flag(),
is_preview=True,
help='Set "exportable" field in KeyAttributes.')

c.argument('release_policy', options_list=['--policy'],
help='The policy rules under which the key can be exported encoded with JSON. '
'Use @{file} to load from a file(e.g. @my_policy.json).',
type=get_json_object, is_preview=True,
validator=process_release_policy)

with self.argument_context('keyvault key create') as c:
c.argument('kty', arg_type=get_enum_type(JsonWebKeyType), validator=validate_key_type,
Expand All @@ -356,6 +371,7 @@ class CLIKeyTypeForBYOKImport(str, Enum):
c.argument('pem_password', help='Password of PEM file.')
c.argument('byok_file', type=file_type, help='BYOK file containing the key to be imported. Must not be password protected.', completer=FilesCompleter(), validator=validate_key_import_source)
c.argument('byok_string', type=file_type, help='BYOK string containing the key to be imported. Must not be password protected.', validator=validate_key_import_source)
c.argument('byok_kty', arg_type=get_enum_type(CLIKeyTypeForBYOKImport), help='The key type of BYOK file or string.')

with self.argument_context('keyvault key backup') as c:
c.argument('file_path', options_list=['--file', '-f'], type=file_type, completer=FilesCompleter(),
Expand Down Expand Up @@ -499,7 +515,7 @@ class CLIKeyTypeForBYOKImport(str, Enum):
c.ignore('cls')

with self.argument_context('keyvault backup start', arg_group='Storage Id') as c:
c.argument('storage_resource_uri', required=False,
c.argument('storage_resource_uri', options_list=['--storage-resource-uri', '-u'], required=False,
help='Azure Blob storage container Uri. If specified all other \'Storage Id\' arguments '
'should be omitted')
c.extra('storage_account_name', help='Name of Azure Storage Account.')
Expand All @@ -515,7 +531,7 @@ class CLIKeyTypeForBYOKImport(str, Enum):
help='Name of the blob container which contains the backup')

with self.argument_context('keyvault restore start', arg_group='Storage Id') as c:
c.extra('storage_resource_uri', required=False,
c.extra('storage_resource_uri', options_list=['--storage-resource-uri', '-u'], required=False,
help='Azure Blob storage container Uri. If specified all other \'Storage Id\' '
'arguments should be omitted')
c.extra('storage_account_name', help='Name of Azure Storage Account.')
Expand Down
17 changes: 14 additions & 3 deletions src/azure-cli/azure/cli/command_modules/keyvault/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def _get_resource_group_from_resource_name(cli_ctx, vault_name, hsm_name=None):
return id_comps['resource_group']

if hsm_name:
client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_KEYVAULT).managed_hsms
client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_PRIVATE_KEYVAULT).managed_hsms
try:
for hsm in client.list_by_subscription():
id_comps = parse_resource_id(hsm.id)
Expand Down Expand Up @@ -145,6 +145,11 @@ def process_hsm_name(ns):
ns.hsm_name = ns.identifier


def process_release_policy(ns):
if ns.release_policy and not ns.exportable:
ns.exportable = ns.key_attributes.exportable = True


def validate_vault_name_and_hsm_name(ns):
vault_name = getattr(ns, 'vault_name', None)
hsm_name = getattr(ns, 'hsm_name', None)
Expand All @@ -160,11 +165,17 @@ def validate_vault_name_and_hsm_name(ns):
def get_attribute_validator(name, attribute_class, create=False):
def validator(ns):
ns_dict = ns.__dict__
exportable = ns_dict.get('exportable', None)
kwargs = {}
if exportable:
kwargs = {'exportable': True}

enabled = not ns_dict.pop('disabled') if create else ns_dict.pop('enabled')
attributes = attribute_class(
enabled=enabled,
not_before=ns_dict.pop('not_before', None),
expires=ns_dict.pop('expires', None))
expires=ns_dict.pop('expires', None),
**kwargs)
setattr(ns, '{}_attributes'.format(name), attributes)

return validator
Expand Down Expand Up @@ -294,7 +305,7 @@ def validate_deleted_vault_or_hsm_name(cmd, ns):
if vault_name:
client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_KEYVAULT).vaults
else:
client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_KEYVAULT).managed_hsms
client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_PRIVATE_KEYVAULT).managed_hsms

# if the location is specified, use get_deleted rather than list_deleted
if ns.location:
Expand Down
25 changes: 18 additions & 7 deletions src/azure-cli/azure/cli/command_modules/keyvault/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def load_command_table(self, _):
data_entity = get_client(self.cli_ctx, ResourceType.DATA_KEYVAULT)

if not is_azure_stack_profile(self):
mgmt_hsms_entity = get_client(self.cli_ctx, ResourceType.MGMT_KEYVAULT, Clients.managed_hsms)
mgmt_hsms_entity = get_client(self.cli_ctx, ResourceType.MGMT_PRIVATE_KEYVAULT, Clients.managed_hsms)
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)
Expand All @@ -54,7 +54,7 @@ def load_command_table(self, _):
if not is_azure_stack_profile(self):
kv_hsms_custom = CliCommandType(
operations_tmpl='azure.cli.command_modules.keyvault.custom#{}',
client_factory=get_client_factory(ResourceType.MGMT_KEYVAULT, Clients.managed_hsms)
client_factory=get_client_factory(ResourceType.MGMT_PRIVATE_KEYVAULT, Clients.managed_hsms)
)
else:
kv_hsms_custom = None
Expand Down Expand Up @@ -142,6 +142,22 @@ def load_command_table(self, _):
g.keyvault_custom('download', 'security_domain_download')
g.keyvault_custom('wait', '_wait_security_domain_operation')

if not is_azure_stack_profile(self):
with self.command_group('keyvault key', private_data_entity.command_type) as g:
g.keyvault_custom('create', 'create_key',
doc_string_source=data_entity.operations_docs_tmpl.format('create_key'))
g.keyvault_custom('import', 'import_key')
g.keyvault_command('set-attributes', 'update_key')
g.keyvault_custom('get-policy-template', 'get_policy_template', is_preview=True)
g.keyvault_command('show', 'get_key')
else:
with self.command_group('keyvault key', data_entity.command_type) as g:
g.keyvault_custom('create', 'create_key',
doc_string_source=data_entity.operations_docs_tmpl.format('create_key'))
g.keyvault_custom('import', 'import_key')
g.keyvault_command('set-attributes', 'update_key')
g.keyvault_command('show', 'get_key')

with self.command_group('keyvault key', data_entity.command_type) as g:
g.keyvault_command('list', 'get_keys',
transform=multi_transformers(
Expand All @@ -156,10 +172,6 @@ def load_command_table(self, _):
transform=multi_transformers(
keep_max_results,
extract_subresource_name(id_parameter='kid')))
g.keyvault_custom('create', 'create_key',
doc_string_source=data_entity.operations_docs_tmpl.format('create_key'))
g.keyvault_command('set-attributes', 'update_key')
g.keyvault_command('show', 'get_key')
g.keyvault_command('show-deleted', 'get_deleted_key')
g.keyvault_command('delete', 'delete_key')
g.keyvault_command('purge', 'purge_deleted_key')
Expand All @@ -168,7 +180,6 @@ def load_command_table(self, _):
doc_string_source=data_entity.operations_docs_tmpl.format('backup_key'))
g.keyvault_custom('restore', 'restore_key', supports_no_wait=True,
doc_string_source=data_entity.operations_docs_tmpl.format('restore_key'))
g.keyvault_custom('import', 'import_key')
g.keyvault_custom('download', 'download_key')
g.keyvault_custom('get-policy-template', 'get_policy_template', is_preview=True)
g.keyvault_command('encrypt', 'encrypt', is_preview=True)
Expand Down
Loading
Loading