Skip to content

Commit

Permalink
Add TL toggle on vm repair create to allow nested virtualization on r…
Browse files Browse the repository at this point in the history
…epair VMs (#7991)
  • Loading branch information
Sandido authored Oct 16, 2024
1 parent 4903ec3 commit 2127744
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 19 deletions.
3 changes: 2 additions & 1 deletion src/vm-repair/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ Release History
===============
1.1.0
++++++
Added script for GT fixit button
Added script for GT fixit button.
Added support for `--disable-trusted-launch` flag parameter to set security type to `Standard` on the repair VM no matter what the source VM has.

1.0.10
++++++
Expand Down
1 change: 1 addition & 0 deletions src/vm-repair/azext_vm_repair/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def load_arguments(self, _):
c.argument('associate_public_ip', help='Option to create repair vm with public ip')
c.argument('distro', help='Option to create repair vm from a specific linux distro (rhel7|rhel8|sles12|sles15|ubuntu20|centos7|centos8|oracle7)')
c.argument('yes', help='Option to skip prompt for associating public ip and confirm yes to it in no Tty mode')
c.argument('disable_trusted_launch', help='Option to disable Trusted Launch security type on the repair vm by setting the security type to Standard.')

with self.argument_context('vm repair restore') as c:
c.argument('repair_vm_id', help='Repair VM resource id.')
Expand Down
35 changes: 21 additions & 14 deletions src/vm-repair/azext_vm_repair/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,11 @@
logger = get_logger(__name__)


def create(cmd, vm_name, resource_group_name, repair_password=None, repair_username=None, repair_vm_name=None, copy_disk_name=None, repair_group_name=None, unlock_encrypted_vm=False, enable_nested=False, associate_public_ip=False, distro='ubuntu', yes=False, encrypt_recovery_key=""):
def create(cmd, vm_name, resource_group_name, repair_password=None, repair_username=None, repair_vm_name=None, copy_disk_name=None, repair_group_name=None, unlock_encrypted_vm=False, enable_nested=False, associate_public_ip=False, distro='ubuntu', yes=False, encrypt_recovery_key="", disable_trusted_launch=False):
# Breaking change warning
logger.warning('After the November 2024 release, if the image of the source Windows VM is not found, the \'az vm repair create\' command will default to use a 2022-Datacenter image for the repair VM.')

# log all the parameters not logging the bitlocker key
logger.debug('vm repair create command parameters: vm_name: %s, resource_group_name: %s, repair_password: %s, repair_username: %s, repair_vm_name: %s, copy_disk_name: %s, repair_group_name: %s, unlock_encrypted_vm: %s, enable_nested: %s, associate_public_ip: %s, distro: %s, yes: %s', vm_name, resource_group_name, repair_password, repair_username, repair_vm_name, copy_disk_name, repair_group_name, unlock_encrypted_vm, enable_nested, associate_public_ip, distro, yes)
logger.debug('vm repair create command parameters: vm_name: %s, resource_group_name: %s, repair_password: %s, repair_username: %s, repair_vm_name: %s, copy_disk_name: %s, repair_group_name: %s, unlock_encrypted_vm: %s, enable_nested: %s, associate_public_ip: %s, distro: %s, yes: %s, disable_trusted_launch: %s', vm_name, resource_group_name, repair_password, repair_username, repair_vm_name, copy_disk_name, repair_group_name, unlock_encrypted_vm, enable_nested, associate_public_ip, distro, yes, disable_trusted_launch)

# Init command helper object
command = command_helper(logger, cmd, 'vm repair create')
Expand Down Expand Up @@ -118,17 +117,25 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern
zone = source_vm.zones[0]
create_repair_vm_command += ' --zone {zone}'.format(zone=zone)

if encrypt_recovery_key:
# For confidential VM and Trusted VM security tags some of the SKU expects the right security type, secure_boot_enabled and vtpm_enabled
logger.debug('Fetching VM security profile...')
vm_security_params = _fetch_vm_security_profile_parameters(source_vm)
if vm_security_params:
create_repair_vm_command += vm_security_params

logger.debug('Fetching OS Disk security profile...')
osdisk_security_params = _fetch_osdisk_security_profile_parameters(source_vm)
if osdisk_security_params:
create_repair_vm_command += osdisk_security_params
if disable_trusted_launch:
logger.debug('Set security-type to Standard...')
create_repair_vm_command += ' --security-type Standard'
else:
# For confidential VM and Trusted VM security tags some of the SKU expects the right security type, secure_boot_enabled and vtpm_enabled

# Note: I don't think TL VMs have an envrypted disk, so I don't think they need the encrypt_recovery_key parameter. Needs to be investigated and reworked.
# TL VMs still have a security profile to copy over though.
if encrypt_recovery_key:
logger.debug('Fetching VM security profile...')
vm_security_params = _fetch_vm_security_profile_parameters(source_vm)
if vm_security_params:
create_repair_vm_command += vm_security_params
# For confidential VM and Trusted Launch VM security tags on disks, the disk security profile needs to be brought over as well.
if encrypt_recovery_key:
logger.debug('Fetching OS Disk security profile...')
osdisk_security_params = _fetch_osdisk_security_profile_parameters(source_vm)
if osdisk_security_params:
create_repair_vm_command += osdisk_security_params

# Create new resource group
existing_rg = _check_existing_rg(repair_group_name)
Expand Down
5 changes: 3 additions & 2 deletions src/vm-repair/azext_vm_repair/repair_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ def _unlock_singlepass_encrypted_disk(repair_vm_name, repair_group_name, is_linu

def _unlock_singlepass_encrypted_disk_fallback(source_vm, resource_group_name, repair_vm_name, repair_group_name, copy_disk_name, is_linux):
"""
This method is not actually invoked.
Fallback for unlocking disk when script fails. This will install the ADE extension to unlock the Data disk.
"""

Expand Down Expand Up @@ -507,13 +508,13 @@ def _fetch_compatible_windows_os_urn_v2(source_vm):
offer = source_vm.storage_profile.image_reference.offer
publisher = source_vm.storage_profile.image_reference.publisher
fetch_urn_command = 'az vm image list -s {sku} -f {offer} -p {publisher} -l {loc} --verbose --all --query "[?sku==\'{sku}\'].urn | reverse(sort(@))" -o json'.format(loc=location, sku=sku, offer=offer, publisher=publisher)
logger.info('Fetching compatible Windows OS images from gallery...')
logger.info('Fetching compatible Windows OS images from gallery V2...')
urns = loads(_call_az_command(fetch_urn_command))

if not urns or len(urns) == 0:
# If source SKU not available then defaulting 2022 datacenter image.
fetch_urn_command = 'az vm image list -s "2022-Datacenter" -f WindowsServer -p MicrosoftWindowsServer -l {loc} --verbose --all --query "[?sku==\'2022-datacenter\'].urn | reverse(sort(@))" -o json'.format(loc=location)
logger.info('Fetching compatible Windows OS images from gallery for 2022 Datacenter...')
logger.info('Fetching compatible Windows OS images from gallery for 2022 Datacenter V2...')
urns = loads(_call_az_command(fetch_urn_command))

# No OS images available for Windows2016
Expand Down
41 changes: 41 additions & 0 deletions src/vm-repair/azext_vm_repair/tests/latest/test_repair_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,47 @@ def test_vmrepair_ConfidentialVMAndUnlockDisk(self, resource_group):
# Call Restore
self.cmd('vm repair restore -g {rg} -n {vm} --yes')

# Check swapped OS disk
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
source_vm = vms[0]
assert source_vm['storageProfile']['osDisk']['name'] == result['copied_disk_name']


@pytest.mark.winTrustedLaunchVMFlag
class WindowsTrustedLaunchVMFlag(LiveScenarioTest):

@ResourceGroupPreparer(location='westus2')
def test_vmrepair_TrustedLaunchVMFlag(self, resource_group):
self.kwargs.update({
'vm': 'vm1',
'rg': resource_group
})

# Create test VM
# need to create a TL VM
self.cmd('vm create -g {rg} -n {vm} --admin-username azureadmin --admin-password !Passw0rd2024 --image Win2022Datacenter')
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
# Something wrong with vm create command if it fails here
assert len(vms) == 1

# Test create
result = self.cmd('vm repair create -g {rg} -n {vm} --repair-username azureadmin --repair-password !Passw0rd2024 --yes --unlock-encrypted-vm --encrypt-recovery-key !Passw0rd2024 --disable-trusted-launch --verbose -o json').get_output_in_json()
assert result['status'] == STATUS_SUCCESS, result['error_message']

# Check repair VM
repair_vms = self.cmd('vm list -g {} -o json'.format(result['repair_resource_group'])).get_output_in_json()
assert len(repair_vms) == 1
repair_vm = repair_vms[0]
print("Printing repair VM Output")
print(repair_vm)
security_profile_repair_vm = repair_vm.get("securityProfile")
assert security_profile_repair_vm is None
# Check attached data disk
assert repair_vm['storageProfile']['dataDisks'][0]['name'] == result['copied_disk_name']

# Call Restore
self.cmd('vm repair restore -g {rg} -n {vm} --yes')

# Check swapped OS disk
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
source_vm = vms[0]
Expand Down
2 changes: 0 additions & 2 deletions src/vm-repair/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
from codecs import open
from setuptools import setup, find_packages


VERSION = "1.1.0"


CLASSIFIERS = [
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
Expand Down

0 comments on commit 2127744

Please sign in to comment.