def encrypt_vm( cmd, resource_group_name, vm_name, # pylint: disable=too-many-locals, too-many-statements disk_encryption_keyvault, aad_client_id=None, aad_client_secret=None, aad_client_cert_thumbprint=None, key_encryption_keyvault=None, key_encryption_key=None, key_encryption_algorithm='RSA-OAEP', volume_type=None, encrypt_format_all=False): from msrestazure.tools import parse_resource_id from knack.util import CLIError # pylint: disable=no-member compute_client = _compute_client_factory(cmd.cli_ctx) vm = compute_client.virtual_machines.get(resource_group_name, vm_name) is_linux = _is_linux_os(vm) backup_encryption_settings = vm.storage_profile.os_disk.encryption_settings vm_encrypted = backup_encryption_settings.enabled if backup_encryption_settings else False _, has_old_ade = _detect_ade_status(vm) use_new_ade = not aad_client_id and not has_old_ade extension = vm_extension_info['Linux' if is_linux else 'Windows'] if not use_new_ade and not aad_client_id: raise CLIError('Please provide --aad-client-id') # 1. First validate arguments if not use_new_ade and not aad_client_cert_thumbprint and not aad_client_secret: raise CLIError( 'Please provide either --aad-client-cert-thumbprint or --aad-client-secret' ) if volume_type is None: if not is_linux: volume_type = _ALL_VOLUME_TYPE elif vm.storage_profile.data_disks: raise CLIError('VM has data disks, please supply --volume-type') else: volume_type = 'OS' # encryption is not supported on all linux distros, but service never tells you # so let us verify at the client side if is_linux: image_reference = getattr(vm.storage_profile, 'image_reference', None) if image_reference: result, message = _check_encrypt_is_supported( image_reference, volume_type) if not result: logger.warning(message) # sequence_version should be unique sequence_version = uuid.uuid4() # retrieve keyvault details disk_encryption_keyvault_url = get_key_vault_base_url( cmd.cli_ctx, (parse_resource_id(disk_encryption_keyvault))['name']) # disk encryption key itself can be further protected, so let us verify if key_encryption_key: key_encryption_keyvault = key_encryption_keyvault or disk_encryption_keyvault if '://' not in key_encryption_key: # appears a key name key_encryption_key = _get_keyvault_key_url( cmd.cli_ctx, (parse_resource_id(key_encryption_keyvault))['name'], key_encryption_key) # 2. we are ready to provision/update the disk encryption extensions # The following logic was mostly ported from xplat-cli public_config = { 'KeyVaultURL': disk_encryption_keyvault_url, 'VolumeType': volume_type, 'EncryptionOperation': 'EnableEncryption' if not encrypt_format_all else 'EnableEncryptionFormatAll', 'KeyEncryptionKeyURL': key_encryption_key, 'KeyEncryptionAlgorithm': key_encryption_algorithm, 'SequenceVersion': sequence_version, } if use_new_ade: public_config.update({ "KeyVaultResourceId": disk_encryption_keyvault, "KekVaultResourceId": key_encryption_keyvault if key_encryption_key else '', }) else: public_config.update({ 'AADClientID': aad_client_id, 'AADClientCertThumbprint': aad_client_cert_thumbprint, }) ade_legacy_private_config = { 'AADClientSecret': aad_client_secret if is_linux else (aad_client_secret or '') } VirtualMachineExtension, DiskEncryptionSettings, KeyVaultSecretReference, KeyVaultKeyReference, SubResource = \ cmd.get_models('VirtualMachineExtension', 'DiskEncryptionSettings', 'KeyVaultSecretReference', 'KeyVaultKeyReference', 'SubResource') ext = VirtualMachineExtension( location=vm.location, # pylint: disable=no-member publisher=extension['publisher'], virtual_machine_extension_type=extension['name'], protected_settings=None if use_new_ade else ade_legacy_private_config, type_handler_version=extension['version'] if use_new_ade else extension['legacy_version'], settings=public_config, auto_upgrade_minor_version=True) poller = compute_client.virtual_machine_extensions.create_or_update( resource_group_name, vm_name, extension['name'], ext) poller.result() # verify the extension was ok extension_result = compute_client.virtual_machine_extensions.get( resource_group_name, vm_name, extension['name'], 'instanceView') if extension_result.provisioning_state != 'Succeeded': raise CLIError( 'Extension needed for disk encryption was not provisioned correctly' ) if not use_new_ade: if not (extension_result.instance_view.statuses and extension_result.instance_view.statuses[0].message): raise CLIError( 'Could not find url pointing to the secret for disk encryption' ) # 3. update VM's storage profile with the secrets status_url = extension_result.instance_view.statuses[0].message vm = compute_client.virtual_machines.get(resource_group_name, vm_name) secret_ref = KeyVaultSecretReference( secret_url=status_url, source_vault=SubResource(id=disk_encryption_keyvault)) key_encryption_key_obj = None if key_encryption_key: key_encryption_key_obj = KeyVaultKeyReference( key_url=key_encryption_key, source_vault=SubResource(id=key_encryption_keyvault)) disk_encryption_settings = DiskEncryptionSettings( disk_encryption_key=secret_ref, key_encryption_key=key_encryption_key_obj, enabled=True) if vm_encrypted: # stop the vm before update if the vm is already encrypted logger.warning( "Deallocating the VM before updating encryption settings...") compute_client.virtual_machines.deallocate(resource_group_name, vm_name).result() vm = compute_client.virtual_machines.get(resource_group_name, vm_name) vm.storage_profile.os_disk.encryption_settings = disk_encryption_settings set_vm(cmd, vm) if vm_encrypted: # and start after the update logger.warning("Restarting the VM after the update...") compute_client.virtual_machines.start(resource_group_name, vm_name).result() if is_linux and volume_type != _DATA_VOLUME_TYPE: old_ade_msg = "If you see 'VMRestartPending', please restart the VM, and the encryption will finish shortly" logger.warning( "The encryption request was accepted. Please use 'show' command to monitor " "the progress. %s", "" if use_new_ade else old_ade_msg)
def decrypt_vm(cmd, resource_group_name, vm_name, volume_type=None, force=False): from knack.util import CLIError compute_client = _compute_client_factory(cmd.cli_ctx) vm = compute_client.virtual_machines.get(resource_group_name, vm_name) has_new_ade, has_old_ade = _detect_ade_status(vm) if not has_new_ade and not has_old_ade: logger.warning('Azure Disk Encryption is not enabled') return is_linux = _is_linux_os(vm) # pylint: disable=no-member # 1. be nice, figure out the default volume type and also verify VM will not be busted if is_linux: if volume_type: if not force and volume_type != _DATA_VOLUME_TYPE: raise CLIError( "Only Data disks can have encryption disabled in a Linux VM. " "Use '--force' to ignore the warning") else: volume_type = _DATA_VOLUME_TYPE elif volume_type is None: volume_type = _ALL_VOLUME_TYPE extension = vm_extension_info['Linux' if is_linux else 'Windows'] # sequence_version should be incremented since encryptions occurred before sequence_version = uuid.uuid4() # 2. update the disk encryption extension # The following logic was mostly ported from xplat-cli public_config = { 'VolumeType': volume_type, 'EncryptionOperation': 'DisableEncryption', 'SequenceVersion': sequence_version, } VirtualMachineExtension, DiskEncryptionSettings = cmd.get_models( 'VirtualMachineExtension', 'DiskEncryptionSettings') ext = VirtualMachineExtension( location=vm.location, # pylint: disable=no-member publisher=extension['publisher'], virtual_machine_extension_type=extension['name'], type_handler_version=extension['version'] if has_new_ade else extension['legacy_version'], settings=public_config, auto_upgrade_minor_version=True) poller = compute_client.virtual_machine_extensions.create_or_update( resource_group_name, vm_name, extension['name'], ext) poller.result() extension_result = compute_client.virtual_machine_extensions.get( resource_group_name, vm_name, extension['name'], 'instanceView') if extension_result.provisioning_state != 'Succeeded': raise CLIError("Extension updating didn't succeed") if not has_new_ade: # 3. Remove the secret from VM's storage profile vm = compute_client.virtual_machines.get(resource_group_name, vm_name) disk_encryption_settings = DiskEncryptionSettings(enabled=False) vm.storage_profile.os_disk.encryption_settings = disk_encryption_settings set_vm(cmd, vm)
def decrypt_vm(cmd, resource_group_name, vm_name, volume_type=None, force=False): from knack.util import CLIError compute_client = _compute_client_factory(cmd.cli_ctx) vm = compute_client.virtual_machines.get(resource_group_name, vm_name) has_new_ade, has_old_ade = _detect_ade_status(vm) if not has_new_ade and not has_old_ade: logger.warning('Azure Disk Encryption is not enabled') return is_linux = _is_linux_os(vm) # pylint: disable=no-member # 1. be nice, figure out the default volume type and also verify VM will not be busted if is_linux: if volume_type: if not force and volume_type != _DATA_VOLUME_TYPE: raise CLIError("Only Data disks can have encryption disabled in a Linux VM. " "Use '--force' to ignore the warning") else: volume_type = _DATA_VOLUME_TYPE elif volume_type is None: volume_type = _ALL_VOLUME_TYPE extension = vm_extension_info['Linux' if is_linux else 'Windows'] # sequence_version should be incremented since encryptions occurred before sequence_version = uuid.uuid4() # 2. update the disk encryption extension # The following logic was mostly ported from xplat-cli public_config = { 'VolumeType': volume_type, 'EncryptionOperation': 'DisableEncryption', 'SequenceVersion': sequence_version, } VirtualMachineExtension, DiskEncryptionSettings = cmd.get_models( 'VirtualMachineExtension', 'DiskEncryptionSettings') ext = VirtualMachineExtension( location=vm.location, # pylint: disable=no-member publisher=extension['publisher'], virtual_machine_extension_type=extension['name'], type_handler_version=extension['version'] if has_new_ade else extension['legacy_version'], settings=public_config, auto_upgrade_minor_version=True) poller = compute_client.virtual_machine_extensions.create_or_update(resource_group_name, vm_name, extension['name'], ext) poller.result() extension_result = compute_client.virtual_machine_extensions.get(resource_group_name, vm_name, extension['name'], 'instanceView') if extension_result.provisioning_state != 'Succeeded': raise CLIError("Extension updating didn't succeed") if not has_new_ade: # 3. Remove the secret from VM's storage profile vm = compute_client.virtual_machines.get(resource_group_name, vm_name) disk_encryption_settings = DiskEncryptionSettings(enabled=False) vm.storage_profile.os_disk.encryption_settings = disk_encryption_settings set_vm(cmd, vm)
def encrypt_vm(cmd, resource_group_name, vm_name, # pylint: disable=too-many-locals, too-many-statements disk_encryption_keyvault, aad_client_id=None, aad_client_secret=None, aad_client_cert_thumbprint=None, key_encryption_keyvault=None, key_encryption_key=None, key_encryption_algorithm='RSA-OAEP', volume_type=None, encrypt_format_all=False): from msrestazure.tools import parse_resource_id from knack.util import CLIError # pylint: disable=no-member compute_client = _compute_client_factory(cmd.cli_ctx) vm = compute_client.virtual_machines.get(resource_group_name, vm_name) is_linux = _is_linux_os(vm) backup_encryption_settings = vm.storage_profile.os_disk.encryption_settings vm_encrypted = backup_encryption_settings.enabled if backup_encryption_settings else False _, has_old_ade = _detect_ade_status(vm) use_new_ade = not aad_client_id and not has_old_ade extension = vm_extension_info['Linux' if is_linux else 'Windows'] if not use_new_ade and not aad_client_id: raise CLIError('Please provide --aad-client-id') # 1. First validate arguments if not use_new_ade and not aad_client_cert_thumbprint and not aad_client_secret: raise CLIError('Please provide either --aad-client-cert-thumbprint or --aad-client-secret') if volume_type is None: if not is_linux: volume_type = _ALL_VOLUME_TYPE elif vm.storage_profile.data_disks: raise CLIError('VM has data disks, please supply --volume-type') else: volume_type = 'OS' # encryption is not supported on all linux distros, but service never tells you # so let us verify at the client side if is_linux: image_reference = getattr(vm.storage_profile, 'image_reference', None) if image_reference: result, message = _check_encrypt_is_supported(image_reference, volume_type) if not result: logger.warning(message) # sequence_version should be unique sequence_version = uuid.uuid4() # retrieve keyvault details disk_encryption_keyvault_url = get_key_vault_base_url( cmd.cli_ctx, (parse_resource_id(disk_encryption_keyvault))['name']) # disk encryption key itself can be further protected, so let us verify if key_encryption_key: key_encryption_keyvault = key_encryption_keyvault or disk_encryption_keyvault if '://' not in key_encryption_key: # appears a key name key_encryption_key = _get_keyvault_key_url( cmd.cli_ctx, (parse_resource_id(key_encryption_keyvault))['name'], key_encryption_key) # 2. we are ready to provision/update the disk encryption extensions # The following logic was mostly ported from xplat-cli public_config = { 'KeyVaultURL': disk_encryption_keyvault_url, 'VolumeType': volume_type, 'EncryptionOperation': 'EnableEncryption' if not encrypt_format_all else 'EnableEncryptionFormatAll', 'KeyEncryptionKeyURL': key_encryption_key, 'KeyEncryptionAlgorithm': key_encryption_algorithm, 'SequenceVersion': sequence_version, } if use_new_ade: public_config.update({ "KeyVaultResourceId": disk_encryption_keyvault, "KekVaultResourceId": key_encryption_keyvault if key_encryption_key else '', }) else: public_config.update({ 'AADClientID': aad_client_id, 'AADClientCertThumbprint': aad_client_cert_thumbprint, }) ade_legacy_private_config = { 'AADClientSecret': aad_client_secret if is_linux else (aad_client_secret or '') } VirtualMachineExtension, DiskEncryptionSettings, KeyVaultSecretReference, KeyVaultKeyReference, SubResource = \ cmd.get_models('VirtualMachineExtension', 'DiskEncryptionSettings', 'KeyVaultSecretReference', 'KeyVaultKeyReference', 'SubResource') ext = VirtualMachineExtension( location=vm.location, # pylint: disable=no-member publisher=extension['publisher'], virtual_machine_extension_type=extension['name'], protected_settings=None if use_new_ade else ade_legacy_private_config, type_handler_version=extension['version'] if use_new_ade else extension['legacy_version'], settings=public_config, auto_upgrade_minor_version=True) poller = compute_client.virtual_machine_extensions.create_or_update( resource_group_name, vm_name, extension['name'], ext) poller.result() # verify the extension was ok extension_result = compute_client.virtual_machine_extensions.get( resource_group_name, vm_name, extension['name'], 'instanceView') if extension_result.provisioning_state != 'Succeeded': raise CLIError('Extension needed for disk encryption was not provisioned correctly') if not use_new_ade: if not (extension_result.instance_view.statuses and extension_result.instance_view.statuses[0].message): raise CLIError('Could not find url pointing to the secret for disk encryption') # 3. update VM's storage profile with the secrets status_url = extension_result.instance_view.statuses[0].message vm = compute_client.virtual_machines.get(resource_group_name, vm_name) secret_ref = KeyVaultSecretReference(secret_url=status_url, source_vault=SubResource(id=disk_encryption_keyvault)) key_encryption_key_obj = None if key_encryption_key: key_encryption_key_obj = KeyVaultKeyReference(key_url=key_encryption_key, source_vault=SubResource(id=key_encryption_keyvault)) disk_encryption_settings = DiskEncryptionSettings(disk_encryption_key=secret_ref, key_encryption_key=key_encryption_key_obj, enabled=True) if vm_encrypted: # stop the vm before update if the vm is already encrypted logger.warning("Deallocating the VM before updating encryption settings...") compute_client.virtual_machines.deallocate(resource_group_name, vm_name).result() vm = compute_client.virtual_machines.get(resource_group_name, vm_name) vm.storage_profile.os_disk.encryption_settings = disk_encryption_settings set_vm(cmd, vm) if vm_encrypted: # and start after the update logger.warning("Restarting the VM after the update...") compute_client.virtual_machines.start(resource_group_name, vm_name).result() if is_linux and volume_type != _DATA_VOLUME_TYPE: old_ade_msg = "If you see 'VMRestartPending', please restart the VM, and the encryption will finish shortly" logger.warning("The encryption request was accepted. Please use 'show' command to monitor " "the progress. %s", "" if use_new_ade else old_ade_msg)
def decrypt_vm(cmd, resource_group_name, vm_name, volume_type=None, force=False): from knack.util import CLIError compute_client = _compute_client_factory(cmd.cli_ctx) vm = compute_client.virtual_machines.get(resource_group_name, vm_name) # pylint: disable=no-member os_type = vm.storage_profile.os_disk.os_type.value # 1. be nice, figure out the default volume type and also verify VM will not be busted is_linux = _is_linux_vm(os_type) if is_linux: if volume_type: if not force: if volume_type == _DATA_VOLUME_TYPE: status = show_vm_encryption_status(cmd, resource_group_name, vm_name) if status['osDisk'] == _STATUS_ENCRYPTED: raise CLIError( "Linux VM's OS disk is encrypted. Disabling encryption on data " "disk can render the VM unbootable. Use '--force' " "to ingore the warning") else: raise CLIError( "Only Data disks can have encryption disabled in a Linux VM. " "Use '--force' to ingore the warning") else: volume_type = _DATA_VOLUME_TYPE elif volume_type is None: if vm.storage_profile.data_disks: raise CLIError("VM has data disks, please specify --volume-type") extension = vm_extension_info[os_type] # sequence_version should be incremented since encryptions occurred before sequence_version = uuid.uuid4() # 2. update the disk encryption extension # The following logic was mostly ported from xplat-cli public_config = { 'VolumeType': volume_type, 'EncryptionOperation': 'DisableEncryption', 'SequenceVersion': sequence_version, } VirtualMachineExtension, DiskEncryptionSettings = cmd.get_models( 'VirtualMachineExtension', 'DiskEncryptionSettings') ext = VirtualMachineExtension( vm.location, # pylint: disable=no-member publisher=extension['publisher'], virtual_machine_extension_type=extension['name'], type_handler_version=extension['version'], settings=public_config, auto_upgrade_minor_version=True) poller = compute_client.virtual_machine_extensions.create_or_update( resource_group_name, vm_name, extension['name'], ext) poller.result() # 3. Remove the secret from VM's storage profile extension_result = compute_client.virtual_machine_extensions.get( resource_group_name, vm_name, extension['name'], 'instanceView') if extension_result.provisioning_state != 'Succeeded': raise CLIError("Extension updating didn't succeed") vm = compute_client.virtual_machines.get(resource_group_name, vm_name) disk_encryption_settings = DiskEncryptionSettings(enabled=False) vm.storage_profile.os_disk.encryption_settings = disk_encryption_settings set_vm(cmd, vm)