示例#1
0
 def create_data_disk(self, lun, source):
     blob_uri, disk, snapshot = self.resolve_storage_source(source)
     if blob_uri or disk or snapshot:
         snapshot_resource = SubResource(snapshot) if snapshot else None
         managed_disk = SubResource(disk) if disk else None
         return ImageDataDisk(lun,
                              blob_uri=blob_uri,
                              snapshot=snapshot_resource,
                              managed_disk=managed_disk)
示例#2
0
 def create_os_disk(self):
     blob_uri, disk, snapshot = self.resolve_storage_source(self.source)
     snapshot_resource = SubResource(snapshot) if snapshot else None
     managed_disk = SubResource(disk) if disk else None
     return ImageOSDisk(os_type=self.os_type,
                        os_state=OperatingSystemStateTypes.generalized,
                        snapshot=snapshot_resource,
                        managed_disk=managed_disk,
                        blob_uri=blob_uri)
示例#3
0
    def exec_module(self, **kwargs):

        for key in self.module_arg_spec:
            setattr(self, key, kwargs[key])

        results = None
        changed = False
        image = None

        if not self.location:
            # Set default location
            resource_group = self.get_resource_group(self.resource_group)
            self.location = resource_group.location

        self.log('Fetching image {0}'.format(self.name))
        image = self.get_image()
        if image:
            self.check_provisioning_state(image, self.state)
            results = image.id
            # update is not supported
            if self.state == 'absent':
                changed = True
        # the image does not exist and create a new one
        elif self.state == 'present':
            changed = True

        self.results['changed'] = changed
        self.results['id'] = results

        if changed:
            if self.state == 'present':
                image_instance = None
                # create from virtual machine
                vm = self.get_source_vm()
                if vm:
                    if self.data_disk_sources:
                        self.fail('data_disk_sources is not allowed when capturing image from vm')
                    image_instance = Image(self.location, source_virtual_machine=SubResource(vm.id))
                else:
                    if not self.os_type:
                        self.fail('os_type is required to create the image')
                    os_disk = self.create_os_disk()
                    data_disks = self.create_data_disks()
                    storage_profile = ImageStorageProfile(os_disk=os_disk, data_disks=data_disks)
                    image_instance = Image(self.location, storage_profile=storage_profile, tags=self.tags)

                # finally make the change if not check mode
                if not self.check_mode and image_instance:
                    new_image = self.create_image(image_instance)
                    self.results['id'] = new_image.id

            elif self.state == 'absent':
                if not self.check_mode:
                    # delete image
                    self.delete_image()
                    # the delete does not actually return anything. if no exception, then we'll assume it worked.
                    self.results['id'] = None

        return self.results
    def capture_image(self):

        try:
            vms = self.compute_client.virtual_machines
            vms.get(resource_group_name=self.resource_group,
                    vm_name=self.vm_name)
        except CloudError:
            self.fail("VM {} not found!".format(self.vm_name))
        except Exception as e:
            self.fail("An exception occurred: {}".format(str(e)))

        image_names = self._list_images()

        found = any(elem['name'] == self.name for elem in image_names)

        if not found:

            images = self.compute_client.images
            source_vm = vms.get(resource_group_name=self.resource_group,
                                vm_name=self.vm_name).id
            params = Image(location=self.location,
                           source_virtual_machine=SubResource(source_vm))
            if self.check_mode:
                return_data = dict(name=self.name,
                                   status="Succeeded",
                                   location=self.location,
                                   resource_group=self.resource_group,
                                   changed=True)
            else:
                vms.deallocate(self.resource_group, self.vm_name).wait()
                vms.generalize(self.resource_group, self.vm_name)
                operation = images.create_or_update(
                    resource_group_name=self.resource_group,
                    image_name=self.name,
                    parameters=params)
                operation.wait()
                result = operation.result()

                return_data = dict(name=result.name,
                                   status=result.provisioning_state,
                                   location=result.location,
                                   resource_group=self.resource_group,
                                   changed=True)
        else:
            return_data = dict(name=self.name,
                               status="Image already exists",
                               changed=False)

        return return_data
示例#5
0
def create_image_from_vm(vm_name):
    # Deallocate
    async_vm_deallocate = compute_client.virtual_machines.begin_deallocate(test_group_name, vm_name)
    async_vm_deallocate.wait()

    # Generalize (possible because deallocated)
    compute_client.virtual_machines.generalize(test_group_name, vm_name)

    vm = compute_client.virtual_machines.get(test_group_name, vm_name)
    sub_resource = SubResource(id=vm.id)
    params = Image(location=location, source_virtual_machine=sub_resource)
    img_name = "vm_image_"+(str(datetime.datetime.now().time())).replace(":","").replace(".","")
    compute_client.images.begin_create_or_update(
        test_group_name,
        img_name,
        params
    )
    return img_name
示例#6
0
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 exec_module(self, **kwargs):

        for key in list(self.module_arg_spec.keys()) + ['tags']:
            setattr(self, key, kwargs[key])

        # make sure options are lower case
        self.remove_on_absent = set(
            [resource.lower() for resource in self.remove_on_absent])

        changed = False
        results = dict()
        vmss = None
        disable_ssh_password = None
        vmss_dict = None
        virtual_network = None
        subnet = None
        image_reference = None
        custom_image = False

        resource_group = self.get_resource_group(self.resource_group)
        if not self.location:
            # Set default location
            self.location = resource_group.location

        if self.state == 'present':
            # Verify parameters and resolve any defaults

            if self.vm_size and not self.vm_size_is_valid():
                self.fail(
                    "Parameter error: vm_size {0} is not valid for your subscription and location."
                    .format(self.vm_size))

            # if self.virtual_network_name:
            #     virtual_network = self.get_virtual_network(self.virtual_network_name)

            if self.ssh_public_keys:
                msg = "Parameter error: expecting ssh_public_keys to be a list of type dict where " \
                    "each dict contains keys: path, key_data."
                for key in self.ssh_public_keys:
                    if not isinstance(key, dict):
                        self.fail(msg)
                    if not key.get('path') or not key.get('key_data'):
                        self.fail(msg)

            if self.image and isinstance(self.image, dict):
                if all(key in self.image
                       for key in ('publisher', 'offer', 'sku', 'version')):
                    marketplace_image = self.get_marketplace_image_version()
                    if self.image['version'] == 'latest':
                        self.image['version'] = marketplace_image.name
                        self.log("Using image version {0}".format(
                            self.image['version']))

                    image_reference = ImageReference(
                        publisher=self.image['publisher'],
                        offer=self.image['offer'],
                        sku=self.image['sku'],
                        version=self.image['version'])
                elif self.image.get('name'):
                    custom_image = True
                    image_reference = self.get_custom_image_reference(
                        self.image.get('name'),
                        self.image.get('resource_group'))
                else:
                    self.fail(
                        "parameter error: expecting image to contain [publisher, offer, sku, version] or [name, resource_group]"
                    )
            elif self.image and isinstance(self.image, str):
                custom_image = True
                image_reference = self.get_custom_image_reference(self.image)
            elif self.image:
                self.fail(
                    "parameter error: expecting image to be a string or dict not {0}"
                    .format(type(self.image).__name__))

            disable_ssh_password = not self.ssh_password_enabled

        try:
            self.log("Fetching virtual machine scale set {0}".format(
                self.name))
            vmss = self.compute_client.virtual_machine_scale_sets.get(
                self.resource_group, self.name)
            self.check_provisioning_state(vmss, self.state)
            vmss_dict = self.serialize_vmss(vmss)

            if self.state == 'present':
                differences = []
                results = vmss_dict

                if self.os_disk_caching and \
                   self.os_disk_caching != vmss_dict['properties']['virtualMachineProfile']['storageProfile']['osDisk']['caching']:
                    self.log(
                        'CHANGED: virtual machine scale set {0} - OS disk caching'
                        .format(self.name))
                    differences.append('OS Disk caching')
                    changed = True
                    vmss_dict['properties']['virtualMachineProfile'][
                        'storageProfile']['osDisk'][
                            'caching'] = self.os_disk_caching

                if self.capacity and \
                   self.capacity != vmss_dict['sku']['capacity']:
                    self.log(
                        'CHANGED: virtual machine scale set {0} - Capacity'.
                        format(self.name))
                    differences.append('Capacity')
                    changed = True
                    vmss_dict['sku']['capacity'] = self.capacity

                if self.data_disks and \
                   len(self.data_disks) != len(vmss_dict['properties']['virtualMachineProfile']['storageProfile']['dataDisks']):
                    self.log(
                        'CHANGED: virtual machine scale set {0} - Data Disks'.
                        format(self.name))
                    differences.append('Data Disks')
                    changed = True

                update_tags, vmss_dict['tags'] = self.update_tags(
                    vmss_dict.get('tags', dict()))
                if update_tags:
                    differences.append('Tags')
                    changed = True

                self.differences = differences

            elif self.state == 'absent':
                self.log(
                    "CHANGED: virtual machine scale set {0} exists and requested state is 'absent'"
                    .format(self.name))
                results = dict()
                changed = True

        except CloudError:
            self.log('Virtual machine scale set {0} does not exist'.format(
                self.name))
            if self.state == 'present':
                self.log(
                    "CHANGED: virtual machine scale set {0} does not exist but state is 'present'."
                    .format(self.name))
                changed = True

        self.results['changed'] = changed
        self.results['ansible_facts']['azure_vmss'] = results

        if self.check_mode:
            return self.results

        if changed:
            if self.state == 'present':
                if not vmss:
                    # Create the VMSS
                    self.log("Create virtual machine scale set {0}".format(
                        self.name))
                    self.results['actions'].append('Created VMSS {0}'.format(
                        self.name))

                    # Validate parameters
                    if not self.admin_username:
                        self.fail(
                            "Parameter error: admin_username required when creating a virtual machine scale set."
                        )

                    if self.os_type == 'Linux':
                        if disable_ssh_password and not self.ssh_public_keys:
                            self.fail(
                                "Parameter error: ssh_public_keys required when disabling SSH password."
                            )

                    if not self.virtual_network_name:
                        default_vnet = self.create_default_vnet()
                        virtual_network = default_vnet.id
                        self.virtual_network_name = default_vnet.name

                    if self.subnet_name:
                        subnet = self.get_subnet(self.virtual_network_name,
                                                 self.subnet_name)

                    load_balancer_backend_address_pools = None
                    load_balancer_inbound_nat_pools = None
                    if self.load_balancer:
                        load_balancer = self.get_load_balancer(
                            self.load_balancer)
                        load_balancer_backend_address_pools = ([
                            SubResource(resource.id)
                            for resource in load_balancer.backend_address_pools
                        ] if load_balancer.backend_address_pools else None)
                        load_balancer_inbound_nat_pools = ([
                            SubResource(resource.id)
                            for resource in load_balancer.inbound_nat_pools
                        ] if load_balancer.inbound_nat_pools else None)

                    if not self.short_hostname:
                        self.short_hostname = self.name

                    if not image_reference:
                        self.fail(
                            "Parameter error: an image is required when creating a virtual machine."
                        )

                    managed_disk = VirtualMachineScaleSetManagedDiskParameters(
                        storage_account_type=self.managed_disk_type)

                    vmss_resource = VirtualMachineScaleSet(
                        self.location,
                        tags=self.tags,
                        upgrade_policy=UpgradePolicy(mode=self.upgrade_policy),
                        sku=Sku(
                            name=self.vm_size,
                            capacity=self.capacity,
                            tier=self.tier,
                        ),
                        virtual_machine_profile=VirtualMachineScaleSetVMProfile(
                            os_profile=VirtualMachineScaleSetOSProfile(
                                admin_username=self.admin_username,
                                computer_name_prefix=self.short_hostname,
                            ),
                            storage_profile=
                            VirtualMachineScaleSetStorageProfile(
                                os_disk=VirtualMachineScaleSetOSDisk(
                                    managed_disk=managed_disk,
                                    create_option=DiskCreateOptionTypes.
                                    from_image,
                                    caching=self.os_disk_caching,
                                ),
                                image_reference=image_reference,
                            ),
                            network_profile=
                            VirtualMachineScaleSetNetworkProfile(
                                network_interface_configurations=[
                                    VirtualMachineScaleSetNetworkConfiguration(
                                        name=self.name,
                                        primary=True,
                                        ip_configurations=[
                                            VirtualMachineScaleSetIPConfiguration(
                                                name='default',
                                                subnet=ApiEntityReference(
                                                    id=subnet.id),
                                                primary=True,
                                                load_balancer_backend_address_pools
                                                =load_balancer_backend_address_pools,
                                                load_balancer_inbound_nat_pools=
                                                load_balancer_inbound_nat_pools
                                            )
                                        ])
                                ])))

                    if self.admin_password:
                        vmss_resource.virtual_machine_profile.os_profile.admin_password = self.admin_password

                    if self.os_type == 'Linux':
                        vmss_resource.virtual_machine_profile.os_profile.linux_configuration = LinuxConfiguration(
                            disable_password_authentication=disable_ssh_password
                        )

                    if self.ssh_public_keys:
                        ssh_config = SshConfiguration()
                        ssh_config.public_keys = \
                            [SshPublicKey(path=key['path'], key_data=key['key_data']) for key in self.ssh_public_keys]
                        vmss_resource.virtual_machine_profile.os_profile.linux_configuration.ssh = ssh_config

                    if self.data_disks:
                        data_disks = []

                        for data_disk in self.data_disks:
                            data_disk_managed_disk = VirtualMachineScaleSetManagedDiskParameters(
                                storage_account_type=data_disk[
                                    'managed_disk_type'])

                            data_disk['caching'] = data_disk.get(
                                'caching', CachingTypes.read_only)

                            data_disks.append(
                                VirtualMachineScaleSetDataDisk(
                                    lun=data_disk['lun'],
                                    caching=data_disk['caching'],
                                    create_option=DiskCreateOptionTypes.empty,
                                    disk_size_gb=data_disk['disk_size_gb'],
                                    managed_disk=data_disk_managed_disk,
                                ))

                        vmss_resource.virtual_machine_profile.storage_profile.data_disks = data_disks

                    self.log("Create virtual machine with parameters:")
                    self.create_or_update_vmss(vmss_resource)

                elif self.differences and len(self.differences) > 0:
                    self.log("Update virtual machine scale set {0}".format(
                        self.name))
                    self.results['actions'].append('Updated VMSS {0}'.format(
                        self.name))

                    vmss_resource = self.get_vmss()
                    vmss_resource.virtual_machine_profile.storage_profile.os_disk.caching = self.os_disk_caching
                    vmss_resource.sku.capacity = self.capacity

                    data_disks = []
                    for data_disk in self.data_disks:
                        data_disks.append(
                            VirtualMachineScaleSetDataDisk(
                                lun=data_disk['lun'],
                                caching=data_disk['caching'],
                                create_option=DiskCreateOptionTypes.empty,
                                disk_size_gb=data_disk['disk_size_gb'],
                                managed_disk=
                                VirtualMachineScaleSetManagedDiskParameters(
                                    storage_account_type=data_disk[
                                        'managed_disk_type']),
                            ))
                    vmss_resource.virtual_machine_profile.storage_profile.data_disks = data_disks

                    self.log("Update virtual machine with parameters:")
                    self.create_or_update_vmss(vmss_resource)

                self.results['ansible_facts'][
                    'azure_vmss'] = self.serialize_vmss(self.get_vmss())

            elif self.state == 'absent':
                # delete the VM
                self.log("Delete virtual machine scale set {0}".format(
                    self.name))
                self.results['ansible_facts']['azure_vmss'] = None
                self.delete_vmss(vmss)

        # until we sort out how we want to do this globally
        del self.results['actions']

        return self.results