@@ -67,6 +67,54 @@ def _call_az_command(command_string, run_async=False, secure_params=None):
67
67
return None
68
68
69
69
70
+ def _invoke_run_command (script_name , vm_name , rg_name , is_linux , parameters = None , additional_custom_scripts = None ):
71
+ """
72
+ Use azure run command to run the scripts within the vm-repair/scripts file and return stdout, stderr.
73
+ """
74
+
75
+ REPAIR_DIR_NAME = 'azext_vm_repair'
76
+ SCRIPTS_DIR_NAME = 'scripts'
77
+ RUN_COMMAND_RUN_SHELL_ID = 'RunShellScript'
78
+ RUN_COMMAND_RUN_PS_ID = 'RunPowerShellScript'
79
+
80
+ # Build absoulte path of driver script
81
+ loader = pkgutil .get_loader (REPAIR_DIR_NAME )
82
+ mod = loader .load_module (REPAIR_DIR_NAME )
83
+ rootpath = os .path .dirname (mod .__file__ )
84
+ run_script = os .path .join (rootpath , SCRIPTS_DIR_NAME , script_name )
85
+
86
+ if is_linux :
87
+ command_id = RUN_COMMAND_RUN_SHELL_ID
88
+ else :
89
+ command_id = RUN_COMMAND_RUN_PS_ID
90
+
91
+ # Process script list to scripts string
92
+ additional_scripts_string = ''
93
+ if additional_custom_scripts :
94
+ for script in additional_custom_scripts :
95
+ additional_scripts_string += ' "@{script_name}"' .format (script_name = script )
96
+
97
+ run_command = 'az vm run-command invoke -g {rg} -n {vm} --command-id {command_id} ' \
98
+ '--scripts @"{run_script}"{additional_scripts} -o json' \
99
+ .format (rg = rg_name , vm = vm_name , command_id = command_id , run_script = run_script , additional_scripts = additional_scripts_string )
100
+ if parameters :
101
+ run_command += " --parameters {params}" .format (params = ' ' .join (parameters ))
102
+ return_str = _call_az_command (run_command )
103
+
104
+ # Extract stdout and stderr, if stderr exists then possible error
105
+ run_command_return = loads (return_str )
106
+
107
+ if is_linux :
108
+ run_command_message = run_command_return ['value' ][0 ]['message' ].split ('[stdout]' )[1 ].split ('[stderr]' )
109
+ stdout = run_command_message [0 ].strip ('\n ' )
110
+ stderr = run_command_message [1 ].strip ('\n ' )
111
+ else :
112
+ stdout = run_command_return ['value' ][0 ]['message' ]
113
+ stderr = run_command_return ['value' ][1 ]['message' ]
114
+
115
+ return stdout , stderr
116
+
117
+
70
118
def _get_current_vmrepair_version ():
71
119
from azure .cli .core .extension .operations import list_extensions
72
120
version = [ext ['version' ] for ext in list_extensions () if ext ['name' ] == 'vm-repair' ]
@@ -186,27 +234,46 @@ def _list_resource_ids_in_rg(resource_group_name):
186
234
def _fetch_encryption_settings (source_vm ):
187
235
key_vault = None
188
236
kekurl = None
237
+ secreturl = None
189
238
if source_vm .storage_profile .os_disk .encryption_settings is not None :
190
- return Encryption .DUAL , key_vault , kekurl
239
+ return Encryption .DUAL , key_vault , kekurl , secreturl
191
240
# Unmanaged disk only support dual
192
241
if not _uses_managed_disk (source_vm ):
193
- return Encryption .NONE , key_vault , kekurl
242
+ return Encryption .NONE , key_vault , kekurl , secreturl
194
243
195
244
disk_id = source_vm .storage_profile .os_disk .managed_disk .id
196
- show_disk_command = 'az disk show --id {i} --query [encryptionSettingsCollection,encryptionSettingsCollection.encryptionSettings[].diskEncryptionKey.sourceVault.id,encryptionSettingsCollection.encryptionSettings[].keyEncryptionKey.keyUrl] -o json' .format (i = disk_id )
197
- encryption_type , key_vault , kekurl = loads (_call_az_command (show_disk_command ))
245
+ show_disk_command = 'az disk show --id {i} --query [encryptionSettingsCollection,encryptionSettingsCollection.encryptionSettings[].diskEncryptionKey.sourceVault.id,encryptionSettingsCollection.encryptionSettings[].keyEncryptionKey.keyUrl,encryptionSettingsCollection.encryptionSettings[].diskEncryptionKey.secretUrl] -o json' \
246
+ .format (i = disk_id )
247
+ encryption_type , key_vault , kekurl , secreturl = loads (_call_az_command (show_disk_command ))
198
248
if [encryption_type , key_vault , kekurl ] == [None , None , None ]:
199
- return Encryption .NONE , key_vault , kekurl
249
+ return Encryption .NONE , key_vault , kekurl , secreturl
200
250
if kekurl == []:
201
- key_vault = key_vault [0 ]
202
- return Encryption .SINGLE_WITHOUT_KEK , key_vault , kekurl
203
- key_vault , kekurl = key_vault [0 ], kekurl [0 ]
204
- return Encryption .SINGLE_WITH_KEK , key_vault , kekurl
251
+ key_vault , secreturl = key_vault [0 ], secreturl [0 ]
252
+ return Encryption .SINGLE_WITHOUT_KEK , key_vault , kekurl , secreturl
253
+ key_vault , kekurl , secreturl = key_vault [0 ], kekurl [0 ], secreturl [0 ]
254
+ return Encryption .SINGLE_WITH_KEK , key_vault , kekurl , secreturl
255
+
256
+
257
+ def _secret_tag_check (resource_group_name , copy_disk_name , secreturl ):
258
+ DEFAULT_LINUXPASSPHRASE_FILENAME = 'LinuxPassPhraseFileName'
259
+ show_disk_command = 'az disk show -g {g} -n {n} --query encryptionSettingsCollection.encryptionSettings[].diskEncryptionKey.secretUrl -o json' \
260
+ .format (n = copy_disk_name , g = resource_group_name )
261
+ secreturl_new = loads (_call_az_command (show_disk_command ))[0 ]
262
+ if secreturl == secreturl_new :
263
+ logger .debug ('Secret urls are same. Skipping the tag check...' )
264
+ else :
265
+ logger .debug ('Secret urls are not same. Changing the tag...' )
266
+ show_tag_command = 'az keyvault secret show --id {securl} --query [tags.DiskEncryptionKeyEncryptionAlgorithm,tags.DiskEncryptionKeyEncryptionKeyURL] -o json' \
267
+ .format (securl = secreturl_new )
268
+ algorithm , keyurl = loads (_call_az_command (show_tag_command ))
269
+ set_tag_command = 'az keyvault secret set-attributes --tags DiskEncryptionKeyFileName={keyfile} DiskEncryptionKeyEncryptionAlgorithm={alg} DiskEncryptionKeyEncryptionKeyURL={kekurl} --id {securl}' \
270
+ .format (keyfile = DEFAULT_LINUXPASSPHRASE_FILENAME , alg = algorithm , kekurl = keyurl , securl = secreturl_new )
271
+ _call_az_command (set_tag_command )
205
272
206
273
207
- def _unlock_singlepass_encrypted_disk (source_vm , is_linux , repair_group_name , repair_vm_name ):
274
+ def _unlock_singlepass_encrypted_disk (source_vm , resource_group_name , repair_vm_name , repair_group_name , copy_disk_name , is_linux ):
208
275
# Installs the extension on repair VM and mounts the disk after unlocking.
209
- encryption_type , key_vault , kekurl = _fetch_encryption_settings (source_vm )
276
+ encryption_type , key_vault , kekurl , secreturl = _fetch_encryption_settings (source_vm )
210
277
if is_linux :
211
278
volume_type = 'DATA'
212
279
else :
@@ -219,36 +286,33 @@ def _unlock_singlepass_encrypted_disk(source_vm, is_linux, repair_group_name, re
219
286
elif encryption_type is Encryption .SINGLE_WITHOUT_KEK :
220
287
install_ade_extension_command = 'az vm encryption enable --disk-encryption-keyvault {vault} --name {repair} --resource-group {g} --volume-type {volume}' \
221
288
.format (g = repair_group_name , repair = repair_vm_name , vault = key_vault , volume = volume_type )
289
+ # Add format-all flag for linux vms
290
+ if is_linux :
291
+ install_ade_extension_command += " --encrypt-format-all"
222
292
logger .info ('Unlocking attached copied disk...' )
223
293
_call_az_command (install_ade_extension_command )
224
294
# Linux VM encryption extension has a bug and we need to manually unlock and mount its disk
225
295
if is_linux :
296
+ # Validating secret tag and setting original tag if it got changed
297
+ _secret_tag_check (resource_group_name , copy_disk_name , secreturl )
226
298
logger .debug ("Manually unlocking and mounting disk for Linux VMs." )
227
- _manually_unlock_mount_encrypted_disk (repair_group_name , repair_vm_name )
299
+ _manually_unlock_mount_encrypted_disk (repair_vm_name , repair_group_name )
228
300
except AzCommandError as azCommandError :
229
301
error_message = str (azCommandError )
230
302
# Linux VM encryption extension bug where it fails and then continue to mount disk manually
231
303
if is_linux and "Failed to encrypt data volumes with error" in error_message :
232
304
logger .debug ("Expected bug for linux VMs. Ignoring error." )
233
- _manually_unlock_mount_encrypted_disk (repair_group_name , repair_vm_name )
305
+ # Validating secret tag and setting original tag if it got changed
306
+ _secret_tag_check (resource_group_name , copy_disk_name , secreturl )
307
+ _manually_unlock_mount_encrypted_disk (repair_vm_name , repair_group_name )
234
308
else :
235
309
raise
236
310
237
311
238
- def _manually_unlock_mount_encrypted_disk (repair_group_name , repair_vm_name ):
312
+ def _manually_unlock_mount_encrypted_disk (repair_vm_name , repair_group_name ):
239
313
# Unlocks the disk using the phasephrase and mounts it on the repair VM.
240
- REPAIR_DIR_NAME = 'azext_vm_repair'
241
- SCRIPTS_DIR_NAME = 'scripts'
242
314
LINUX_RUN_SCRIPT_NAME = 'mount-encrypted-disk.sh'
243
- command_id = 'RunShellScript'
244
- loader = pkgutil .get_loader (REPAIR_DIR_NAME )
245
- mod = loader .load_module (REPAIR_DIR_NAME )
246
- rootpath = os .path .dirname (mod .__file__ )
247
- run_script = os .path .join (rootpath , SCRIPTS_DIR_NAME , LINUX_RUN_SCRIPT_NAME )
248
- mount_disk_command = 'az vm run-command invoke -g {rg} -n {vm} --command-id {command_id} ' \
249
- '--scripts "@{run_script}" -o json' \
250
- .format (rg = repair_group_name , vm = repair_vm_name , command_id = command_id , run_script = run_script )
251
- _call_az_command (mount_disk_command )
315
+ return _invoke_run_command (LINUX_RUN_SCRIPT_NAME , repair_vm_name , repair_group_name , True )
252
316
253
317
254
318
def _fetch_compatible_windows_os_urn (source_vm ):
0 commit comments