def test_disable_encryption_error_cases_handling(self, mock_compute_client_factory, mock_vm_set): # pylint: disable=unused-argument os_disk = OSDisk(None, OperatingSystemTypes.linux) existing_disk = DataDisk(lun=1, vhd='https://someuri', name='d1', create_option=DiskCreateOptionTypes.empty) vm = FakedVM(None, [existing_disk], os_disk=os_disk) vm_extension = VirtualMachineExtension('westus', settings={'SequenceVersion': 1}, instance_view=VirtualMachineExtensionInstanceView( statuses=[InstanceViewStatus(message='Encryption completed successfully')], substatuses=[InstanceViewStatus(message='{"os":"Encrypted"}')])) vm_extension.provisioning_state = 'Succeeded' compute_client_mock = mock.MagicMock() compute_client_mock.virtual_machines.get.return_value = vm compute_client_mock.virtual_machine_extensions.get.return_value = vm_extension mock_compute_client_factory.return_value = compute_client_mock # throw on disabling encryption on OS disk of a linux VM with self.assertRaises(CLIError) as context: disable('rg1', 'vm1', 'OS') self.assertTrue("Only data disk is supported to disable on Linux VM" in str(context.exception)) # throw on disabling encryption on data disk, but os disk is also encrypted with self.assertRaises(CLIError) as context: disable('rg1', 'vm1', 'DATA') self.assertTrue("Disabling encryption on data disk can render the VM unbootable" in str(context.exception)) # works fine to disable encryption on daat disk when OS disk is never encrypted vm_extension.instance_view.substatuses[0].message = '{}' disable('rg1', 'vm1', 'DATA')
def image_vm(self, vm): group = parse_resource_id(vm.id)["resource_group"] tokens = self.freta.image.upload_sas(vm.name, "lime", "eastus") settings = { "fileUris": [ "https://github.com/microsoft/avml/releases/download/v0.2.0/avml" ] } cmd = ( "./avml --sas_block_size 20 --delete --compress --sas_url '%s' /root/image.lime.%f" % (tokens["image"]["sas_url"], time.time())) protected_settings = {"commandToExecute": cmd} version = self.get_extension_version(vm) extension = VirtualMachineExtension( location=vm.location, publisher=self.publisher, virtual_machine_extension_type=self.extension, settings=settings, protected_settings=protected_settings, type_handler_version=version, auto_upgrade_minor_version=False, ) poller = self.az.virtual_machine_extensions.create_or_update( group, vm.name, self.extension, extension) print("imaging: {} ({})".format(repr(vm.name), tokens["image_id"])) return (tokens["image_id"], poller)
def set_extension( resource_group_name, vm_name, vm_extension_name, publisher, version=None, settings=None, protected_settings=None, no_auto_upgrade=False): '''create/update extensions for a VM in a resource group. You can use 'extension image list' to get extension details :param vm_extension_name: the name of the extension :param publisher: the name of extension publisher :param version: the version of extension. :param settings: public settings or a file path with such contents :param protected_settings: protected settings or a file path with such contents :param no_auto_upgrade: by doing this, extension system will not pick the highest minor version for the specified version number, and will not auto update to the latest build/revision number on any VM updates in future. ''' vm = _vm_get(resource_group_name, vm_name) client = _compute_client_factory() from azure.mgmt.compute.models import VirtualMachineExtension protected_settings = load_json(protected_settings) if protected_settings else {} settings = load_json(settings) if settings else None #pylint: disable=no-member version = _normalize_extension_version(publisher, vm_extension_name, version, vm.location) ext = VirtualMachineExtension(vm.location, publisher=publisher, virtual_machine_extension_type=vm_extension_name, protected_settings=protected_settings, type_handler_version=version, settings=settings, auto_upgrade_minor_version=(not no_auto_upgrade)) return client.virtual_machine_extensions.create_or_update( resource_group_name, vm_name, vm_extension_name, ext)
def reset_windows_admin( resource_group_name, vm_name, username, password): '''Update the password. You can only change the password. Adding a new user is not supported. ''' vm = _vm_get(resource_group_name, vm_name, 'instanceView') client = _compute_client_factory() from azure.mgmt.compute.models import VirtualMachineExtension extension_name = _WINDOWS_ACCESS_EXT publisher, version, auto_upgrade = _get_access_extension_upgrade_info( vm.resources, extension_name) ext = VirtualMachineExtension(vm.location,#pylint: disable=no-member publisher=publisher, virtual_machine_extension_type=extension_name, protected_settings={'Password': password}, type_handler_version=version, settings={'UserName': username}, auto_upgrade_minor_version=auto_upgrade) poller = client.virtual_machine_extensions.create_or_update(resource_group_name, vm_name, _ACCESS_EXT_HANDLER_NAME, ext) return ExtensionUpdateLongRunningOperation('resetting admin', 'done')(poller)
def create_or_update_vmextension(self): ''' Method calling the Azure SDK to create or update the VM extension. :return: void ''' self.log("Creating VM extension {0}".format(self.name)) try: params = VirtualMachineExtension( location=self.location, publisher=self.publisher, virtual_machine_extension_type=self. virtual_machine_extension_type, type_handler_version=self.type_handler_version, auto_upgrade_minor_version=self.auto_upgrade_minor_version, settings=self.settings, protected_settings=self.protected_settings) poller = self.compute_client.virtual_machine_extensions.create_or_update( self.resource_group, self.virtual_machine_name, self.name, params) response = self.get_poller_result(poller) return vmextension_to_dict(response) except CloudError as e: self.log('Error attempting to create the VM extension.') self.fail("Error creating the VM extension: {0}".format(str(e)))
def get_ext_props(self, extension_data, settings=None, protected_settings=None, auto_upgrade_minor_version=True, force_update_tag=None) -> VirtualMachineExtension: return VirtualMachineExtension( location=self.vm_data.location, publisher=extension_data.publisher, type_properties_type=extension_data.ext_type, type_handler_version=extension_data.version, auto_upgrade_minor_version=auto_upgrade_minor_version, settings=settings, protected_settings=protected_settings, force_update_tag=force_update_tag)
def create_windows_vm_script_extension( self, script_file_path, script_config, vm_name, resource_group_name, region, tags, wait_for_result=True, ): """Create Windows VM Script Extension. :param str script_file_path: :param str script_config: :param str vm_name: :param str resource_group_name: :param str region: :param dict[str, str] tags: :param bool wait_for_result: :return: """ file_name = script_file_path.split("/")[-1] vm_extension = VirtualMachineExtension( location=region, publisher=self.VM_SCRIPT_WINDOWS_PUBLISHER, type_handler_version=self.VM_SCRIPT_WINDOWS_HANDLER_VERSION, virtual_machine_extension_type=self. VM_SCRIPT_WINDOWS_EXTENSION_TYPE, tags=tags, settings={ "fileUris": [script_file_path], "commandToExecute": self.VM_SCRIPT_WINDOWS_COMMAND_TPL.format( file_name=file_name, script_configuration=script_config), }, ) operation_poller = self._compute_client.virtual_machine_extensions.create_or_update( # noqa: E501 resource_group_name=resource_group_name, vm_name=vm_name, vm_extension_name=vm_name, extension_parameters=vm_extension, ) if wait_for_result: return operation_poller.result() return operation_poller
def create_linux_vm_script_extension( self, script_file_path, script_config, vm_name, resource_group_name, region, tags, wait_for_result=True, ): """Create Linux VM Script Extension. :param str script_file_path: :param str script_config: :param str vm_name: :param str resource_group_name: :param str region: :param dict[str, str] tags: :param bool wait_for_result: :return: """ file_uris = [ file_uri.strip() for file_uri in script_file_path.split(",") ] vm_extension = VirtualMachineExtension( location=region, publisher=self.VM_SCRIPT_LINUX_PUBLISHER, type_handler_version=self.VM_SCRIPT_LINUX_HANDLER_VERSION, virtual_machine_extension_type=self.VM_SCRIPT_LINUX_EXTENSION_TYPE, tags=tags, settings={ "fileUris": file_uris, "commandToExecute": script_config }, ) operation_poller = self._compute_client.virtual_machine_extensions.create_or_update( # noqa: E501 resource_group_name=resource_group_name, vm_name=vm_name, vm_extension_name=vm_name, extension_parameters=vm_extension, ) if wait_for_result: return operation_poller.result() return operation_poller
def run(job=None, logger=None, service=None, server=None, **kwargs): """ Install an extension on the specified server or server tier. Can be used as a server action, a service action, or a blueprint action. """ # Change the following section when developing an action for a new extension. EXTENSION_NAME = "{{ extension_name }}" PUBLISHER = "{{ publisher }}" VERSION = "{{ version }}" settings = """{{ settings }}""" protected_settings = """{{ protected_settings }}""" set_progress("Adding {} extension to {}".format(EXTENSION_NAME, server)) resource_handler = server.resource_handler.cast() if PUBLISHER == "": PUBLISHER = "Microsoft.Compute" if protected_settings == "": protected_settings = "{}" server_info = resource_handler.tech_specific_server_details(server) server_name = server.hostname resource_group_name = server_info.resource_group location = server_info.location w = resource_handler.get_api_wrapper() from azure.mgmt.compute.models import VirtualMachineExtension extension_parameters = VirtualMachineExtension( location=location, publisher=PUBLISHER, virtual_machine_extension_type=EXTENSION_NAME, type_handler_version=VERSION, settings=json.loads(settings), protected_settings=json.loads(protected_settings)) poller = w.compute_client.virtual_machine_extensions.create_or_update( resource_group_name, server_name, EXTENSION_NAME, extension_parameters) poller.wait(timeout=240) has_failures = False status = "FAILURE" if has_failures else "SUCCESS" return status, "", ""
def set_linux_user(resource_group_name, vm_name, username, password=None, ssh_key_value=None): '''create or update a user credential :param username: user name :param password: user password. :param ssh_key_value: SSH key file value or key file path ''' vm = _vm_get(resource_group_name, vm_name, 'instanceView') client = _compute_client_factory() from azure.mgmt.compute.models import VirtualMachineExtension protected_settings = {} protected_settings['username'] = username if password: protected_settings['password'] = password elif not ssh_key_value and not password: #default to ssh ssh_key_value = os.path.join(os.path.expanduser('~'), '.ssh', 'id_rsa.pub') if ssh_key_value: protected_settings['ssh_key'] = read_content_if_is_file(ssh_key_value) extension_name = _LINUX_ACCESS_EXT publisher, version, auto_upgrade = _get_access_extension_upgrade_info( vm.resources, extension_name) ext = VirtualMachineExtension( vm.location, #pylint: disable=no-member publisher=publisher, virtual_machine_extension_type=extension_name, protected_settings=protected_settings, type_handler_version=version, settings={}, auto_upgrade_minor_version=auto_upgrade) poller = client.virtual_machine_extensions.create_or_update( resource_group_name, vm_name, _ACCESS_EXT_HANDLER_NAME, ext) return ExtensionUpdateLongRunningOperation('setting user', 'done')(poller)
def _prepare_linux_vm_script_extension(self, location, script_file, script_configurations, tags): """Prepare VirtualMachineExtension model for Linux custom script extension :param location: (str) Azure region :param script_file: (str) path to the script file(s) that will be downloaded to the virtual machine :param script_configurations: (str) additional information for the extension execution :param tags: (dict) Azure tags :return: azure.mgmt.compute.models.VirtualMachineExtension instance """ file_uris = [file_uri.strip() for file_uri in script_file.split(",")] return VirtualMachineExtension( location=location, publisher=self.LINUX_PUBLISHER, type_handler_version=self.LINUX_HANDLER_VERSION, virtual_machine_extension_type=self.LINUX_EXTENSION_TYPE, tags=tags, settings={ "fileUris": file_uris, "commandToExecute": script_configurations, })
def delete_linux_user( resource_group_name, vm_name, username): '''Remove the user ''' vm = _vm_get(resource_group_name, vm_name, 'instanceView') client = _compute_client_factory() from azure.mgmt.compute.models import VirtualMachineExtension extension_name = _LINUX_ACCESS_EXT publisher, version, auto_upgrade = _get_access_extension_upgrade_info( vm.resources, extension_name) ext = VirtualMachineExtension(vm.location,#pylint: disable=no-member publisher=publisher, virtual_machine_extension_type=extension_name, protected_settings={'remove_user':username}, type_handler_version=version, settings={}, auto_upgrade_minor_version=auto_upgrade) poller = client.virtual_machine_extensions.create_or_update(resource_group_name, vm_name, _ACCESS_EXT_HANDLER_NAME, ext) return ExtensionUpdateLongRunningOperation('deleting user', 'done')(poller)
def _prepare_windows_vm_script_extension(self, location, script_file, script_configurations, tags): """Prepare VirtualMachineExtension model for Windows PowerShell script extension :param location: (str) Azure region :param script_file: :param script_configurations: :param tags: (dict) Azure tags :return: azure.mgmt.compute.models.VirtualMachineExtension instance """ file_name = script_file.rstrip("/").split("/")[-1] exec_command = "powershell.exe -ExecutionPolicy Unrestricted -File {} {}".format( file_name, script_configurations) return VirtualMachineExtension( location=location, publisher=self.WINDOWS_PUBLISHER, type_handler_version=self.WINDOWS_HANDLER_VERSION, virtual_machine_extension_type=self.WINDOWS_EXTENSION_TYPE, tags=tags, settings={ "fileUris": [script_file], "commandToExecute": exec_command, })
def enable( resource_group_name, vm_name, # pylint: disable=too-many-arguments,too-many-locals, too-many-statements aad_client_id, disk_encryption_keyvault, 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): ''' Enable disk encryption on OS disk, Data disks, or both :param str aad_client_id: Client ID of AAD app with permissions to write secrets to KeyVault :param str aad_client_secret: Client Secret of AAD app with permissions to write secrets to KeyVault :param str aad_client_cert_thumbprint: Thumbprint of AAD app certificate with permissions to write secrets to KeyVault :param str disk_encryption_keyvault:the KeyVault where generated encryption key will be placed :param str key_encryption_key: KeyVault key name or URL used to encrypt the disk encryption key :param str key_encryption_keyvault: the KeyVault containing the key encryption key used to encrypt the disk encryption key. If missing, CLI will use --disk-encryption-keyvault ''' # pylint: disable=no-member compute_client = _compute_client_factory() vm = compute_client.virtual_machines.get(resource_group_name, vm_name) os_type = vm.storage_profile.os_disk.os_type.value is_linux = _is_linux_vm(os_type) extension = extension_info[os_type] # 1. First validate arguments if not aad_client_cert_thumbprint and not aad_client_secret: raise CLIError( 'Please provide either --aad-client-id or --aad-client-cert-thumbprint' ) if volume_type is None: if 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( (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( (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 = { 'AADClientID': aad_client_id, 'AADClientCertThumbprint': aad_client_cert_thumbprint, 'KeyVaultURL': disk_encryption_keyvault_url, 'VolumeType': volume_type, 'EncryptionOperation': 'EnableEncryption', 'KeyEncryptionKeyURL': key_encryption_key, 'KeyEncryptionAlgorithm': key_encryption_algorithm, 'SequenceVersion': sequence_version, } private_config = { 'AADClientSecret': aad_client_secret if is_linux else (aad_client_secret or '') } from azure.mgmt.compute.models import (VirtualMachineExtension, DiskEncryptionSettings, KeyVaultSecretReference, KeyVaultKeyReference, SubResource) ext = VirtualMachineExtension( vm.location, # pylint: disable=no-member publisher=extension['publisher'], virtual_machine_extension_type=extension['name'], protected_settings=private_config, 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() # 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 (extension_result.instance_view.statuses and extension_result.instance_view.statuses[0].message): raise CLIError( 'Could not found 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(disk_encryption_keyvault)) key_encryption_key_obj = None if key_encryption_key: key_encryption_key_obj = KeyVaultKeyReference( key_encryption_key, SubResource(key_encryption_keyvault)) disk_encryption_settings = DiskEncryptionSettings( disk_encryption_key=secret_ref, key_encryption_key=key_encryption_key_obj, enabled=True) vm.storage_profile.os_disk.encryption_settings = disk_encryption_settings set_vm(vm) if is_linux and volume_type != _DATA_VOLUME_TYPE: # TODO: expose a 'wait' command to do the monitor and handle the reboot logger.warning( "The encryption request was accepted. Please use 'show' command to monitor " "the progress. If you see 'VMRestartPending', please restart the VM, and " "the encryption will finish shortly")
def disable(resource_group_name, vm_name, volume_type=None, force=False): ''' Disable disk encryption on OS disk, Data disks, or both ''' compute_client = _compute_client_factory() 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(resource_group_name, vm_name) if status['osDisk'] == _STATUS_ENCRYPTED: raise CLIError( "VM's OS disk is encrypted. Disabling encryption on data " "disk can still cause VM unbootable. Use '--force' " "to continue") else: raise CLIError( "Only data disk is supported to disable on Linux VM") 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") # sequence_version should be incremented since encryptions occurred before extension = extension_info[os_type] 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, } from azure.mgmt.compute.models import (VirtualMachineExtension, DiskEncryptionSettings, KeyVaultSecretReference, KeyVaultKeyReference, SubResource) 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(vm)