1# -------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for 4# license information. 5# 6# Code generated by Microsoft (R) AutoRest Code Generator. 7# Changes may cause incorrect behavior and will be lost if the code is 8# regenerated. 9# 10# Generation mode: Incremental 11# -------------------------------------------------------------------------- 12 13# pylint: disable=no-self-use,too-many-lines,no-else-return 14import json 15import os 16 17import requests 18 19try: 20 from urllib.parse import urlparse 21except ImportError: 22 from urlparse import urlparse # pylint: disable=import-error 23 24# the urlopen is imported for automation purpose 25from six.moves.urllib.request import urlopen # noqa, pylint: disable=import-error,unused-import,ungrouped-imports 26 27from knack.log import get_logger 28from knack.util import CLIError 29from azure.cli.core.azclierror import CLIInternalError, ValidationError, RequiredArgumentMissingError 30 31from azure.cli.command_modules.vm._validators import _get_resource_group_from_vault_name 32from azure.cli.core.commands.validators import validate_file_or_dict 33 34from azure.cli.core.commands import LongRunningOperation, DeploymentOutputLongRunningOperation 35from azure.cli.core.commands.client_factory import get_mgmt_service_client, get_data_service_client 36from azure.cli.core.profiles import ResourceType 37from azure.cli.core.util import sdk_no_wait 38 39from ._vm_utils import read_content_if_is_file 40from ._vm_diagnostics_templates import get_default_diag_config 41 42from ._actions import (load_images_from_aliases_doc, load_extension_images_thru_services, 43 load_images_thru_services, _get_latest_image_version) 44from ._client_factory import (_compute_client_factory, cf_public_ip_addresses, cf_vm_image_term, 45 _dev_test_labs_client_factory) 46 47from .generated.custom import * # noqa: F403, pylint: disable=unused-wildcard-import,wildcard-import 48try: 49 from .manual.custom import * # noqa: F403, pylint: disable=unused-wildcard-import,wildcard-import 50except ImportError: 51 pass 52 53logger = get_logger(__name__) 54 55 56# Use the same name by portal, so people can update from both cli and portal 57# (VM doesn't allow multiple handlers for the same extension) 58_ACCESS_EXT_HANDLER_NAME = 'enablevmaccess' 59 60_LINUX_ACCESS_EXT = 'VMAccessForLinux' 61_WINDOWS_ACCESS_EXT = 'VMAccessAgent' 62_LINUX_DIAG_EXT = 'LinuxDiagnostic' 63_WINDOWS_DIAG_EXT = 'IaaSDiagnostics' 64_LINUX_OMS_AGENT_EXT = 'OmsAgentForLinux' 65_WINDOWS_OMS_AGENT_EXT = 'MicrosoftMonitoringAgent' 66extension_mappings = { 67 _LINUX_ACCESS_EXT: { 68 'version': '1.5', 69 'publisher': 'Microsoft.OSTCExtensions' 70 }, 71 _WINDOWS_ACCESS_EXT: { 72 'version': '2.4', 73 'publisher': 'Microsoft.Compute' 74 }, 75 _LINUX_DIAG_EXT: { 76 'version': '3.0', 77 'publisher': 'Microsoft.Azure.Diagnostics' 78 }, 79 _WINDOWS_DIAG_EXT: { 80 'version': '1.5', 81 'publisher': 'Microsoft.Azure.Diagnostics' 82 }, 83 _LINUX_OMS_AGENT_EXT: { 84 'version': '1.0', 85 'publisher': 'Microsoft.EnterpriseCloud.Monitoring' 86 }, 87 _WINDOWS_OMS_AGENT_EXT: { 88 'version': '1.0', 89 'publisher': 'Microsoft.EnterpriseCloud.Monitoring' 90 } 91} 92 93 94def _construct_identity_info(identity_scope, identity_role, implicit_identity, external_identities): 95 info = {} 96 if identity_scope: 97 info['scope'] = identity_scope 98 info['role'] = str(identity_role) # could be DefaultStr, so convert to string 99 info['userAssignedIdentities'] = external_identities or {} 100 info['systemAssignedIdentity'] = implicit_identity or '' 101 return info 102 103 104# for injecting test seams to produce predicatable role assignment id for playback 105def _gen_guid(): 106 import uuid 107 return uuid.uuid4() 108 109 110def _get_access_extension_upgrade_info(extensions, name): 111 version = extension_mappings[name]['version'] 112 publisher = extension_mappings[name]['publisher'] 113 114 auto_upgrade = None 115 116 if extensions: 117 extension = next((e for e in extensions if e.name == name), None) 118 from packaging.version import parse # pylint: disable=no-name-in-module,import-error 119 if extension and parse(extension.type_handler_version) < parse(version): 120 auto_upgrade = True 121 elif extension and parse(extension.type_handler_version) > parse(version): 122 version = extension.type_handler_version 123 124 return publisher, version, auto_upgrade 125 126 127def _get_extension_instance_name(instance_view, publisher, extension_type_name, 128 suggested_name=None): 129 extension_instance_name = suggested_name or extension_type_name 130 full_type_name = '.'.join([publisher, extension_type_name]) 131 if instance_view.extensions: 132 ext = next((x for x in instance_view.extensions 133 if x.type and (x.type.lower() == full_type_name.lower())), None) 134 if ext: 135 extension_instance_name = ext.name 136 return extension_instance_name 137 138 139def _get_storage_management_client(cli_ctx): 140 return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_STORAGE) 141 142 143def _get_disk_lun(data_disks): 144 # start from 0, search for unused int for lun 145 if not data_disks: 146 return 0 147 148 existing_luns = sorted([d.lun for d in data_disks]) 149 for i, current in enumerate(existing_luns): 150 if current != i: 151 return i 152 return len(existing_luns) 153 154 155def _get_private_config(cli_ctx, resource_group_name, storage_account): 156 storage_mgmt_client = _get_storage_management_client(cli_ctx) 157 # pylint: disable=no-member 158 keys = storage_mgmt_client.storage_accounts.list_keys(resource_group_name, storage_account).keys 159 160 private_config = { 161 'storageAccountName': storage_account, 162 'storageAccountKey': keys[0].value 163 } 164 return private_config 165 166 167def _get_resource_group_location(cli_ctx, resource_group_name): 168 client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) 169 # pylint: disable=no-member 170 return client.resource_groups.get(resource_group_name).location 171 172 173def _get_sku_object(cmd, sku): 174 if cmd.supported_api_version(min_api='2017-03-30'): 175 DiskSku = cmd.get_models('DiskSku') 176 return DiskSku(name=sku) 177 return sku 178 179 180def _grant_access(cmd, resource_group_name, name, duration_in_seconds, is_disk, access_level): 181 AccessLevel, GrantAccessData = cmd.get_models('AccessLevel', 'GrantAccessData') 182 client = _compute_client_factory(cmd.cli_ctx) 183 op = client.disks if is_disk else client.snapshots 184 grant_access_data = GrantAccessData( 185 access=access_level or AccessLevel.read, duration_in_seconds=duration_in_seconds) 186 return op.begin_grant_access(resource_group_name, name, grant_access_data) 187 188 189def _is_linux_os(vm): 190 os_type = None 191 if vm and vm.storage_profile and vm.storage_profile.os_disk and vm.storage_profile.os_disk.os_type: 192 os_type = vm.storage_profile.os_disk.os_type 193 if os_type: 194 return os_type.lower() == 'linux' 195 # the os_type could be None for VM scaleset, let us check out os configurations 196 if vm.os_profile.linux_configuration: 197 return bool(vm.os_profile.linux_configuration) 198 return False 199 200 201def _merge_secrets(secrets): 202 """ 203 Merge a list of secrets. Each secret should be a dict fitting the following JSON structure: 204 [{ "sourceVault": { "id": "value" }, 205 "vaultCertificates": [{ "certificateUrl": "value", 206 "certificateStore": "cert store name (only on windows)"}] }] 207 The array of secrets is merged on sourceVault.id. 208 :param secrets: 209 :return: 210 """ 211 merged = {} 212 vc_name = 'vaultCertificates' 213 for outer in secrets: 214 for secret in outer: 215 if secret['sourceVault']['id'] not in merged: 216 merged[secret['sourceVault']['id']] = [] 217 merged[secret['sourceVault']['id']] = \ 218 secret[vc_name] + merged[secret['sourceVault']['id']] 219 220 # transform the reduced map to vm format 221 formatted = [{'sourceVault': {'id': source_id}, 222 'vaultCertificates': value} 223 for source_id, value in list(merged.items())] 224 return formatted 225 226 227def _normalize_extension_version(cli_ctx, publisher, vm_extension_name, version, location): 228 229 def _trim_away_build_number(version): 230 # workaround a known issue: the version must only contain "major.minor", even though 231 # "extension image list" gives more detail 232 return '.'.join(version.split('.')[0:2]) 233 234 if not version: 235 result = load_extension_images_thru_services(cli_ctx, publisher, vm_extension_name, None, location, 236 show_latest=True, partial_match=False) 237 if not result: 238 raise CLIError('Failed to find the latest version for the extension "{}"'.format(vm_extension_name)) 239 # with 'show_latest' enabled, we will only get one result. 240 version = result[0]['version'] 241 242 version = _trim_away_build_number(version) 243 return version 244 245 246def _parse_rg_name(strid): 247 '''From an ID, extract the contained (resource group, name) tuple.''' 248 from msrestazure.tools import parse_resource_id 249 parts = parse_resource_id(strid) 250 return (parts['resource_group'], parts['name']) 251 252 253def _set_sku(cmd, instance, sku): 254 if cmd.supported_api_version(min_api='2017-03-30'): 255 instance.sku = cmd.get_models('DiskSku')(name=sku) 256 else: 257 instance.account_type = sku 258 259 260def _show_missing_access_warning(resource_group, name, command): 261 warn = ("No access was given yet to the '{1}', because '--scope' was not provided. " 262 "You should setup by creating a role assignment, e.g. " 263 "'az role assignment create --assignee <principal-id> --role contributor -g {0}' " 264 "would let it access the current resource group. To get the pricipal id, run " 265 "'az {2} show -g {0} -n {1} --query \"identity.principalId\" -otsv'".format(resource_group, name, command)) 266 logger.warning(warn) 267 268 269def _parse_aux_subscriptions(resource_id): 270 from msrestazure.tools import is_valid_resource_id, parse_resource_id 271 if is_valid_resource_id(resource_id): 272 res = parse_resource_id(resource_id) 273 return [res['subscription']] 274 return None 275 276 277# Hide extension information from output as the info is not correct and unhelpful; also 278# commands using it mean to hide the extension concept from users. 279class ExtensionUpdateLongRunningOperation(LongRunningOperation): # pylint: disable=too-few-public-methods 280 pass 281 282 283# region Disks (Managed) 284def create_managed_disk(cmd, resource_group_name, disk_name, location=None, # pylint: disable=too-many-locals, too-many-branches, too-many-statements 285 size_gb=None, sku='Premium_LRS', os_type=None, 286 source=None, for_upload=None, upload_size_bytes=None, # pylint: disable=unused-argument 287 # below are generated internally from 'source' 288 source_blob_uri=None, source_disk=None, source_snapshot=None, 289 source_storage_account_id=None, no_wait=False, tags=None, zone=None, 290 disk_iops_read_write=None, disk_mbps_read_write=None, hyper_v_generation=None, 291 encryption_type=None, disk_encryption_set=None, max_shares=None, 292 disk_iops_read_only=None, disk_mbps_read_only=None, 293 image_reference=None, image_reference_lun=None, 294 gallery_image_reference=None, gallery_image_reference_lun=None, 295 network_access_policy=None, disk_access=None, logical_sector_size=None, 296 tier=None, enable_bursting=None, edge_zone=None, security_type=None, support_hibernation=None): 297 from msrestazure.tools import resource_id, is_valid_resource_id 298 from azure.cli.core.commands.client_factory import get_subscription_id 299 300 Disk, CreationData, DiskCreateOption, Encryption = cmd.get_models( 301 'Disk', 'CreationData', 'DiskCreateOption', 'Encryption') 302 303 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 304 if source_blob_uri: 305 option = DiskCreateOption.import_enum 306 elif source_disk or source_snapshot: 307 option = DiskCreateOption.copy 308 elif for_upload: 309 option = DiskCreateOption.upload 310 elif image_reference or gallery_image_reference: 311 option = DiskCreateOption.from_image 312 else: 313 option = DiskCreateOption.empty 314 315 if source_storage_account_id is None and source_blob_uri is not None: 316 subscription_id = get_subscription_id(cmd.cli_ctx) 317 storage_account_name = source_blob_uri.split('.')[0].split('/')[-1] 318 source_storage_account_id = resource_id( 319 subscription=subscription_id, resource_group=resource_group_name, 320 namespace='Microsoft.Storage', type='storageAccounts', name=storage_account_name) 321 322 if upload_size_bytes is not None and for_upload is not True: 323 raise CLIError('usage error: --upload-size-bytes should be used together with --for-upload') 324 325 if image_reference is not None: 326 if not is_valid_resource_id(image_reference): 327 # URN or name 328 terms = image_reference.split(':') 329 if len(terms) == 4: # URN 330 disk_publisher, disk_offer, disk_sku, disk_version = terms[0], terms[1], terms[2], terms[3] 331 if disk_version.lower() == 'latest': 332 disk_version = _get_latest_image_version(cmd.cli_ctx, location, disk_publisher, disk_offer, 333 disk_sku) 334 client = _compute_client_factory(cmd.cli_ctx) 335 response = client.virtual_machine_images.get(location, disk_publisher, disk_offer, disk_sku, 336 disk_version) 337 image_reference = response.id 338 else: # error 339 raise CLIError('usage error: --image-reference should be ID or URN (publisher:offer:sku:version).') 340 # image_reference is an ID now 341 image_reference = {'id': image_reference} 342 if image_reference_lun is not None: 343 image_reference['lun'] = image_reference_lun 344 345 if gallery_image_reference is not None: 346 gallery_image_reference = {'id': gallery_image_reference} 347 if gallery_image_reference_lun is not None: 348 gallery_image_reference['lun'] = gallery_image_reference_lun 349 350 creation_data = CreationData(create_option=option, source_uri=source_blob_uri, 351 image_reference=image_reference, gallery_image_reference=gallery_image_reference, 352 source_resource_id=source_disk or source_snapshot, 353 storage_account_id=source_storage_account_id, 354 upload_size_bytes=upload_size_bytes, 355 logical_sector_size=logical_sector_size) 356 357 if size_gb is None and upload_size_bytes is None and (option == DiskCreateOption.empty or for_upload): 358 raise CLIError('usage error: --size-gb or --upload-size-bytes required to create an empty disk') 359 360 if disk_encryption_set is not None and not is_valid_resource_id(disk_encryption_set): 361 disk_encryption_set = resource_id( 362 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 363 namespace='Microsoft.Compute', type='diskEncryptionSets', name=disk_encryption_set) 364 365 if disk_access is not None and not is_valid_resource_id(disk_access): 366 disk_access = resource_id( 367 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 368 namespace='Microsoft.Compute', type='diskAccesses', name=disk_access) 369 370 encryption = None 371 if disk_encryption_set: 372 encryption = Encryption(type=encryption_type, disk_encryption_set_id=disk_encryption_set) 373 374 disk = Disk(location=location, creation_data=creation_data, tags=(tags or {}), 375 sku=_get_sku_object(cmd, sku), disk_size_gb=size_gb, os_type=os_type, encryption=encryption) 376 377 if hyper_v_generation: 378 disk.hyper_v_generation = hyper_v_generation 379 380 if zone: 381 disk.zones = zone 382 if disk_iops_read_write is not None: 383 disk.disk_iops_read_write = disk_iops_read_write 384 if disk_mbps_read_write is not None: 385 disk.disk_m_bps_read_write = disk_mbps_read_write 386 if max_shares is not None: 387 disk.max_shares = max_shares 388 if disk_iops_read_only is not None: 389 disk.disk_iops_read_only = disk_iops_read_only 390 if disk_mbps_read_only is not None: 391 disk.disk_m_bps_read_only = disk_mbps_read_only 392 if network_access_policy is not None: 393 disk.network_access_policy = network_access_policy 394 if disk_access is not None: 395 disk.disk_access_id = disk_access 396 if tier is not None: 397 disk.tier = tier 398 if enable_bursting is not None: 399 disk.bursting_enabled = enable_bursting 400 if edge_zone is not None: 401 disk.extended_location = edge_zone 402 if security_type is not None: 403 disk.security_profile = {'securityType': security_type} 404 if support_hibernation is not None: 405 disk.supports_hibernation = support_hibernation 406 407 client = _compute_client_factory(cmd.cli_ctx) 408 return sdk_no_wait(no_wait, client.disks.begin_create_or_update, resource_group_name, disk_name, disk) 409 410 411def grant_disk_access(cmd, resource_group_name, disk_name, duration_in_seconds, access_level=None): 412 return _grant_access(cmd, resource_group_name, disk_name, duration_in_seconds, is_disk=True, 413 access_level=access_level) 414 415 416def list_managed_disks(cmd, resource_group_name=None): 417 client = _compute_client_factory(cmd.cli_ctx) 418 if resource_group_name: 419 return client.disks.list_by_resource_group(resource_group_name) 420 return client.disks.list() 421 422 423def update_managed_disk(cmd, resource_group_name, instance, size_gb=None, sku=None, disk_iops_read_write=None, 424 disk_mbps_read_write=None, encryption_type=None, disk_encryption_set=None, 425 network_access_policy=None, disk_access=None, max_shares=None, disk_iops_read_only=None, 426 disk_mbps_read_only=None, enable_bursting=None): 427 from msrestazure.tools import resource_id, is_valid_resource_id 428 from azure.cli.core.commands.client_factory import get_subscription_id 429 430 if size_gb is not None: 431 instance.disk_size_gb = size_gb 432 if sku is not None: 433 _set_sku(cmd, instance, sku) 434 if disk_iops_read_write is not None: 435 instance.disk_iops_read_write = disk_iops_read_write 436 if disk_mbps_read_write is not None: 437 instance.disk_m_bps_read_write = disk_mbps_read_write 438 if disk_iops_read_only is not None: 439 instance.disk_iops_read_only = disk_iops_read_only 440 if disk_mbps_read_only is not None: 441 instance.disk_m_bps_read_only = disk_mbps_read_only 442 if max_shares is not None: 443 instance.max_shares = max_shares 444 if disk_encryption_set is not None: 445 if instance.encryption.type != 'EncryptionAtRestWithCustomerKey' and \ 446 encryption_type != 'EncryptionAtRestWithCustomerKey': 447 raise CLIError('usage error: Please set --encryption-type to EncryptionAtRestWithCustomerKey') 448 if not is_valid_resource_id(disk_encryption_set): 449 disk_encryption_set = resource_id( 450 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 451 namespace='Microsoft.Compute', type='diskEncryptionSets', name=disk_encryption_set) 452 instance.encryption.disk_encryption_set_id = disk_encryption_set 453 if encryption_type is not None: 454 instance.encryption.type = encryption_type 455 if network_access_policy is not None: 456 instance.network_access_policy = network_access_policy 457 if disk_access is not None and not is_valid_resource_id(disk_access): 458 disk_access = resource_id( 459 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 460 namespace='Microsoft.Compute', type='diskAccesses', name=disk_access) 461 instance.disk_access_id = disk_access 462 if enable_bursting is not None: 463 instance.bursting_enabled = enable_bursting 464 return instance 465# endregion 466 467 468# region Images (Managed) 469def create_image(cmd, resource_group_name, name, source, os_type=None, data_disk_sources=None, location=None, # pylint: disable=too-many-locals,unused-argument 470 # below are generated internally from 'source' and 'data_disk_sources' 471 source_virtual_machine=None, storage_sku=None, hyper_v_generation=None, 472 os_blob_uri=None, data_blob_uris=None, 473 os_snapshot=None, data_snapshots=None, 474 os_disk=None, os_disk_caching=None, data_disks=None, data_disk_caching=None, 475 tags=None, zone_resilient=None, edge_zone=None): 476 ImageOSDisk, ImageDataDisk, ImageStorageProfile, Image, SubResource, OperatingSystemStateTypes = cmd.get_models( 477 'ImageOSDisk', 'ImageDataDisk', 'ImageStorageProfile', 'Image', 'SubResource', 'OperatingSystemStateTypes') 478 479 if source_virtual_machine: 480 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 481 image_storage_profile = None if zone_resilient is None else ImageStorageProfile(zone_resilient=zone_resilient) 482 image = Image(location=location, source_virtual_machine=SubResource(id=source_virtual_machine), 483 storage_profile=image_storage_profile, tags=(tags or {})) 484 else: 485 os_disk = ImageOSDisk(os_type=os_type, 486 os_state=OperatingSystemStateTypes.generalized, 487 caching=os_disk_caching, 488 snapshot=SubResource(id=os_snapshot) if os_snapshot else None, 489 managed_disk=SubResource(id=os_disk) if os_disk else None, 490 blob_uri=os_blob_uri, 491 storage_account_type=storage_sku) 492 all_data_disks = [] 493 lun = 0 494 if data_blob_uris: 495 for d in data_blob_uris: 496 all_data_disks.append(ImageDataDisk(lun=lun, blob_uri=d, caching=data_disk_caching)) 497 lun += 1 498 if data_snapshots: 499 for d in data_snapshots: 500 all_data_disks.append(ImageDataDisk(lun=lun, snapshot=SubResource(id=d), caching=data_disk_caching)) 501 lun += 1 502 if data_disks: 503 for d in data_disks: 504 all_data_disks.append(ImageDataDisk(lun=lun, managed_disk=SubResource(id=d), caching=data_disk_caching)) 505 lun += 1 506 507 image_storage_profile = ImageStorageProfile(os_disk=os_disk, data_disks=all_data_disks) 508 if zone_resilient is not None: 509 image_storage_profile.zone_resilient = zone_resilient 510 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 511 # pylint disable=no-member 512 image = Image(location=location, storage_profile=image_storage_profile, tags=(tags or {})) 513 514 if hyper_v_generation: 515 image.hyper_v_generation = hyper_v_generation 516 517 if edge_zone: 518 image.extended_location = edge_zone 519 520 client = _compute_client_factory(cmd.cli_ctx) 521 return client.images.begin_create_or_update(resource_group_name, name, image) 522 523 524def update_image(instance, tags=None): 525 if tags is not None: 526 instance.tags = tags 527 return instance 528 529 530def list_images(cmd, resource_group_name=None): 531 client = _compute_client_factory(cmd.cli_ctx) 532 if resource_group_name: 533 return client.images.list_by_resource_group(resource_group_name) 534 return client.images.list() 535# endregion 536 537 538# region Snapshots 539# pylint: disable=unused-argument,too-many-locals 540def create_snapshot(cmd, resource_group_name, snapshot_name, location=None, size_gb=None, sku='Standard_LRS', 541 source=None, for_upload=None, incremental=None, 542 # below are generated internally from 'source' 543 source_blob_uri=None, source_disk=None, source_snapshot=None, source_storage_account_id=None, 544 hyper_v_generation=None, tags=None, no_wait=False, disk_encryption_set=None, 545 encryption_type=None, network_access_policy=None, disk_access=None, edge_zone=None): 546 from msrestazure.tools import resource_id, is_valid_resource_id 547 from azure.cli.core.commands.client_factory import get_subscription_id 548 549 Snapshot, CreationData, DiskCreateOption, Encryption = cmd.get_models( 550 'Snapshot', 'CreationData', 'DiskCreateOption', 'Encryption') 551 552 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 553 if source_blob_uri: 554 option = DiskCreateOption.import_enum 555 elif source_disk or source_snapshot: 556 option = DiskCreateOption.copy 557 elif for_upload: 558 option = DiskCreateOption.upload 559 else: 560 option = DiskCreateOption.empty 561 562 creation_data = CreationData(create_option=option, source_uri=source_blob_uri, 563 image_reference=None, 564 source_resource_id=source_disk or source_snapshot, 565 storage_account_id=source_storage_account_id) 566 567 if size_gb is None and option == DiskCreateOption.empty: 568 raise CLIError('Please supply size for the snapshots') 569 570 if disk_encryption_set is not None and not is_valid_resource_id(disk_encryption_set): 571 disk_encryption_set = resource_id( 572 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 573 namespace='Microsoft.Compute', type='diskEncryptionSets', name=disk_encryption_set) 574 575 if disk_access is not None and not is_valid_resource_id(disk_access): 576 disk_access = resource_id( 577 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 578 namespace='Microsoft.Compute', type='diskAccesses', name=disk_access) 579 580 if disk_encryption_set is not None and encryption_type is None: 581 raise CLIError('usage error: Please specify --encryption-type.') 582 if encryption_type is not None: 583 encryption = Encryption(type=encryption_type, disk_encryption_set_id=disk_encryption_set) 584 else: 585 encryption = None 586 587 snapshot = Snapshot(location=location, creation_data=creation_data, tags=(tags or {}), 588 sku=_get_sku_object(cmd, sku), disk_size_gb=size_gb, incremental=incremental, 589 encryption=encryption) 590 if hyper_v_generation: 591 snapshot.hyper_v_generation = hyper_v_generation 592 if network_access_policy is not None: 593 snapshot.network_access_policy = network_access_policy 594 if disk_access is not None: 595 snapshot.disk_access_id = disk_access 596 if edge_zone: 597 snapshot.extended_location = edge_zone 598 599 client = _compute_client_factory(cmd.cli_ctx) 600 return sdk_no_wait(no_wait, client.snapshots.begin_create_or_update, resource_group_name, snapshot_name, snapshot) 601 602 603def grant_snapshot_access(cmd, resource_group_name, snapshot_name, duration_in_seconds, access_level=None): 604 return _grant_access(cmd, resource_group_name, snapshot_name, duration_in_seconds, is_disk=False, 605 access_level=access_level) 606 607 608def list_snapshots(cmd, resource_group_name=None): 609 client = _compute_client_factory(cmd.cli_ctx) 610 if resource_group_name: 611 return client.snapshots.list_by_resource_group(resource_group_name) 612 return client.snapshots.list() 613 614 615def update_snapshot(cmd, resource_group_name, instance, sku=None, disk_encryption_set=None, 616 encryption_type=None, network_access_policy=None, disk_access=None): 617 from msrestazure.tools import resource_id, is_valid_resource_id 618 from azure.cli.core.commands.client_factory import get_subscription_id 619 620 if sku is not None: 621 _set_sku(cmd, instance, sku) 622 if disk_encryption_set is not None: 623 if instance.encryption.type != 'EncryptionAtRestWithCustomerKey' and \ 624 encryption_type != 'EncryptionAtRestWithCustomerKey': 625 raise CLIError('usage error: Please set --encryption-type to EncryptionAtRestWithCustomerKey') 626 if not is_valid_resource_id(disk_encryption_set): 627 disk_encryption_set = resource_id( 628 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 629 namespace='Microsoft.Compute', type='diskEncryptionSets', name=disk_encryption_set) 630 instance.encryption.disk_encryption_set_id = disk_encryption_set 631 if encryption_type is not None: 632 instance.encryption.type = encryption_type 633 if network_access_policy is not None: 634 instance.network_access_policy = network_access_policy 635 if disk_access is not None and not is_valid_resource_id(disk_access): 636 disk_access = resource_id( 637 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 638 namespace='Microsoft.Compute', type='diskAccesses', name=disk_access) 639 instance.disk_access_id = disk_access 640 return instance 641# endregion 642 643 644# region VirtualMachines Identity 645def show_vm_identity(cmd, resource_group_name, vm_name): 646 client = _compute_client_factory(cmd.cli_ctx) 647 return client.virtual_machines.get(resource_group_name, vm_name).identity 648 649 650def show_vmss_identity(cmd, resource_group_name, vm_name): 651 client = _compute_client_factory(cmd.cli_ctx) 652 return client.virtual_machine_scale_sets.get(resource_group_name, vm_name).identity 653 654 655def assign_vm_identity(cmd, resource_group_name, vm_name, assign_identity=None, identity_role='Contributor', 656 identity_role_id=None, identity_scope=None): 657 VirtualMachineIdentity, ResourceIdentityType, VirtualMachineUpdate = cmd.get_models('VirtualMachineIdentity', 658 'ResourceIdentityType', 659 'VirtualMachineUpdate') 660 UserAssignedIdentitiesValue = cmd.get_models('UserAssignedIdentitiesValue') 661 from azure.cli.core.commands.arm import assign_identity as assign_identity_helper 662 client = _compute_client_factory(cmd.cli_ctx) 663 _, _, external_identities, enable_local_identity = _build_identities_info(assign_identity) 664 665 def getter(): 666 return client.virtual_machines.get(resource_group_name, vm_name) 667 668 def setter(vm, external_identities=external_identities): 669 if vm.identity and vm.identity.type == ResourceIdentityType.system_assigned_user_assigned: 670 identity_types = ResourceIdentityType.system_assigned_user_assigned 671 elif vm.identity and vm.identity.type == ResourceIdentityType.system_assigned and external_identities: 672 identity_types = ResourceIdentityType.system_assigned_user_assigned 673 elif vm.identity and vm.identity.type == ResourceIdentityType.user_assigned and enable_local_identity: 674 identity_types = ResourceIdentityType.system_assigned_user_assigned 675 elif external_identities and enable_local_identity: 676 identity_types = ResourceIdentityType.system_assigned_user_assigned 677 elif external_identities: 678 identity_types = ResourceIdentityType.user_assigned 679 else: 680 identity_types = ResourceIdentityType.system_assigned 681 682 vm.identity = VirtualMachineIdentity(type=identity_types) 683 if external_identities: 684 vm.identity.user_assigned_identities = {} 685 if not cmd.supported_api_version(min_api='2018-06-01', resource_type=ResourceType.MGMT_COMPUTE): 686 raise CLIInternalError("Usage error: user assigned identity is not available under current profile.", 687 "You can set the cloud's profile to latest with 'az cloud set --profile latest" 688 " --name <cloud name>'") 689 for identity in external_identities: 690 vm.identity.user_assigned_identities[identity] = UserAssignedIdentitiesValue() 691 692 vm_patch = VirtualMachineUpdate() 693 vm_patch.identity = vm.identity 694 return patch_vm(cmd, resource_group_name, vm_name, vm_patch) 695 696 assign_identity_helper(cmd.cli_ctx, getter, setter, identity_role=identity_role_id, identity_scope=identity_scope) 697 vm = client.virtual_machines.get(resource_group_name, vm_name) 698 return _construct_identity_info(identity_scope, identity_role, vm.identity.principal_id, 699 vm.identity.user_assigned_identities) 700# endregion 701 702 703# region VirtualMachines 704def capture_vm(cmd, resource_group_name, vm_name, vhd_name_prefix, 705 storage_container='vhds', overwrite=True): 706 VirtualMachineCaptureParameters = cmd.get_models('VirtualMachineCaptureParameters') 707 client = _compute_client_factory(cmd.cli_ctx) 708 parameter = VirtualMachineCaptureParameters(vhd_prefix=vhd_name_prefix, 709 destination_container_name=storage_container, 710 overwrite_vhds=overwrite) 711 poller = client.virtual_machines.begin_capture(resource_group_name, vm_name, parameter) 712 result = LongRunningOperation(cmd.cli_ctx)(poller) 713 output = getattr(result, 'output', None) or result.resources[0] 714 print(json.dumps(output, indent=2)) # pylint: disable=no-member 715 716 717# pylint: disable=too-many-locals, unused-argument, too-many-statements, too-many-branches 718def create_vm(cmd, vm_name, resource_group_name, image=None, size='Standard_DS1_v2', location=None, tags=None, 719 no_wait=False, authentication_type=None, admin_password=None, computer_name=None, 720 admin_username=None, ssh_dest_key_path=None, ssh_key_value=None, generate_ssh_keys=False, 721 availability_set=None, nics=None, nsg=None, nsg_rule=None, accelerated_networking=None, 722 private_ip_address=None, public_ip_address=None, public_ip_address_allocation='dynamic', 723 public_ip_address_dns_name=None, public_ip_sku=None, os_disk_name=None, os_type=None, 724 storage_account=None, os_caching=None, data_caching=None, storage_container_name=None, storage_sku=None, 725 use_unmanaged_disk=False, attach_os_disk=None, os_disk_size_gb=None, attach_data_disks=None, 726 data_disk_sizes_gb=None, disk_info=None, 727 vnet_name=None, vnet_address_prefix='10.0.0.0/16', subnet=None, subnet_address_prefix='10.0.0.0/24', 728 storage_profile=None, os_publisher=None, os_offer=None, os_sku=None, os_version=None, 729 storage_account_type=None, vnet_type=None, nsg_type=None, public_ip_address_type=None, nic_type=None, 730 validate=False, custom_data=None, secrets=None, plan_name=None, plan_product=None, plan_publisher=None, 731 plan_promotion_code=None, license_type=None, assign_identity=None, identity_scope=None, 732 identity_role='Contributor', identity_role_id=None, application_security_groups=None, zone=None, 733 boot_diagnostics_storage=None, ultra_ssd_enabled=None, ephemeral_os_disk=None, 734 proximity_placement_group=None, dedicated_host=None, dedicated_host_group=None, aux_subscriptions=None, 735 priority=None, max_price=None, eviction_policy=None, enable_agent=None, workspace=None, vmss=None, 736 os_disk_encryption_set=None, data_disk_encryption_sets=None, specialized=None, 737 encryption_at_host=None, enable_auto_update=None, patch_mode=None, ssh_key_name=None, 738 enable_hotpatching=None, platform_fault_domain=None, security_type=None, enable_secure_boot=None, 739 enable_vtpm=None, count=None, edge_zone=None, nic_delete_option=None, os_disk_delete_option=None, 740 data_disk_delete_option=None, user_data=None, capacity_reservation_group=None): 741 742 from azure.cli.core.commands.client_factory import get_subscription_id 743 from azure.cli.core.util import random_string, hash_string 744 from azure.cli.core.commands.arm import ArmTemplateBuilder 745 from azure.cli.command_modules.vm._template_builder import (build_vm_resource, 746 build_storage_account_resource, build_nic_resource, 747 build_vnet_resource, build_nsg_resource, 748 build_public_ip_resource, StorageProfile, 749 build_msi_role_assignment, 750 build_vm_linux_log_analytics_workspace_agent, 751 build_vm_windows_log_analytics_workspace_agent) 752 from azure.cli.command_modules.vm._vm_utils import ArmTemplateBuilder20190401 753 from msrestazure.tools import resource_id, is_valid_resource_id, parse_resource_id 754 755 # In the latest profile, the default public IP will be expected to be changed from Basic to Standard. 756 # In order to avoid breaking change which has a big impact to users, 757 # we use the hint to guide users to use Standard public IP to create VM in the first stage. 758 if public_ip_sku is None and cmd.cli_ctx.cloud.profile == 'latest': 759 logger.warning( 760 'It is recommended to use parameter "--public-ip-sku Standard" to create new VM with Standard public IP. ' 761 'Please note that the default public IP used for VM creation will be changed from Basic to Standard ' 762 'in the future.') 763 764 subscription_id = get_subscription_id(cmd.cli_ctx) 765 766 if os_disk_encryption_set is not None and not is_valid_resource_id(os_disk_encryption_set): 767 os_disk_encryption_set = resource_id( 768 subscription=subscription_id, resource_group=resource_group_name, 769 namespace='Microsoft.Compute', type='diskEncryptionSets', name=os_disk_encryption_set) 770 771 if data_disk_encryption_sets is None: 772 data_disk_encryption_sets = [] 773 for i, des in enumerate(data_disk_encryption_sets): 774 if des is not None and not is_valid_resource_id(des): 775 data_disk_encryption_sets[i] = resource_id( 776 subscription=subscription_id, resource_group=resource_group_name, 777 namespace='Microsoft.Compute', type='diskEncryptionSets', name=des) 778 779 storage_sku = disk_info['os'].get('storageAccountType') 780 781 network_id_template = resource_id( 782 subscription=subscription_id, resource_group=resource_group_name, 783 namespace='Microsoft.Network') 784 785 vm_id = resource_id( 786 subscription=subscription_id, resource_group=resource_group_name, 787 namespace='Microsoft.Compute', type='virtualMachines', name=vm_name) 788 789 # determine final defaults and calculated values 790 tags = tags or {} 791 os_disk_name = os_disk_name or ('osdisk_{}'.format(hash_string(vm_id, length=10)) if use_unmanaged_disk else None) 792 storage_container_name = storage_container_name or 'vhds' 793 794 # Build up the ARM template 795 if count is None: 796 master_template = ArmTemplateBuilder() 797 else: 798 master_template = ArmTemplateBuilder20190401() 799 800 vm_dependencies = [] 801 if storage_account_type == 'new': 802 storage_account = storage_account or 'vhdstorage{}'.format( 803 hash_string(vm_id, length=14, force_lower=True)) 804 vm_dependencies.append('Microsoft.Storage/storageAccounts/{}'.format(storage_account)) 805 master_template.add_resource(build_storage_account_resource(cmd, storage_account, location, 806 tags, storage_sku, edge_zone)) 807 808 nic_name = None 809 if nic_type == 'new': 810 nic_name = '{}VMNic'.format(vm_name) 811 nic_full_name = 'Microsoft.Network/networkInterfaces/{}'.format(nic_name) 812 if count: 813 vm_dependencies.extend([nic_full_name + str(i) for i in range(count)]) 814 else: 815 vm_dependencies.append(nic_full_name) 816 817 nic_dependencies = [] 818 if vnet_type == 'new': 819 subnet = subnet or '{}Subnet'.format(vm_name) 820 vnet_exists = False 821 if vnet_name: 822 from azure.cli.command_modules.vm._vm_utils import check_existence 823 vnet_exists = \ 824 check_existence(cmd.cli_ctx, vnet_name, resource_group_name, 'Microsoft.Network', 'virtualNetworks') 825 if vnet_exists: 826 from azure.cli.core.commands import cached_get, cached_put, upsert_to_collection 827 from azure.cli.command_modules.vm._validators import get_network_client 828 client = get_network_client(cmd.cli_ctx).virtual_networks 829 vnet = cached_get(cmd, client.get, resource_group_name, vnet_name) 830 831 Subnet = cmd.get_models('Subnet', resource_type=ResourceType.MGMT_NETWORK) 832 subnet_obj = Subnet( 833 name=subnet, 834 address_prefixes=[subnet_address_prefix], 835 address_prefix=subnet_address_prefix 836 ) 837 upsert_to_collection(vnet, 'subnets', subnet_obj, 'name') 838 try: 839 cached_put(cmd, client.begin_create_or_update, vnet, resource_group_name, vnet_name).result() 840 except Exception: 841 raise CLIError('Subnet({}) does not exist, but failed to create a new subnet with address ' 842 'prefix {}. It may be caused by name or address prefix conflict. Please specify ' 843 'an appropriate subnet name with --subnet or a valid address prefix value with ' 844 '--subnet-address-prefix.'.format(subnet, subnet_address_prefix)) 845 if not vnet_exists: 846 vnet_name = vnet_name or '{}VNET'.format(vm_name) 847 nic_dependencies.append('Microsoft.Network/virtualNetworks/{}'.format(vnet_name)) 848 master_template.add_resource(build_vnet_resource(cmd, vnet_name, location, tags, vnet_address_prefix, 849 subnet, subnet_address_prefix, edge_zone=edge_zone)) 850 851 if nsg_type == 'new': 852 if nsg_rule is None: 853 nsg_rule = 'RDP' if os_type.lower() == 'windows' else 'SSH' 854 nsg = nsg or '{}NSG'.format(vm_name) 855 nic_dependencies.append('Microsoft.Network/networkSecurityGroups/{}'.format(nsg)) 856 master_template.add_resource(build_nsg_resource(cmd, nsg, location, tags, nsg_rule)) 857 858 if public_ip_address_type == 'new': 859 public_ip_address = public_ip_address or '{}PublicIP'.format(vm_name) 860 public_ip_address_full_name = 'Microsoft.Network/publicIpAddresses/{}'.format(public_ip_address) 861 if count: 862 nic_dependencies.extend([public_ip_address_full_name + str(i) for i in range(count)]) 863 else: 864 nic_dependencies.append(public_ip_address_full_name) 865 master_template.add_resource(build_public_ip_resource(cmd, public_ip_address, location, tags, 866 public_ip_address_allocation, 867 public_ip_address_dns_name, 868 public_ip_sku, zone, count, edge_zone)) 869 870 subnet_id = subnet if is_valid_resource_id(subnet) else \ 871 '{}/virtualNetworks/{}/subnets/{}'.format(network_id_template, vnet_name, subnet) 872 873 nsg_id = None 874 if nsg: 875 nsg_id = nsg if is_valid_resource_id(nsg) else \ 876 '{}/networkSecurityGroups/{}'.format(network_id_template, nsg) 877 878 public_ip_address_id = None 879 if public_ip_address: 880 public_ip_address_id = public_ip_address if is_valid_resource_id(public_ip_address) \ 881 else '{}/publicIPAddresses/{}'.format(network_id_template, public_ip_address) 882 883 nics_id = '{}/networkInterfaces/{}'.format(network_id_template, nic_name) 884 885 if count: 886 nics = [ 887 { 888 'id': "[concat('{}', copyIndex())]".format(nics_id), 889 'properties': { 890 'deleteOption': nic_delete_option 891 } 892 } 893 ] 894 else: 895 nics = [ 896 { 897 'id': nics_id, 898 'properties': { 899 'deleteOption': nic_delete_option 900 } 901 } 902 ] 903 904 nic_resource = build_nic_resource( 905 cmd, nic_name, location, tags, vm_name, subnet_id, private_ip_address, nsg_id, 906 public_ip_address_id, application_security_groups, accelerated_networking=accelerated_networking, 907 count=count, edge_zone=edge_zone) 908 nic_resource['dependsOn'] = nic_dependencies 909 master_template.add_resource(nic_resource) 910 else: 911 # Using an existing NIC 912 invalid_parameters = [nsg, public_ip_address, subnet, vnet_name, application_security_groups] 913 if any(invalid_parameters): 914 raise CLIError('When specifying an existing NIC, do not specify NSG, ' 915 'public IP, ASGs, VNet or subnet.') 916 if accelerated_networking is not None: 917 logger.warning('When specifying an existing NIC, do not specify accelerated networking. ' 918 'Ignore --accelerated-networking now. ' 919 'This will trigger an error instead of a warning in future releases.') 920 921 os_vhd_uri = None 922 if storage_profile in [StorageProfile.SACustomImage, StorageProfile.SAPirImage]: 923 storage_account_name = storage_account.rsplit('/', 1) 924 storage_account_name = storage_account_name[1] if \ 925 len(storage_account_name) > 1 else storage_account_name[0] 926 os_vhd_uri = 'https://{}.blob.{}/{}/{}.vhd'.format( 927 storage_account_name, cmd.cli_ctx.cloud.suffixes.storage_endpoint, storage_container_name, os_disk_name) 928 elif storage_profile == StorageProfile.SASpecializedOSDisk: 929 os_vhd_uri = attach_os_disk 930 os_disk_name = attach_os_disk.rsplit('/', 1)[1][:-4] 931 932 if custom_data: 933 custom_data = read_content_if_is_file(custom_data) 934 935 if user_data: 936 user_data = read_content_if_is_file(user_data) 937 938 if secrets: 939 secrets = _merge_secrets([validate_file_or_dict(secret) for secret in secrets]) 940 941 vm_resource = build_vm_resource( 942 cmd=cmd, name=vm_name, location=location, tags=tags, size=size, storage_profile=storage_profile, nics=nics, 943 admin_username=admin_username, availability_set_id=availability_set, admin_password=admin_password, 944 ssh_key_values=ssh_key_value, ssh_key_path=ssh_dest_key_path, image_reference=image, 945 os_disk_name=os_disk_name, custom_image_os_type=os_type, authentication_type=authentication_type, 946 os_publisher=os_publisher, os_offer=os_offer, os_sku=os_sku, os_version=os_version, os_vhd_uri=os_vhd_uri, 947 attach_os_disk=attach_os_disk, os_disk_size_gb=os_disk_size_gb, custom_data=custom_data, secrets=secrets, 948 license_type=license_type, zone=zone, disk_info=disk_info, 949 boot_diagnostics_storage_uri=boot_diagnostics_storage, ultra_ssd_enabled=ultra_ssd_enabled, 950 proximity_placement_group=proximity_placement_group, computer_name=computer_name, 951 dedicated_host=dedicated_host, priority=priority, max_price=max_price, eviction_policy=eviction_policy, 952 enable_agent=enable_agent, vmss=vmss, os_disk_encryption_set=os_disk_encryption_set, 953 data_disk_encryption_sets=data_disk_encryption_sets, specialized=specialized, 954 encryption_at_host=encryption_at_host, dedicated_host_group=dedicated_host_group, 955 enable_auto_update=enable_auto_update, patch_mode=patch_mode, enable_hotpatching=enable_hotpatching, 956 platform_fault_domain=platform_fault_domain, security_type=security_type, enable_secure_boot=enable_secure_boot, 957 enable_vtpm=enable_vtpm, count=count, edge_zone=edge_zone, os_disk_delete_option=os_disk_delete_option, 958 user_data=user_data, capacity_reservation_group=capacity_reservation_group) 959 960 vm_resource['dependsOn'] = vm_dependencies 961 962 if plan_name: 963 vm_resource['plan'] = { 964 'name': plan_name, 965 'publisher': plan_publisher, 966 'product': plan_product, 967 'promotionCode': plan_promotion_code 968 } 969 970 enable_local_identity = None 971 if assign_identity is not None: 972 vm_resource['identity'], _, _, enable_local_identity = _build_identities_info(assign_identity) 973 role_assignment_guid = None 974 if identity_scope: 975 role_assignment_guid = str(_gen_guid()) 976 master_template.add_resource(build_msi_role_assignment(vm_name, vm_id, identity_role_id, 977 role_assignment_guid, identity_scope)) 978 979 if workspace is not None: 980 workspace_id = _prepare_workspace(cmd, resource_group_name, workspace) 981 master_template.add_secure_parameter('workspaceId', workspace_id) 982 if os_type.lower() == 'linux': 983 vm_mmaExtension_resource = build_vm_linux_log_analytics_workspace_agent(cmd, vm_name, location) 984 master_template.add_resource(vm_mmaExtension_resource) 985 elif os_type.lower() == 'windows': 986 vm_mmaExtension_resource = build_vm_windows_log_analytics_workspace_agent(cmd, vm_name, location) 987 master_template.add_resource(vm_mmaExtension_resource) 988 else: 989 logger.warning("Unsupported OS type. Skip the connection step for log analytics workspace.") 990 991 master_template.add_resource(vm_resource) 992 993 if admin_password: 994 master_template.add_secure_parameter('adminPassword', admin_password) 995 996 template = master_template.build() 997 parameters = master_template.build_parameters() 998 999 # deploy ARM template 1000 deployment_name = 'vm_deploy_' + random_string(32) 1001 client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES, 1002 aux_subscriptions=aux_subscriptions).deployments 1003 DeploymentProperties = cmd.get_models('DeploymentProperties', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) 1004 properties = DeploymentProperties(template=template, parameters=parameters, mode='incremental') 1005 Deployment = cmd.get_models('Deployment', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) 1006 deployment = Deployment(properties=properties) 1007 1008 if validate: 1009 from azure.cli.command_modules.vm._vm_utils import log_pprint_template 1010 log_pprint_template(template) 1011 log_pprint_template(parameters) 1012 1013 if cmd.supported_api_version(min_api='2019-10-01', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES): 1014 validation_poller = client.begin_validate(resource_group_name, deployment_name, deployment) 1015 return LongRunningOperation(cmd.cli_ctx)(validation_poller) 1016 1017 return client.validate(resource_group_name, deployment_name, deployment) 1018 1019 # creates the VM deployment 1020 if no_wait: 1021 return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, deployment_name, deployment) 1022 LongRunningOperation(cmd.cli_ctx)(client.begin_create_or_update(resource_group_name, deployment_name, deployment)) 1023 1024 if count: 1025 vm_names = [vm_name + str(i) for i in range(count)] 1026 else: 1027 vm_names = [vm_name] 1028 vms = [] 1029 # Use vm_name2 to avoid R1704: Redefining argument with the local name 'vm_name' (redefined-argument-from-local) 1030 for vm_name2 in vm_names: 1031 vm = get_vm_details(cmd, resource_group_name, vm_name2) 1032 if assign_identity is not None: 1033 if enable_local_identity and not identity_scope: 1034 _show_missing_access_warning(resource_group_name, vm_name2, 'vm') 1035 setattr(vm, 'identity', _construct_identity_info(identity_scope, identity_role, vm.identity.principal_id, 1036 vm.identity.user_assigned_identities)) 1037 vms.append(vm) 1038 1039 if workspace is not None: 1040 workspace_name = parse_resource_id(workspace_id)['name'] 1041 _set_data_source_for_workspace(cmd, os_type, resource_group_name, workspace_name) 1042 1043 if len(vms) == 1: 1044 return vms[0] 1045 return vms 1046 1047 1048def auto_shutdown_vm(cmd, resource_group_name, vm_name, off=None, email=None, webhook=None, time=None, 1049 location=None): 1050 from msrestazure.tools import resource_id 1051 from azure.mgmt.devtestlabs.models import Schedule 1052 from azure.cli.core.commands.client_factory import get_subscription_id 1053 subscription_id = get_subscription_id(cmd.cli_ctx) 1054 client = _dev_test_labs_client_factory(cmd.cli_ctx, subscription_id) 1055 name = 'shutdown-computevm-' + vm_name 1056 vm_id = resource_id(subscription=client.config.subscription_id, resource_group=resource_group_name, 1057 namespace='Microsoft.Compute', type='virtualMachines', name=vm_name) 1058 if off: 1059 if email is not None or webhook is not None or time is not None: 1060 # I don't want to disrupt users. So I warn instead of raising an error. 1061 logger.warning('If --off, other parameters will be ignored.') 1062 return client.global_schedules.delete(resource_group_name, name) 1063 1064 if time is None: 1065 raise CLIError('usage error: --time is a required parameter') 1066 daily_recurrence = {'time': time} 1067 notification_settings = None 1068 if email or webhook: 1069 notification_settings = { 1070 'timeInMinutes': 30, 1071 'status': 'Enabled' 1072 } 1073 if email: 1074 notification_settings['emailRecipient'] = email 1075 if webhook: 1076 notification_settings['webhookUrl'] = webhook 1077 1078 schedule = Schedule(status='Enabled', 1079 target_resource_id=vm_id, 1080 daily_recurrence=daily_recurrence, 1081 notification_settings=notification_settings, 1082 time_zone_id='UTC', 1083 task_type='ComputeVmShutdownTask', 1084 location=location) 1085 return client.global_schedules.create_or_update(resource_group_name, name, schedule) 1086 1087 1088def get_instance_view(cmd, resource_group_name, vm_name, include_user_data=False): 1089 expand = 'instanceView' 1090 if include_user_data: 1091 expand = expand + ',userData' 1092 return get_vm(cmd, resource_group_name, vm_name, expand) 1093 1094 1095def get_vm(cmd, resource_group_name, vm_name, expand=None): 1096 client = _compute_client_factory(cmd.cli_ctx) 1097 return client.virtual_machines.get(resource_group_name, vm_name, expand=expand) 1098 1099 1100def get_vm_to_update(cmd, resource_group_name, vm_name): 1101 client = _compute_client_factory(cmd.cli_ctx) 1102 vm = client.virtual_machines.get(resource_group_name, vm_name) 1103 # To avoid unnecessary permission check of image 1104 vm.storage_profile.image_reference = None 1105 return vm 1106 1107 1108def get_vm_details(cmd, resource_group_name, vm_name, include_user_data=False): 1109 from msrestazure.tools import parse_resource_id 1110 from azure.cli.command_modules.vm._vm_utils import get_target_network_api 1111 result = get_instance_view(cmd, resource_group_name, vm_name, include_user_data) 1112 network_client = get_mgmt_service_client( 1113 cmd.cli_ctx, ResourceType.MGMT_NETWORK, api_version=get_target_network_api(cmd.cli_ctx)) 1114 public_ips = [] 1115 fqdns = [] 1116 private_ips = [] 1117 mac_addresses = [] 1118 # pylint: disable=line-too-long,no-member 1119 for nic_ref in result.network_profile.network_interfaces: 1120 nic_parts = parse_resource_id(nic_ref.id) 1121 nic = network_client.network_interfaces.get(nic_parts['resource_group'], nic_parts['name']) 1122 if nic.mac_address: 1123 mac_addresses.append(nic.mac_address) 1124 for ip_configuration in nic.ip_configurations: 1125 if ip_configuration.private_ip_address: 1126 private_ips.append(ip_configuration.private_ip_address) 1127 if ip_configuration.public_ip_address: 1128 res = parse_resource_id(ip_configuration.public_ip_address.id) 1129 public_ip_info = network_client.public_ip_addresses.get(res['resource_group'], 1130 res['name']) 1131 if public_ip_info.ip_address: 1132 public_ips.append(public_ip_info.ip_address) 1133 if public_ip_info.dns_settings: 1134 fqdns.append(public_ip_info.dns_settings.fqdn) 1135 1136 setattr(result, 'power_state', 1137 ','.join([s.display_status for s in result.instance_view.statuses if s.code.startswith('PowerState/')])) 1138 setattr(result, 'public_ips', ','.join(public_ips)) 1139 setattr(result, 'fqdns', ','.join(fqdns)) 1140 setattr(result, 'private_ips', ','.join(private_ips)) 1141 setattr(result, 'mac_addresses', ','.join(mac_addresses)) 1142 del result.instance_view # we don't need other instance_view info as people won't care 1143 return result 1144 1145 1146def list_skus(cmd, location=None, size=None, zone=None, show_all=None, resource_type=None): 1147 from ._vm_utils import list_sku_info 1148 result = list_sku_info(cmd.cli_ctx, location) 1149 # pylint: disable=too-many-nested-blocks 1150 if not show_all: 1151 available_skus = [] 1152 for sku_info in result: 1153 is_available = True 1154 if sku_info.restrictions: 1155 for restriction in sku_info.restrictions: 1156 if restriction.reason_code == 'NotAvailableForSubscription': 1157 # The attribute location_info is not supported in versions 2017-03-30 and earlier 1158 if cmd.supported_api_version(max_api='2017-03-30'): 1159 is_available = False 1160 break 1161 # This SKU is not available only if all zones are restricted 1162 if not (set(sku_info.location_info[0].zones or []) - 1163 set(restriction.restriction_info.zones or [])): 1164 is_available = False 1165 break 1166 if is_available: 1167 available_skus.append(sku_info) 1168 result = available_skus 1169 if resource_type: 1170 result = [x for x in result if x.resource_type.lower() == resource_type.lower()] 1171 if size: 1172 result = [x for x in result if x.resource_type == 'virtualMachines' and size.lower() in x.name.lower()] 1173 if zone: 1174 result = [x for x in result if x.location_info and x.location_info[0].zones] 1175 return result 1176 1177 1178def list_vm(cmd, resource_group_name=None, show_details=False): 1179 ccf = _compute_client_factory(cmd.cli_ctx) 1180 vm_list = ccf.virtual_machines.list(resource_group_name=resource_group_name) \ 1181 if resource_group_name else ccf.virtual_machines.list_all() 1182 if show_details: 1183 return [get_vm_details(cmd, _parse_rg_name(v.id)[0], v.name) for v in vm_list] 1184 1185 return list(vm_list) 1186 1187 1188def list_vm_ip_addresses(cmd, resource_group_name=None, vm_name=None): 1189 # We start by getting NICs as they are the smack in the middle of all data that we 1190 # want to collect for a VM (as long as we don't need any info on the VM than what 1191 # is available in the Id, we don't need to make any calls to the compute RP) 1192 # 1193 # Since there is no guarantee that a NIC is in the same resource group as a given 1194 # Virtual Machine, we can't constrain the lookup to only a single group... 1195 network_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_NETWORK) 1196 nics = network_client.network_interfaces.list_all() 1197 public_ip_addresses = network_client.public_ip_addresses.list_all() 1198 1199 ip_address_lookup = {pip.id: pip for pip in list(public_ip_addresses)} 1200 1201 result = [] 1202 for nic in [n for n in list(nics) if n.virtual_machine]: 1203 nic_resource_group, nic_vm_name = _parse_rg_name(nic.virtual_machine.id) 1204 1205 # If provided, make sure that resource group name and vm name match the NIC we are 1206 # looking at before adding it to the result... 1207 same_resource_group_name = (resource_group_name is None or 1208 resource_group_name.lower() == nic_resource_group.lower()) 1209 same_vm_name = (vm_name is None or 1210 vm_name.lower() == nic_vm_name.lower()) 1211 if same_resource_group_name and same_vm_name: 1212 network_info = { 1213 'privateIpAddresses': [], 1214 'publicIpAddresses': [] 1215 } 1216 for ip_configuration in nic.ip_configurations: 1217 network_info['privateIpAddresses'].append(ip_configuration.private_ip_address) 1218 if ip_configuration.public_ip_address and ip_configuration.public_ip_address.id in ip_address_lookup: 1219 public_ip_address = ip_address_lookup[ip_configuration.public_ip_address.id] 1220 1221 public_ip_addr_info = { 1222 'id': public_ip_address.id, 1223 'name': public_ip_address.name, 1224 'ipAddress': public_ip_address.ip_address, 1225 'ipAllocationMethod': public_ip_address.public_ip_allocation_method 1226 } 1227 1228 try: 1229 public_ip_addr_info['zone'] = public_ip_address.zones[0] 1230 except (AttributeError, IndexError, TypeError): 1231 pass 1232 1233 network_info['publicIpAddresses'].append(public_ip_addr_info) 1234 1235 result.append({ 1236 'virtualMachine': { 1237 'resourceGroup': nic_resource_group, 1238 'name': nic_vm_name, 1239 'network': network_info 1240 } 1241 }) 1242 1243 return result 1244 1245 1246def open_vm_port(cmd, resource_group_name, vm_name, port, priority=900, network_security_group_name=None, 1247 apply_to_subnet=False): 1248 from msrestazure.tools import parse_resource_id 1249 1250 network = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_NETWORK) 1251 1252 vm = get_vm(cmd, resource_group_name, vm_name) 1253 location = vm.location 1254 if not vm.network_profile: 1255 raise CLIError("Network profile not found for VM '{}'".format(vm_name)) 1256 1257 nic_ids = list(vm.network_profile.network_interfaces) 1258 if len(nic_ids) > 1: 1259 raise CLIError('Multiple NICs is not supported for this command. Create rules on the NSG ' 1260 'directly.') 1261 if not nic_ids: 1262 raise CLIError("No NIC associated with VM '{}'".format(vm_name)) 1263 1264 # get existing NSG or create a new one 1265 created_nsg = False 1266 nic = network.network_interfaces.get(resource_group_name, os.path.split(nic_ids[0].id)[1]) 1267 if not apply_to_subnet: 1268 nsg = nic.network_security_group 1269 else: 1270 subnet_id = parse_resource_id(nic.ip_configurations[0].subnet.id) 1271 subnet = network.subnets.get(resource_group_name, subnet_id['name'], subnet_id['child_name_1']) 1272 nsg = subnet.network_security_group 1273 1274 if not nsg: 1275 NetworkSecurityGroup = \ 1276 cmd.get_models('NetworkSecurityGroup', resource_type=ResourceType.MGMT_NETWORK) 1277 nsg = LongRunningOperation(cmd.cli_ctx, 'Creating network security group')( 1278 network.network_security_groups.begin_create_or_update( 1279 resource_group_name=resource_group_name, 1280 network_security_group_name=network_security_group_name, 1281 parameters=NetworkSecurityGroup(location=location) 1282 ) 1283 ) 1284 created_nsg = True 1285 1286 # update the NSG with the new rule to allow inbound traffic 1287 SecurityRule = cmd.get_models('SecurityRule', resource_type=ResourceType.MGMT_NETWORK) 1288 1289 rule_name = 'open-port-all' if port == '*' else 'open-port-{}'.format((port.replace(',', '_'))) 1290 1291 # use portranges if multiple ports are entered 1292 if "," not in port: 1293 port_arg = { 1294 'destination_port_range': port 1295 } 1296 else: 1297 port_arg = { 1298 'destination_port_ranges': port.split(',') 1299 } 1300 1301 rule = SecurityRule(protocol='*', access='allow', direction='inbound', name=rule_name, 1302 source_port_range='*', **port_arg, priority=priority, 1303 source_address_prefix='*', destination_address_prefix='*') 1304 nsg_name = nsg.name or os.path.split(nsg.id)[1] 1305 LongRunningOperation(cmd.cli_ctx, 'Adding security rule')( 1306 network.security_rules.begin_create_or_update( 1307 resource_group_name, nsg_name, rule_name, rule) 1308 ) 1309 1310 # update the NIC or subnet if a new NSG was created 1311 if created_nsg and not apply_to_subnet: 1312 nic.network_security_group = nsg 1313 LongRunningOperation(cmd.cli_ctx, 'Updating NIC')(network.network_interfaces.begin_create_or_update( 1314 resource_group_name, nic.name, nic)) 1315 elif created_nsg and apply_to_subnet: 1316 subnet.network_security_group = nsg 1317 LongRunningOperation(cmd.cli_ctx, 'Updating subnet')(network.subnets.begin_create_or_update( 1318 resource_group_name=resource_group_name, 1319 virtual_network_name=subnet_id['name'], 1320 subnet_name=subnet_id['child_name_1'], 1321 subnet_parameters=subnet 1322 )) 1323 1324 return network.network_security_groups.get(resource_group_name, nsg_name) 1325 1326 1327def resize_vm(cmd, resource_group_name, vm_name, size, no_wait=False): 1328 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 1329 if vm.hardware_profile.vm_size == size: 1330 logger.warning("VM is already %s", size) 1331 return None 1332 1333 vm.hardware_profile.vm_size = size # pylint: disable=no-member 1334 return set_vm(cmd, vm, no_wait=no_wait) 1335 1336 1337def restart_vm(cmd, resource_group_name, vm_name, no_wait=False, force=False): 1338 client = _compute_client_factory(cmd.cli_ctx) 1339 if force: 1340 return sdk_no_wait(no_wait, client.virtual_machines.begin_redeploy, resource_group_name, vm_name) 1341 return sdk_no_wait(no_wait, client.virtual_machines.begin_restart, resource_group_name, vm_name) 1342 1343 1344def set_vm(cmd, instance, lro_operation=None, no_wait=False): 1345 instance.resources = None # Issue: https://github.com/Azure/autorest/issues/934 1346 client = _compute_client_factory(cmd.cli_ctx) 1347 parsed_id = _parse_rg_name(instance.id) 1348 poller = sdk_no_wait(no_wait, client.virtual_machines.begin_create_or_update, 1349 resource_group_name=parsed_id[0], 1350 vm_name=parsed_id[1], 1351 parameters=instance) 1352 if lro_operation: 1353 return lro_operation(poller) 1354 1355 return LongRunningOperation(cmd.cli_ctx)(poller) 1356 1357 1358def patch_vm(cmd, resource_group_name, vm_name, vm): 1359 client = _compute_client_factory(cmd.cli_ctx) 1360 poller = client.virtual_machines.begin_update(resource_group_name, vm_name, vm) 1361 return LongRunningOperation(cmd.cli_ctx)(poller) 1362 1363 1364def show_vm(cmd, resource_group_name, vm_name, show_details=False, include_user_data=False): 1365 if show_details: 1366 return get_vm_details(cmd, resource_group_name, vm_name, include_user_data) 1367 1368 expand = None 1369 if include_user_data: 1370 expand = "userData" 1371 return get_vm(cmd, resource_group_name, vm_name, expand) 1372 1373 1374def update_vm(cmd, resource_group_name, vm_name, os_disk=None, disk_caching=None, 1375 write_accelerator=None, license_type=None, no_wait=False, ultra_ssd_enabled=None, 1376 priority=None, max_price=None, proximity_placement_group=None, workspace=None, enable_secure_boot=None, 1377 enable_vtpm=None, user_data=None, capacity_reservation_group=None, **kwargs): 1378 from msrestazure.tools import parse_resource_id, resource_id, is_valid_resource_id 1379 from ._vm_utils import update_write_accelerator_settings, update_disk_caching 1380 vm = kwargs['parameters'] 1381 if os_disk is not None: 1382 if is_valid_resource_id(os_disk): 1383 disk_id, disk_name = os_disk, parse_resource_id(os_disk)['name'] 1384 else: 1385 res = parse_resource_id(vm.id) 1386 disk_id = resource_id(subscription=res['subscription'], resource_group=res['resource_group'], 1387 namespace='Microsoft.Compute', type='disks', name=os_disk) 1388 disk_name = os_disk 1389 vm.storage_profile.os_disk.managed_disk.id = disk_id 1390 vm.storage_profile.os_disk.name = disk_name 1391 1392 if write_accelerator is not None: 1393 update_write_accelerator_settings(vm.storage_profile, write_accelerator) 1394 1395 if disk_caching is not None: 1396 update_disk_caching(vm.storage_profile, disk_caching) 1397 1398 if license_type is not None: 1399 vm.license_type = license_type 1400 1401 if user_data is not None: 1402 from azure.cli.core.util import b64encode 1403 vm.user_data = b64encode(user_data) 1404 1405 if capacity_reservation_group is not None: 1406 CapacityReservationProfile = cmd.get_models('CapacityReservationProfile') 1407 SubResource = cmd.get_models('SubResource') 1408 if capacity_reservation_group == 'None': 1409 capacity_reservation_group = None 1410 sub_resource = SubResource(id=capacity_reservation_group) 1411 capacity_reservation = CapacityReservationProfile(capacity_reservation_group=sub_resource) 1412 vm.capacity_reservation = capacity_reservation 1413 1414 if ultra_ssd_enabled is not None: 1415 if vm.additional_capabilities is None: 1416 AdditionalCapabilities = cmd.get_models('AdditionalCapabilities') 1417 vm.additional_capabilities = AdditionalCapabilities(ultra_ssd_enabled=ultra_ssd_enabled) 1418 else: 1419 vm.additional_capabilities.ultra_ssd_enabled = ultra_ssd_enabled 1420 1421 if priority is not None: 1422 vm.priority = priority 1423 1424 if max_price is not None: 1425 if vm.billing_profile is None: 1426 BillingProfile = cmd.get_models('BillingProfile') 1427 vm.billing_profile = BillingProfile(max_price=max_price) 1428 else: 1429 vm.billing_profile.max_price = max_price 1430 1431 if proximity_placement_group is not None: 1432 vm.proximity_placement_group = {'id': proximity_placement_group} 1433 1434 if enable_secure_boot is not None or enable_vtpm is not None: 1435 vm.security_profile = {'uefiSettings': { 1436 'secureBootEnabled': enable_secure_boot, 1437 'vTpmEnabled': enable_vtpm 1438 }} 1439 1440 if workspace is not None: 1441 workspace_id = _prepare_workspace(cmd, resource_group_name, workspace) 1442 workspace_name = parse_resource_id(workspace_id)['name'] 1443 _set_log_analytics_workspace_extension(cmd=cmd, 1444 resource_group_name=resource_group_name, 1445 vm=vm, 1446 vm_name=vm_name, 1447 workspace_name=workspace_name) 1448 os_type = vm.storage_profile.os_disk.os_type if vm.storage_profile.os_disk.os_type else None 1449 _set_data_source_for_workspace(cmd, os_type, resource_group_name, workspace_name) 1450 1451 aux_subscriptions = None 1452 if vm and vm.storage_profile and vm.storage_profile.image_reference and 'id' in vm.storage_profile.image_reference: 1453 aux_subscriptions = _parse_aux_subscriptions(vm.storage_profile.image_reference['id']) 1454 client = _compute_client_factory(cmd.cli_ctx, aux_subscriptions=aux_subscriptions) 1455 return sdk_no_wait(no_wait, client.virtual_machines.begin_create_or_update, resource_group_name, vm_name, **kwargs) 1456# endregion 1457 1458 1459# region VirtualMachines AvailabilitySets 1460def _get_availset(cmd, resource_group_name, name): 1461 return _compute_client_factory(cmd.cli_ctx).availability_sets.get(resource_group_name, name) 1462 1463 1464def _set_availset(cmd, resource_group_name, name, **kwargs): 1465 return _compute_client_factory(cmd.cli_ctx).availability_sets.create_or_update(resource_group_name, name, **kwargs) 1466 1467 1468# pylint: disable=inconsistent-return-statements 1469def convert_av_set_to_managed_disk(cmd, resource_group_name, availability_set_name): 1470 av_set = _get_availset(cmd, resource_group_name, availability_set_name) 1471 if av_set.sku.name != 'Aligned': 1472 av_set.sku.name = 'Aligned' 1473 1474 # let us double check whether the existing FD number is supported 1475 skus = list_skus(cmd, av_set.location) 1476 av_sku = next((s for s in skus if s.resource_type == 'availabilitySets' and s.name == 'Aligned'), None) 1477 if av_sku and av_sku.capabilities: 1478 max_fd = int(next((c.value for c in av_sku.capabilities if c.name == 'MaximumPlatformFaultDomainCount'), 1479 '0')) 1480 if max_fd and max_fd < av_set.platform_fault_domain_count: 1481 logger.warning("The fault domain count will be adjusted from %s to %s so to stay within region's " 1482 "limitation", av_set.platform_fault_domain_count, max_fd) 1483 av_set.platform_fault_domain_count = max_fd 1484 1485 return _set_availset(cmd, resource_group_name=resource_group_name, name=availability_set_name, 1486 parameters=av_set) 1487 logger.warning('Availability set %s is already configured for managed disks.', availability_set_name) 1488 1489 1490def create_av_set(cmd, availability_set_name, resource_group_name, platform_fault_domain_count=2, 1491 platform_update_domain_count=None, location=None, proximity_placement_group=None, unmanaged=False, 1492 no_wait=False, tags=None, validate=False): 1493 from azure.cli.core.util import random_string 1494 from azure.cli.core.commands.arm import ArmTemplateBuilder 1495 from azure.cli.command_modules.vm._template_builder import build_av_set_resource 1496 1497 tags = tags or {} 1498 1499 # Build up the ARM template 1500 master_template = ArmTemplateBuilder() 1501 1502 av_set_resource = build_av_set_resource(cmd, availability_set_name, location, tags, 1503 platform_update_domain_count, 1504 platform_fault_domain_count, unmanaged, 1505 proximity_placement_group=proximity_placement_group) 1506 master_template.add_resource(av_set_resource) 1507 1508 template = master_template.build() 1509 1510 # deploy ARM template 1511 deployment_name = 'av_set_deploy_' + random_string(32) 1512 client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES).deployments 1513 DeploymentProperties = cmd.get_models('DeploymentProperties', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) 1514 properties = DeploymentProperties(template=template, parameters={}, mode='incremental') 1515 Deployment = cmd.get_models('Deployment', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) 1516 deployment = Deployment(properties=properties) 1517 1518 if validate: 1519 if cmd.supported_api_version(min_api='2019-10-01', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES): 1520 validation_poller = client.begin_validate(resource_group_name, deployment_name, deployment) 1521 return LongRunningOperation(cmd.cli_ctx)(validation_poller) 1522 1523 return client.validate(resource_group_name, deployment_name, deployment) 1524 1525 if no_wait: 1526 return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, deployment_name, deployment) 1527 LongRunningOperation(cmd.cli_ctx)(sdk_no_wait(no_wait, client.begin_create_or_update, 1528 resource_group_name, deployment_name, deployment)) 1529 1530 compute_client = _compute_client_factory(cmd.cli_ctx) 1531 return compute_client.availability_sets.get(resource_group_name, availability_set_name) 1532 1533 1534def update_av_set(instance, resource_group_name, proximity_placement_group=None): 1535 if proximity_placement_group is not None: 1536 instance.proximity_placement_group = {'id': proximity_placement_group} 1537 return instance 1538 1539 1540def list_av_sets(cmd, resource_group_name=None): 1541 op_group = _compute_client_factory(cmd.cli_ctx).availability_sets 1542 if resource_group_name: 1543 return op_group.list(resource_group_name) 1544 return op_group.list_by_subscription(expand='virtualMachines/$ref') 1545# endregion 1546 1547 1548# region VirtualMachines BootDiagnostics 1549def disable_boot_diagnostics(cmd, resource_group_name, vm_name): 1550 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 1551 diag_profile = vm.diagnostics_profile 1552 if not (diag_profile and diag_profile.boot_diagnostics and diag_profile.boot_diagnostics.enabled): 1553 return 1554 1555 diag_profile.boot_diagnostics.enabled = False 1556 diag_profile.boot_diagnostics.storage_uri = None 1557 set_vm(cmd, vm, ExtensionUpdateLongRunningOperation(cmd.cli_ctx, 'disabling boot diagnostics', 'done')) 1558 1559 1560def enable_boot_diagnostics(cmd, resource_group_name, vm_name, storage=None): 1561 from azure.cli.command_modules.vm._vm_utils import get_storage_blob_uri 1562 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 1563 storage_uri = None 1564 if storage: 1565 storage_uri = get_storage_blob_uri(cmd.cli_ctx, storage) 1566 1567 DiagnosticsProfile, BootDiagnostics = cmd.get_models('DiagnosticsProfile', 'BootDiagnostics') 1568 1569 boot_diag = BootDiagnostics(enabled=True, storage_uri=storage_uri) 1570 if vm.diagnostics_profile is None: 1571 vm.diagnostics_profile = DiagnosticsProfile(boot_diagnostics=boot_diag) 1572 else: 1573 vm.diagnostics_profile.boot_diagnostics = boot_diag 1574 1575 set_vm(cmd, vm, ExtensionUpdateLongRunningOperation(cmd.cli_ctx, 'enabling boot diagnostics', 'done')) 1576 1577 1578class BootLogStreamWriter: # pylint: disable=too-few-public-methods 1579 1580 def __init__(self, out): 1581 self.out = out 1582 1583 def write(self, str_or_bytes): 1584 content = str_or_bytes 1585 if isinstance(str_or_bytes, bytes): 1586 content = str_or_bytes.decode('utf8') 1587 try: 1588 self.out.write(content) 1589 except UnicodeEncodeError: 1590 # e.g. 'charmap' codec can't encode characters in position 258829-258830: character maps to <undefined> 1591 import unicodedata 1592 ascii_content = unicodedata.normalize('NFKD', content).encode('ascii', 'ignore') 1593 self.out.write(ascii_content.decode()) 1594 logger.warning("A few unicode characters have been ignored because the shell is not able to display. " 1595 "To see the full log, use a shell with unicode capacity") 1596 1597 1598def get_boot_log(cmd, resource_group_name, vm_name): 1599 import re 1600 import sys 1601 from azure.cli.core.profiles import get_sdk 1602 from msrestazure.azure_exceptions import CloudError 1603 BlockBlobService = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE, 'blob.blockblobservice#BlockBlobService') 1604 1605 client = _compute_client_factory(cmd.cli_ctx) 1606 1607 virtual_machine = client.virtual_machines.get(resource_group_name, vm_name, expand='instanceView') 1608 # pylint: disable=no-member 1609 1610 blob_uri = None 1611 if virtual_machine.instance_view and virtual_machine.instance_view.boot_diagnostics: 1612 blob_uri = virtual_machine.instance_view.boot_diagnostics.serial_console_log_blob_uri 1613 1614 # Managed storage 1615 if blob_uri is None: 1616 try: 1617 boot_diagnostics_data = client.virtual_machines.retrieve_boot_diagnostics_data(resource_group_name, vm_name) 1618 blob_uri = boot_diagnostics_data.serial_console_log_blob_uri 1619 except CloudError: 1620 pass 1621 if blob_uri is None: 1622 raise CLIError('Please enable boot diagnostics.') 1623 return requests.get(blob_uri).content 1624 1625 # Find storage account for diagnostics 1626 storage_mgmt_client = _get_storage_management_client(cmd.cli_ctx) 1627 if not blob_uri: 1628 raise CLIError('No console log available') 1629 try: 1630 storage_accounts = storage_mgmt_client.storage_accounts.list() 1631 matching_storage_account = (a for a in list(storage_accounts) 1632 if a.primary_endpoints.blob and blob_uri.startswith(a.primary_endpoints.blob)) 1633 storage_account = next(matching_storage_account) 1634 except StopIteration: 1635 raise CLIError('Failed to find storage account for console log file') 1636 1637 regex = r'/subscriptions/[^/]+/resourceGroups/(?P<rg>[^/]+)/.+' 1638 match = re.search(regex, storage_account.id, re.I) 1639 rg = match.group('rg') 1640 # Get account key 1641 keys = storage_mgmt_client.storage_accounts.list_keys(rg, storage_account.name) 1642 1643 # Extract container and blob name from url... 1644 container, blob = urlparse(blob_uri).path.split('/')[-2:] 1645 1646 storage_client = get_data_service_client( 1647 cmd.cli_ctx, 1648 BlockBlobService, 1649 storage_account.name, 1650 keys.keys[0].value, 1651 endpoint_suffix=cmd.cli_ctx.cloud.suffixes.storage_endpoint) # pylint: disable=no-member 1652 1653 # our streamwriter not seekable, so no parallel. 1654 storage_client.get_blob_to_stream(container, blob, BootLogStreamWriter(sys.stdout), max_connections=1) 1655 1656 1657def get_boot_log_uris(cmd, resource_group_name, vm_name, expire=None): 1658 client = _compute_client_factory(cmd.cli_ctx) 1659 return client.virtual_machines.retrieve_boot_diagnostics_data( 1660 resource_group_name, vm_name, sas_uri_expiration_time_in_minutes=expire) 1661# endregion 1662 1663 1664# region VirtualMachines Diagnostics 1665def set_diagnostics_extension( 1666 cmd, resource_group_name, vm_name, settings, protected_settings=None, version=None, 1667 no_auto_upgrade=False): 1668 client = _compute_client_factory(cmd.cli_ctx) 1669 vm = client.virtual_machines.get(resource_group_name, vm_name, 'instanceView') 1670 # pylint: disable=no-member 1671 is_linux_os = _is_linux_os(vm) 1672 vm_extension_name = _LINUX_DIAG_EXT if is_linux_os else _WINDOWS_DIAG_EXT 1673 if is_linux_os: # check incompatible version 1674 exts = vm.instance_view.extensions or [] 1675 major_ver = extension_mappings[_LINUX_DIAG_EXT]['version'].split('.')[0] 1676 if next((e for e in exts if e.name == vm_extension_name and 1677 not e.type_handler_version.startswith(major_ver + '.')), None): 1678 logger.warning('There is an incompatible version of diagnostics extension installed. ' 1679 'We will update it with a new version') 1680 poller = client.virtual_machine_extensions.begin_delete(resource_group_name, vm_name, vm_extension_name) 1681 LongRunningOperation(cmd.cli_ctx)(poller) 1682 1683 return set_extension(cmd, resource_group_name, vm_name, vm_extension_name, 1684 extension_mappings[vm_extension_name]['publisher'], 1685 version or extension_mappings[vm_extension_name]['version'], 1686 settings, 1687 protected_settings, 1688 no_auto_upgrade) 1689 1690 1691def show_default_diagnostics_configuration(is_windows_os=False): 1692 public_settings = get_default_diag_config(is_windows_os) 1693 # pylint: disable=line-too-long 1694 protected_settings_info = json.dumps({ 1695 'storageAccountName': "__STORAGE_ACCOUNT_NAME__", 1696 # LAD and WAD are not consistent on sas token format. Call it out here 1697 "storageAccountSasToken": "__SAS_TOKEN_{}__".format("WITH_LEADING_QUESTION_MARK" if is_windows_os else "WITHOUT_LEADING_QUESTION_MARK") 1698 }, indent=2) 1699 logger.warning('Protected settings with storage account info is required to work with the default configurations, e.g. \n%s', protected_settings_info) 1700 return public_settings 1701# endregion 1702 1703 1704# region VirtualMachines Disks (Managed) 1705def attach_managed_data_disk(cmd, resource_group_name, vm_name, disk, new=False, sku=None, 1706 size_gb=1023, lun=None, caching=None, enable_write_accelerator=False): 1707 '''attach a managed disk''' 1708 from msrestazure.tools import parse_resource_id 1709 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 1710 DataDisk, ManagedDiskParameters, DiskCreateOption = cmd.get_models( 1711 'DataDisk', 'ManagedDiskParameters', 'DiskCreateOptionTypes') 1712 1713 # pylint: disable=no-member 1714 if lun is None: 1715 lun = _get_disk_lun(vm.storage_profile.data_disks) 1716 if new: 1717 data_disk = DataDisk(lun=lun, create_option=DiskCreateOption.empty, 1718 name=parse_resource_id(disk)['name'], 1719 disk_size_gb=size_gb, caching=caching, 1720 managed_disk=ManagedDiskParameters(storage_account_type=sku)) 1721 else: 1722 params = ManagedDiskParameters(id=disk, storage_account_type=sku) 1723 data_disk = DataDisk(lun=lun, create_option=DiskCreateOption.attach, managed_disk=params, caching=caching) 1724 1725 if enable_write_accelerator: 1726 data_disk.write_accelerator_enabled = enable_write_accelerator 1727 1728 vm.storage_profile.data_disks.append(data_disk) 1729 set_vm(cmd, vm) 1730 1731 1732def detach_data_disk(cmd, resource_group_name, vm_name, disk_name): 1733 # here we handle both unmanaged or managed disk 1734 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 1735 # pylint: disable=no-member 1736 leftovers = [d for d in vm.storage_profile.data_disks if d.name.lower() != disk_name.lower()] 1737 if len(vm.storage_profile.data_disks) == len(leftovers): 1738 raise CLIError("No disk with the name '{}' was found".format(disk_name)) 1739 vm.storage_profile.data_disks = leftovers 1740 set_vm(cmd, vm) 1741# endregion 1742 1743 1744# region VirtualMachines Extensions 1745def list_extensions(cmd, resource_group_name, vm_name): 1746 vm = get_vm(cmd, resource_group_name, vm_name) 1747 extension_type = 'Microsoft.Compute/virtualMachines/extensions' 1748 result = [r for r in (vm.resources or []) if r.type == extension_type] 1749 return result 1750 1751 1752def set_extension(cmd, resource_group_name, vm_name, vm_extension_name, publisher, version=None, settings=None, 1753 protected_settings=None, no_auto_upgrade=False, force_update=False, no_wait=False, 1754 extension_instance_name=None, enable_auto_upgrade=None): 1755 vm = get_vm(cmd, resource_group_name, vm_name, 'instanceView') 1756 client = _compute_client_factory(cmd.cli_ctx) 1757 1758 if not extension_instance_name: 1759 extension_instance_name = vm_extension_name 1760 1761 VirtualMachineExtension = cmd.get_models('VirtualMachineExtension') 1762 instance_name = _get_extension_instance_name(vm.instance_view, publisher, vm_extension_name, 1763 suggested_name=extension_instance_name) 1764 if instance_name != extension_instance_name: 1765 msg = "A %s extension with name %s already exists. Updating it with your settings..." 1766 logger.warning(msg, vm_extension_name, instance_name) 1767 1768 version = _normalize_extension_version(cmd.cli_ctx, publisher, vm_extension_name, version, vm.location) 1769 ext = VirtualMachineExtension(location=vm.location, 1770 publisher=publisher, 1771 type_properties_type=vm_extension_name, 1772 protected_settings=protected_settings, 1773 type_handler_version=version, 1774 settings=settings, 1775 auto_upgrade_minor_version=(not no_auto_upgrade), 1776 enable_automatic_upgrade=enable_auto_upgrade) 1777 if force_update: 1778 ext.force_update_tag = str(_gen_guid()) 1779 return sdk_no_wait(no_wait, client.virtual_machine_extensions.begin_create_or_update, 1780 resource_group_name, vm_name, instance_name, ext) 1781# endregion 1782 1783 1784# region VirtualMachines Extension Images 1785def list_vm_extension_images( 1786 cmd, image_location=None, publisher_name=None, name=None, version=None, latest=False): 1787 return load_extension_images_thru_services( 1788 cmd.cli_ctx, publisher_name, name, version, image_location, latest) 1789# endregion 1790 1791 1792# region VirtualMachines Identity 1793def _remove_identities(cmd, resource_group_name, name, identities, getter, setter): 1794 from ._vm_utils import MSI_LOCAL_ID 1795 ResourceIdentityType = cmd.get_models('ResourceIdentityType', operation_group='virtual_machines') 1796 remove_system_assigned_identity = False 1797 if MSI_LOCAL_ID in identities: 1798 remove_system_assigned_identity = True 1799 identities.remove(MSI_LOCAL_ID) 1800 resource = getter(cmd, resource_group_name, name) 1801 if resource.identity is None: 1802 return None 1803 emsis_to_remove = [] 1804 if identities: 1805 existing_emsis = {x.lower() for x in list((resource.identity.user_assigned_identities or {}).keys())} 1806 emsis_to_remove = {x.lower() for x in identities} 1807 non_existing = emsis_to_remove.difference(existing_emsis) 1808 if non_existing: 1809 raise CLIError("'{}' are not associated with '{}'".format(','.join(non_existing), name)) 1810 if not list(existing_emsis - emsis_to_remove): # if all emsis are gone, we need to update the type 1811 if resource.identity.type == ResourceIdentityType.user_assigned: 1812 resource.identity.type = ResourceIdentityType.none 1813 elif resource.identity.type == ResourceIdentityType.system_assigned_user_assigned: 1814 resource.identity.type = ResourceIdentityType.system_assigned 1815 1816 resource.identity.user_assigned_identities = None 1817 if remove_system_assigned_identity: 1818 resource.identity.type = (ResourceIdentityType.none 1819 if resource.identity.type == ResourceIdentityType.system_assigned 1820 else ResourceIdentityType.user_assigned) 1821 1822 if emsis_to_remove: 1823 if resource.identity.type not in [ResourceIdentityType.none, ResourceIdentityType.system_assigned]: 1824 resource.identity.user_assigned_identities = {} 1825 for identity in emsis_to_remove: 1826 resource.identity.user_assigned_identities[identity] = None 1827 1828 result = LongRunningOperation(cmd.cli_ctx)(setter(resource_group_name, name, resource)) 1829 return result.identity 1830 1831 1832def remove_vm_identity(cmd, resource_group_name, vm_name, identities=None): 1833 def setter(resource_group_name, vm_name, vm): 1834 client = _compute_client_factory(cmd.cli_ctx) 1835 VirtualMachineUpdate = cmd.get_models('VirtualMachineUpdate', operation_group='virtual_machines') 1836 vm_update = VirtualMachineUpdate(identity=vm.identity) 1837 return client.virtual_machines.begin_update(resource_group_name, vm_name, vm_update) 1838 1839 if identities is None: 1840 from ._vm_utils import MSI_LOCAL_ID 1841 identities = [MSI_LOCAL_ID] 1842 1843 return _remove_identities(cmd, resource_group_name, vm_name, identities, get_vm, setter) 1844# endregion 1845 1846 1847# region VirtualMachines Images 1848def list_vm_images(cmd, image_location=None, publisher_name=None, offer=None, sku=None, all=False, edge_zone=None): # pylint: disable=redefined-builtin 1849 load_thru_services = all or edge_zone is not None 1850 1851 if load_thru_services: 1852 if not publisher_name and not offer and not sku and not edge_zone: 1853 logger.warning("You are retrieving all the images from server which could take more than a minute. " 1854 "To shorten the wait, provide '--publisher', '--offer' , '--sku' or '--edge-zone'." 1855 " Partial name search is supported.") 1856 all_images = load_images_thru_services(cmd.cli_ctx, publisher_name, offer, sku, image_location, edge_zone) 1857 else: 1858 all_images = load_images_from_aliases_doc(cmd.cli_ctx, publisher_name, offer, sku) 1859 logger.warning('You are viewing an offline list of images, use --all to retrieve an up-to-date list') 1860 1861 if edge_zone is not None: 1862 for i in all_images: 1863 i['urn'] = ':'.join([i['publisher'], i['offer'], i['sku'], i['edge_zone'], i['version']]) 1864 else: 1865 for i in all_images: 1866 i['urn'] = ':'.join([i['publisher'], i['offer'], i['sku'], i['version']]) 1867 return all_images 1868 1869 1870def list_offers(cmd, publisher_name, location, edge_zone=None): 1871 if edge_zone is not None: 1872 edge_zone_client = get_mgmt_service_client(cmd.cli_ctx, 1873 ResourceType.MGMT_COMPUTE).virtual_machine_images_edge_zone 1874 return edge_zone_client.list_offers(location=location, edge_zone=edge_zone, publisher_name=publisher_name) 1875 else: 1876 client = _compute_client_factory(cmd.cli_ctx).virtual_machine_images 1877 return client.list_offers(location=location, publisher_name=publisher_name) 1878 1879 1880def list_publishers(cmd, location, edge_zone=None): 1881 if edge_zone is not None: 1882 edge_zone_client = get_mgmt_service_client(cmd.cli_ctx, 1883 ResourceType.MGMT_COMPUTE).virtual_machine_images_edge_zone 1884 return edge_zone_client.list_publishers(location=location, edge_zone=edge_zone) 1885 else: 1886 client = _compute_client_factory(cmd.cli_ctx).virtual_machine_images 1887 return client.list_publishers(location=location) 1888 1889 1890def list_sku(cmd, location, publisher_name, offer, edge_zone=None,): 1891 if edge_zone is not None: 1892 edge_zone_client = get_mgmt_service_client(cmd.cli_ctx, 1893 ResourceType.MGMT_COMPUTE).virtual_machine_images_edge_zone 1894 return edge_zone_client.list_skus(location=location, edge_zone=edge_zone, 1895 publisher_name=publisher_name, offer=offer) 1896 else: 1897 client = _compute_client_factory(cmd.cli_ctx).virtual_machine_images 1898 return client.list_skus(location=location, publisher_name=publisher_name, offer=offer) 1899 1900 1901def show_vm_image(cmd, urn=None, publisher=None, offer=None, sku=None, version=None, location=None, edge_zone=None): 1902 from azure.cli.core.commands.parameters import get_one_of_subscription_locations 1903 from azure.cli.core.azclierror import (MutuallyExclusiveArgumentError, 1904 InvalidArgumentValueError) 1905 1906 location = location or get_one_of_subscription_locations(cmd.cli_ctx) 1907 error_msg = 'Please specify all of (--publisher, --offer, --sku, --version), or --urn' 1908 if urn: 1909 if any([publisher, offer, sku, edge_zone, version]): 1910 recommendation = 'Try to use --urn publisher:offer:sku:version or' \ 1911 ' --urn publisher:offer:sku:edge_zone:version' 1912 raise MutuallyExclusiveArgumentError(error_msg, recommendation) 1913 items = urn.split(":") 1914 if len(items) != 4 and len(items) != 5: 1915 raise InvalidArgumentValueError( 1916 '--urn should be in the format of publisher:offer:sku:version or publisher:offer:sku:edge_zone:version') 1917 if len(items) == 5: 1918 publisher, offer, sku, edge_zone, version = urn.split(":") 1919 elif len(items) == 4: 1920 publisher, offer, sku, version = urn.split(":") 1921 if version.lower() == 'latest': 1922 version = _get_latest_image_version(cmd.cli_ctx, location, publisher, offer, sku) 1923 elif not publisher or not offer or not sku or not version: 1924 raise RequiredArgumentMissingError(error_msg) 1925 if edge_zone is not None: 1926 edge_zone_client = get_mgmt_service_client(cmd.cli_ctx, 1927 ResourceType.MGMT_COMPUTE).virtual_machine_images_edge_zone 1928 return edge_zone_client.get(location=location, edge_zone=edge_zone, publisher_name=publisher, offer=offer, 1929 skus=sku, version=version) 1930 else: 1931 client = _compute_client_factory(cmd.cli_ctx) 1932 return client.virtual_machine_images.get(location, publisher, offer, sku, version) 1933 1934 1935def accept_market_ordering_terms(cmd, urn=None, publisher=None, offer=None, plan=None): 1936 from azure.mgmt.marketplaceordering import MarketplaceOrderingAgreements 1937 from azure.mgmt.marketplaceordering.models import OfferType 1938 from azure.cli.core.azclierror import (MutuallyExclusiveArgumentError, 1939 InvalidArgumentValueError) 1940 1941 error_msg = 'Please specify all of (--plan, --offer, --publish), or --urn' 1942 if urn: 1943 if any([publisher, offer, plan]): 1944 recommendation = 'Try to use --urn publisher:offer:sku:version only' 1945 raise MutuallyExclusiveArgumentError(error_msg, recommendation) 1946 items = urn.split(':') 1947 if len(items) != 4: 1948 raise InvalidArgumentValueError('--urn should be in the format of publisher:offer:sku:version') 1949 publisher, offer, _, _ = items 1950 image = show_vm_image(cmd, urn) 1951 if not image.plan: 1952 logger.warning("Image '%s' has no terms to accept.", urn) 1953 return 1954 plan = image.plan.name 1955 else: 1956 if not publisher or not offer or not plan: 1957 raise RequiredArgumentMissingError(error_msg) 1958 1959 market_place_client = get_mgmt_service_client(cmd.cli_ctx, MarketplaceOrderingAgreements) 1960 1961 term = market_place_client.marketplace_agreements.get(offer_type=OfferType.VIRTUALMACHINE, 1962 publisher_id=publisher, 1963 offer_id=offer, 1964 plan_id=plan) 1965 term.accepted = True 1966 return market_place_client.marketplace_agreements.create(offer_type=OfferType.VIRTUALMACHINE, 1967 publisher_id=publisher, 1968 offer_id=offer, 1969 plan_id=plan, 1970 parameters=term) 1971# endregion 1972 1973 1974def _terms_prepare(cmd, urn, publisher, offer, plan): 1975 if urn: 1976 if any([publisher, offer, plan]): 1977 raise CLIError('usage error: If using --urn, do not use any of --plan, --offer, --publisher.') 1978 terms = urn.split(':') 1979 if len(terms) != 4: 1980 raise CLIError('usage error: urn should be in the format of publisher:offer:sku:version.') 1981 publisher, offer = terms[0], terms[1] 1982 image = show_vm_image(cmd, urn) 1983 if not image.plan: 1984 raise CLIError("Image '%s' has no terms to accept." % urn) 1985 plan = image.plan.name 1986 else: 1987 if not all([publisher, offer, plan]): 1988 raise CLIError( 1989 'usage error: If not using --urn, all of --plan, --offer and --publisher should be provided.') 1990 return publisher, offer, plan 1991 1992 1993def _accept_cancel_terms(cmd, urn, publisher, offer, plan, accept): 1994 from azure.mgmt.marketplaceordering.models import OfferType 1995 publisher, offer, plan = _terms_prepare(cmd, urn, publisher, offer, plan) 1996 op = cf_vm_image_term(cmd.cli_ctx, '') 1997 terms = op.get(offer_type=OfferType.VIRTUALMACHINE, 1998 publisher_id=publisher, 1999 offer_id=offer, 2000 plan_id=plan) 2001 terms.accepted = accept 2002 return op.create(offer_type=OfferType.VIRTUALMACHINE, 2003 publisher_id=publisher, 2004 offer_id=offer, 2005 plan_id=plan, 2006 parameters=terms) 2007 2008 2009def accept_terms(cmd, urn=None, publisher=None, offer=None, plan=None): 2010 """ 2011 Accept Azure Marketplace image terms so that the image can be used to create VMs. 2012 :param cmd:cmd 2013 :param urn:URN, in the format of 'publisher:offer:sku:version'. If specified, other argument values can be omitted 2014 :param publisher:Image publisher 2015 :param offer:Image offer 2016 :param plan:Image billing plan 2017 :return: 2018 """ 2019 return _accept_cancel_terms(cmd, urn, publisher, offer, plan, True) 2020 2021 2022def cancel_terms(cmd, urn=None, publisher=None, offer=None, plan=None): 2023 """ 2024 Cancel Azure Marketplace image terms. 2025 :param cmd:cmd 2026 :param urn:URN, in the format of 'publisher:offer:sku:version'. If specified, other argument values can be omitted 2027 :param publisher:Image publisher 2028 :param offer:Image offer 2029 :param plan:Image billing plan 2030 :return: 2031 """ 2032 return _accept_cancel_terms(cmd, urn, publisher, offer, plan, False) 2033 2034 2035def get_terms(cmd, urn=None, publisher=None, offer=None, plan=None): 2036 """ 2037 Get the details of Azure Marketplace image terms. 2038 :param cmd:cmd 2039 :param urn:URN, in the format of 'publisher:offer:sku:version'. If specified, other argument values can be omitted 2040 :param publisher:Image publisher 2041 :param offer:Image offer 2042 :param plan:Image billing plan 2043 :return: 2044 """ 2045 from azure.mgmt.marketplaceordering.models import OfferType 2046 publisher, offer, plan = _terms_prepare(cmd, urn, publisher, offer, plan) 2047 op = cf_vm_image_term(cmd.cli_ctx, '') 2048 terms = op.get(offer_type=OfferType.VIRTUALMACHINE, 2049 publisher_id=publisher, 2050 offer_id=offer, 2051 plan_id=plan) 2052 return terms 2053 2054 2055# region VirtualMachines NetworkInterfaces (NICs) 2056def show_vm_nic(cmd, resource_group_name, vm_name, nic): 2057 from msrestazure.tools import parse_resource_id 2058 vm = get_vm(cmd, resource_group_name, vm_name) 2059 found = next( 2060 (n for n in vm.network_profile.network_interfaces if nic.lower() == n.id.lower()), None 2061 # pylint: disable=no-member 2062 ) 2063 if found: 2064 network_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_NETWORK) 2065 nic_name = parse_resource_id(found.id)['name'] 2066 return network_client.network_interfaces.get(resource_group_name, nic_name) 2067 raise CLIError("NIC '{}' not found on VM '{}'".format(nic, vm_name)) 2068 2069 2070def list_vm_nics(cmd, resource_group_name, vm_name): 2071 vm = get_vm(cmd, resource_group_name, vm_name) 2072 return vm.network_profile.network_interfaces # pylint: disable=no-member 2073 2074 2075def add_vm_nic(cmd, resource_group_name, vm_name, nics, primary_nic=None): 2076 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 2077 new_nics = _build_nic_list(cmd, nics) 2078 existing_nics = _get_existing_nics(vm) 2079 return _update_vm_nics(cmd, vm, existing_nics + new_nics, primary_nic) 2080 2081 2082def remove_vm_nic(cmd, resource_group_name, vm_name, nics, primary_nic=None): 2083 2084 def to_delete(nic_id): 2085 return [n for n in nics_to_delete if n.id.lower() == nic_id.lower()] 2086 2087 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 2088 nics_to_delete = _build_nic_list(cmd, nics) 2089 existing_nics = _get_existing_nics(vm) 2090 survived = [x for x in existing_nics if not to_delete(x.id)] 2091 return _update_vm_nics(cmd, vm, survived, primary_nic) 2092 2093 2094def set_vm_nic(cmd, resource_group_name, vm_name, nics, primary_nic=None): 2095 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 2096 nics = _build_nic_list(cmd, nics) 2097 return _update_vm_nics(cmd, vm, nics, primary_nic) 2098 2099 2100def _build_nic_list(cmd, nic_ids): 2101 NetworkInterfaceReference = cmd.get_models('NetworkInterfaceReference') 2102 nic_list = [] 2103 if nic_ids: 2104 # pylint: disable=no-member 2105 network_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_NETWORK) 2106 for nic_id in nic_ids: 2107 rg, name = _parse_rg_name(nic_id) 2108 nic = network_client.network_interfaces.get(rg, name) 2109 nic_list.append(NetworkInterfaceReference(id=nic.id, primary=False)) 2110 return nic_list 2111 2112 2113def _get_existing_nics(vm): 2114 network_profile = getattr(vm, 'network_profile', None) 2115 nics = [] 2116 if network_profile is not None: 2117 nics = network_profile.network_interfaces or [] 2118 return nics 2119 2120 2121def _update_vm_nics(cmd, vm, nics, primary_nic): 2122 NetworkProfile = cmd.get_models('NetworkProfile') 2123 2124 if primary_nic: 2125 try: 2126 _, primary_nic_name = _parse_rg_name(primary_nic) 2127 except IndexError: 2128 primary_nic_name = primary_nic 2129 2130 matched = [n for n in nics if _parse_rg_name(n.id)[1].lower() == primary_nic_name.lower()] 2131 if not matched: 2132 raise CLIError('Primary Nic {} is not found'.format(primary_nic)) 2133 if len(matched) > 1: 2134 raise CLIError('Duplicate Nic entries with name {}'.format(primary_nic)) 2135 for n in nics: 2136 n.primary = False 2137 matched[0].primary = True 2138 elif nics: 2139 if not [n for n in nics if n.primary]: 2140 nics[0].primary = True 2141 2142 network_profile = getattr(vm, 'network_profile', None) 2143 if network_profile is None: 2144 vm.network_profile = NetworkProfile(network_interfaces=nics) 2145 else: 2146 network_profile.network_interfaces = nics 2147 2148 return set_vm(cmd, vm).network_profile.network_interfaces 2149# endregion 2150 2151 2152# region VirtualMachines RunCommand 2153def run_command_invoke(cmd, resource_group_name, vm_vmss_name, command_id, scripts=None, parameters=None, instance_id=None): # pylint: disable=line-too-long 2154 RunCommandInput, RunCommandInputParameter = cmd.get_models('RunCommandInput', 'RunCommandInputParameter') 2155 2156 parameters = parameters or [] 2157 run_command_input_parameters = [] 2158 auto_arg_name_num = 0 2159 for p in parameters: 2160 if '=' in p: 2161 n, v = p.split('=', 1) 2162 else: 2163 # RunCommand API requires named arguments, which doesn't make lots of sense for bash scripts 2164 # using positional arguments, so here we provide names just to get API happy 2165 # note, we don't handle mixing styles, but will consolidate by GA when API is settled 2166 auto_arg_name_num += 1 2167 n = 'arg{}'.format(auto_arg_name_num) 2168 v = p 2169 run_command_input_parameters.append(RunCommandInputParameter(name=n, value=v)) 2170 2171 client = _compute_client_factory(cmd.cli_ctx) 2172 2173 # if instance_id, this is a vmss instance 2174 if instance_id: 2175 return client.virtual_machine_scale_set_vms.begin_run_command( 2176 resource_group_name, vm_vmss_name, instance_id, 2177 RunCommandInput(command_id=command_id, script=scripts, parameters=run_command_input_parameters)) # pylint: disable=line-too-long 2178 # otherwise this is a regular vm instance 2179 return client.virtual_machines.begin_run_command( 2180 resource_group_name, vm_vmss_name, 2181 RunCommandInput(command_id=command_id, script=scripts, parameters=run_command_input_parameters)) 2182 2183 2184def vm_run_command_invoke(cmd, resource_group_name, vm_name, command_id, scripts=None, parameters=None): 2185 return run_command_invoke(cmd, resource_group_name, vm_name, command_id, scripts, parameters) 2186 2187# endregion 2188 2189 2190# region VirtualMachines Secrets 2191def _get_vault_id_from_name(cli_ctx, client, vault_name): 2192 group_name = _get_resource_group_from_vault_name(cli_ctx, vault_name) 2193 if not group_name: 2194 raise CLIError("unable to find vault '{}' in current subscription.".format(vault_name)) 2195 vault = client.get(group_name, vault_name) 2196 return vault.id 2197 2198 2199def get_vm_format_secret(cmd, secrets, certificate_store=None, keyvault=None, resource_group_name=None): 2200 from azure.keyvault import KeyVaultId 2201 import re 2202 client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_KEYVAULT).vaults 2203 grouped_secrets = {} 2204 2205 merged_secrets = [] 2206 for s in secrets: 2207 merged_secrets += s.splitlines() 2208 2209 # group secrets by source vault 2210 for secret in merged_secrets: 2211 parsed = KeyVaultId.parse_secret_id(secret) 2212 match = re.search('://(.+?)\\.', parsed.vault) 2213 vault_name = match.group(1) 2214 if vault_name not in grouped_secrets: 2215 grouped_secrets[vault_name] = { 2216 'vaultCertificates': [], 2217 'id': keyvault or _get_vault_id_from_name(cmd.cli_ctx, client, vault_name) 2218 } 2219 2220 vault_cert = {'certificateUrl': secret} 2221 if certificate_store: 2222 vault_cert['certificateStore'] = certificate_store 2223 2224 grouped_secrets[vault_name]['vaultCertificates'].append(vault_cert) 2225 2226 # transform the reduced map to vm format 2227 formatted = [{'sourceVault': {'id': value['id']}, 2228 'vaultCertificates': value['vaultCertificates']} 2229 for _, value in list(grouped_secrets.items())] 2230 2231 return formatted 2232 2233 2234def add_vm_secret(cmd, resource_group_name, vm_name, keyvault, certificate, certificate_store=None): 2235 from msrestazure.tools import parse_resource_id 2236 from ._vm_utils import create_keyvault_data_plane_client, get_key_vault_base_url 2237 VaultSecretGroup, SubResource, VaultCertificate = cmd.get_models( 2238 'VaultSecretGroup', 'SubResource', 'VaultCertificate') 2239 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 2240 2241 if '://' not in certificate: # has a cert name rather a full url? 2242 keyvault_client = create_keyvault_data_plane_client(cmd.cli_ctx) 2243 cert_info = keyvault_client.get_certificate( 2244 get_key_vault_base_url(cmd.cli_ctx, parse_resource_id(keyvault)['name']), certificate, '') 2245 certificate = cert_info.sid 2246 2247 if not _is_linux_os(vm): 2248 certificate_store = certificate_store or 'My' 2249 elif certificate_store: 2250 raise CLIError('Usage error: --certificate-store is only applicable on Windows VM') 2251 vault_cert = VaultCertificate(certificate_url=certificate, certificate_store=certificate_store) 2252 vault_secret_group = next((x for x in vm.os_profile.secrets 2253 if x.source_vault and x.source_vault.id.lower() == keyvault.lower()), None) 2254 if vault_secret_group: 2255 vault_secret_group.vault_certificates.append(vault_cert) 2256 else: 2257 vault_secret_group = VaultSecretGroup(source_vault=SubResource(id=keyvault), vault_certificates=[vault_cert]) 2258 vm.os_profile.secrets.append(vault_secret_group) 2259 vm = set_vm(cmd, vm) 2260 return vm.os_profile.secrets 2261 2262 2263def list_vm_secrets(cmd, resource_group_name, vm_name): 2264 vm = get_vm(cmd, resource_group_name, vm_name) 2265 if vm.os_profile: 2266 return vm.os_profile.secrets 2267 return [] 2268 2269 2270def remove_vm_secret(cmd, resource_group_name, vm_name, keyvault, certificate=None): 2271 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 2272 2273 # support 2 kinds of filter: 2274 # a. if only keyvault is supplied, we delete its whole vault group. 2275 # b. if both keyvault and certificate are supplied, we only delete the specific cert entry. 2276 2277 to_keep = vm.os_profile.secrets 2278 keyvault_matched = [] 2279 if keyvault: 2280 keyvault = keyvault.lower() 2281 keyvault_matched = [x for x in to_keep if x.source_vault and x.source_vault.id.lower() == keyvault] 2282 2283 if keyvault and not certificate: 2284 to_keep = [x for x in to_keep if x not in keyvault_matched] 2285 elif certificate: 2286 temp = keyvault_matched if keyvault else to_keep 2287 cert_url_pattern = certificate.lower() 2288 if '://' not in cert_url_pattern: # just a cert name? 2289 cert_url_pattern = '/' + cert_url_pattern + '/' 2290 for x in temp: 2291 x.vault_certificates = ([v for v in x.vault_certificates 2292 if not(v.certificate_url and cert_url_pattern in v.certificate_url.lower())]) 2293 to_keep = [x for x in to_keep if x.vault_certificates] # purge all groups w/o any cert entries 2294 2295 vm.os_profile.secrets = to_keep 2296 vm = set_vm(cmd, vm) 2297 return vm.os_profile.secrets 2298# endregion 2299 2300 2301# region VirtualMachines UnmanagedDisks 2302def attach_unmanaged_data_disk(cmd, resource_group_name, vm_name, new=False, vhd_uri=None, lun=None, 2303 disk_name=None, size_gb=1023, caching=None): 2304 DataDisk, DiskCreateOptionTypes, VirtualHardDisk = cmd.get_models( 2305 'DataDisk', 'DiskCreateOptionTypes', 'VirtualHardDisk') 2306 if not new and not disk_name: 2307 raise CLIError('Please provide the name of the existing disk to attach') 2308 create_option = DiskCreateOptionTypes.empty if new else DiskCreateOptionTypes.attach 2309 2310 vm = get_vm_to_update(cmd, resource_group_name, vm_name) 2311 if disk_name is None: 2312 import datetime 2313 disk_name = vm_name + '-' + datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") 2314 # pylint: disable=no-member 2315 if vhd_uri is None: 2316 if not hasattr(vm.storage_profile.os_disk, 'vhd') or not vm.storage_profile.os_disk.vhd: 2317 raise CLIError('Adding unmanaged disks to a VM with managed disks is not supported') 2318 blob_uri = vm.storage_profile.os_disk.vhd.uri 2319 vhd_uri = blob_uri[0:blob_uri.rindex('/') + 1] + disk_name + '.vhd' 2320 2321 if lun is None: 2322 lun = _get_disk_lun(vm.storage_profile.data_disks) 2323 disk = DataDisk(lun=lun, vhd=VirtualHardDisk(uri=vhd_uri), name=disk_name, 2324 create_option=create_option, 2325 caching=caching, disk_size_gb=size_gb if new else None) 2326 if vm.storage_profile.data_disks is None: 2327 vm.storage_profile.data_disks = [] 2328 vm.storage_profile.data_disks.append(disk) 2329 return set_vm(cmd, vm) 2330 2331 2332def list_unmanaged_disks(cmd, resource_group_name, vm_name): 2333 vm = get_vm(cmd, resource_group_name, vm_name) 2334 return vm.storage_profile.data_disks # pylint: disable=no-member 2335# endregion 2336 2337 2338# region VirtualMachines Users 2339def _update_linux_access_extension(cmd, vm_instance, resource_group_name, protected_settings, 2340 no_wait=False): 2341 client = _compute_client_factory(cmd.cli_ctx) 2342 2343 VirtualMachineExtension = cmd.get_models('VirtualMachineExtension') 2344 2345 # pylint: disable=no-member 2346 instance_name = _get_extension_instance_name(vm_instance.instance_view, 2347 extension_mappings[_LINUX_ACCESS_EXT]['publisher'], 2348 _LINUX_ACCESS_EXT, 2349 _ACCESS_EXT_HANDLER_NAME) 2350 2351 publisher, version, auto_upgrade = _get_access_extension_upgrade_info( 2352 vm_instance.resources, _LINUX_ACCESS_EXT) 2353 2354 ext = VirtualMachineExtension(location=vm_instance.location, # pylint: disable=no-member 2355 publisher=publisher, 2356 type_properties_type=_LINUX_ACCESS_EXT, 2357 protected_settings=protected_settings, 2358 type_handler_version=version, 2359 settings={}, 2360 auto_upgrade_minor_version=auto_upgrade) 2361 return sdk_no_wait(no_wait, client.virtual_machine_extensions.begin_create_or_update, 2362 resource_group_name, vm_instance.name, instance_name, ext) 2363 2364 2365def _set_linux_user(cmd, vm_instance, resource_group_name, username, 2366 password=None, ssh_key_value=None, no_wait=False): 2367 protected_settings = {} 2368 protected_settings['username'] = username 2369 if password: 2370 protected_settings['password'] = password 2371 elif not ssh_key_value and not password: # default to ssh 2372 ssh_key_value = os.path.join(os.path.expanduser('~'), '.ssh', 'id_rsa.pub') 2373 2374 if ssh_key_value: 2375 protected_settings['ssh_key'] = read_content_if_is_file(ssh_key_value) 2376 2377 if no_wait: 2378 return _update_linux_access_extension(cmd, vm_instance, resource_group_name, 2379 protected_settings, no_wait) 2380 poller = _update_linux_access_extension(cmd, vm_instance, resource_group_name, 2381 protected_settings) 2382 return ExtensionUpdateLongRunningOperation(cmd.cli_ctx, 'setting user', 'done')(poller) 2383 2384 2385def _reset_windows_admin(cmd, vm_instance, resource_group_name, username, password, no_wait=False): 2386 '''Update the password. You can only change the password. Adding a new user is not supported. ''' 2387 client = _compute_client_factory(cmd.cli_ctx) 2388 VirtualMachineExtension = cmd.get_models('VirtualMachineExtension') 2389 2390 publisher, version, auto_upgrade = _get_access_extension_upgrade_info( 2391 vm_instance.resources, _WINDOWS_ACCESS_EXT) 2392 # pylint: disable=no-member 2393 instance_name = _get_extension_instance_name(vm_instance.instance_view, 2394 publisher, 2395 _WINDOWS_ACCESS_EXT, 2396 _ACCESS_EXT_HANDLER_NAME) 2397 2398 ext = VirtualMachineExtension(location=vm_instance.location, # pylint: disable=no-member 2399 publisher=publisher, 2400 type_properties_type=_WINDOWS_ACCESS_EXT, 2401 protected_settings={'Password': password}, 2402 type_handler_version=version, 2403 settings={'UserName': username}, 2404 auto_upgrade_minor_version=auto_upgrade) 2405 2406 if no_wait: 2407 return sdk_no_wait(no_wait, client.virtual_machine_extensions.create_or_update, 2408 resource_group_name, vm_instance.name, instance_name, ext) 2409 poller = client.virtual_machine_extensions.begin_create_or_update( 2410 resource_group_name, vm_instance.name, instance_name, ext) 2411 return ExtensionUpdateLongRunningOperation(cmd.cli_ctx, 'resetting admin', 'done')(poller) 2412 2413 2414def set_user(cmd, resource_group_name, vm_name, username, password=None, ssh_key_value=None, 2415 no_wait=False): 2416 vm = get_vm(cmd, resource_group_name, vm_name, 'instanceView') 2417 if _is_linux_os(vm): 2418 return _set_linux_user(cmd, vm, resource_group_name, username, password, ssh_key_value, no_wait) 2419 if ssh_key_value: 2420 raise CLIError('SSH key is not appliable on a Windows VM') 2421 return _reset_windows_admin(cmd, vm, resource_group_name, username, password, no_wait) 2422 2423 2424def delete_user(cmd, resource_group_name, vm_name, username, no_wait=False): 2425 vm = get_vm(cmd, resource_group_name, vm_name, 'instanceView') 2426 if not _is_linux_os(vm): 2427 raise CLIError('Deleting a user is not supported on Windows VM') 2428 if no_wait: 2429 return _update_linux_access_extension(cmd, vm, resource_group_name, 2430 {'remove_user': username}, no_wait) 2431 poller = _update_linux_access_extension(cmd, vm, resource_group_name, 2432 {'remove_user': username}) 2433 return ExtensionUpdateLongRunningOperation(cmd.cli_ctx, 'deleting user', 'done')(poller) 2434 2435 2436def reset_linux_ssh(cmd, resource_group_name, vm_name, no_wait=False): 2437 vm = get_vm(cmd, resource_group_name, vm_name, 'instanceView') 2438 if not _is_linux_os(vm): 2439 raise CLIError('Resetting SSH is not supported in Windows VM') 2440 if no_wait: 2441 return _update_linux_access_extension(cmd, vm, resource_group_name, 2442 {'reset_ssh': True}, no_wait) 2443 poller = _update_linux_access_extension(cmd, vm, resource_group_name, 2444 {'reset_ssh': True}) 2445 return ExtensionUpdateLongRunningOperation(cmd.cli_ctx, 'resetting SSH', 'done')(poller) 2446# endregion 2447 2448 2449# region VirtualMachineScaleSets 2450def assign_vmss_identity(cmd, resource_group_name, vmss_name, assign_identity=None, identity_role='Contributor', 2451 identity_role_id=None, identity_scope=None): 2452 VirtualMachineScaleSetIdentity, UpgradeMode, ResourceIdentityType, VirtualMachineScaleSetUpdate = cmd.get_models( 2453 'VirtualMachineScaleSetIdentity', 'UpgradeMode', 'ResourceIdentityType', 'VirtualMachineScaleSetUpdate') 2454 IdentityUserAssignedIdentitiesValue = cmd.get_models('VirtualMachineScaleSetIdentityUserAssignedIdentitiesValue') 2455 from azure.cli.core.commands.arm import assign_identity as assign_identity_helper 2456 client = _compute_client_factory(cmd.cli_ctx) 2457 _, _, external_identities, enable_local_identity = _build_identities_info(assign_identity) 2458 2459 def getter(): 2460 return client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 2461 2462 def setter(vmss, external_identities=external_identities): 2463 2464 if vmss.identity and vmss.identity.type == ResourceIdentityType.system_assigned_user_assigned: 2465 identity_types = ResourceIdentityType.system_assigned_user_assigned 2466 elif vmss.identity and vmss.identity.type == ResourceIdentityType.system_assigned and external_identities: 2467 identity_types = ResourceIdentityType.system_assigned_user_assigned 2468 elif vmss.identity and vmss.identity.type == ResourceIdentityType.user_assigned and enable_local_identity: 2469 identity_types = ResourceIdentityType.system_assigned_user_assigned 2470 elif external_identities and enable_local_identity: 2471 identity_types = ResourceIdentityType.system_assigned_user_assigned 2472 elif external_identities: 2473 identity_types = ResourceIdentityType.user_assigned 2474 else: 2475 identity_types = ResourceIdentityType.system_assigned 2476 vmss.identity = VirtualMachineScaleSetIdentity(type=identity_types) 2477 if external_identities: 2478 vmss.identity.user_assigned_identities = {} 2479 for identity in external_identities: 2480 vmss.identity.user_assigned_identities[identity] = IdentityUserAssignedIdentitiesValue() 2481 vmss_patch = VirtualMachineScaleSetUpdate() 2482 vmss_patch.identity = vmss.identity 2483 poller = client.virtual_machine_scale_sets.begin_update(resource_group_name, vmss_name, vmss_patch) 2484 return LongRunningOperation(cmd.cli_ctx)(poller) 2485 2486 assign_identity_helper(cmd.cli_ctx, getter, setter, identity_role=identity_role_id, identity_scope=identity_scope) 2487 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 2488 if vmss.upgrade_policy.mode == UpgradeMode.manual: 2489 logger.warning("With manual upgrade mode, you will need to run 'az vmss update-instances -g %s -n %s " 2490 "--instance-ids *' to propagate the change", resource_group_name, vmss_name) 2491 2492 return _construct_identity_info(identity_scope, identity_role, vmss.identity.principal_id, 2493 vmss.identity.user_assigned_identities) 2494 2495 2496# pylint: disable=too-many-locals, too-many-statements 2497def create_vmss(cmd, vmss_name, resource_group_name, image=None, 2498 disable_overprovision=None, instance_count=2, 2499 location=None, tags=None, upgrade_policy_mode='manual', validate=False, 2500 admin_username=None, admin_password=None, authentication_type=None, 2501 vm_sku=None, no_wait=False, 2502 ssh_dest_key_path=None, ssh_key_value=None, generate_ssh_keys=False, 2503 load_balancer=None, load_balancer_sku=None, application_gateway=None, 2504 app_gateway_subnet_address_prefix=None, 2505 app_gateway_sku='Standard_Large', app_gateway_capacity=10, 2506 backend_pool_name=None, nat_pool_name=None, backend_port=None, health_probe=None, 2507 public_ip_address=None, public_ip_address_allocation=None, 2508 public_ip_address_dns_name=None, accelerated_networking=None, 2509 public_ip_per_vm=False, vm_domain_name=None, dns_servers=None, nsg=None, 2510 os_caching=None, data_caching=None, 2511 storage_container_name='vhds', storage_sku=None, 2512 os_type=None, os_disk_name=None, 2513 use_unmanaged_disk=False, data_disk_sizes_gb=None, disk_info=None, 2514 vnet_name=None, vnet_address_prefix='10.0.0.0/16', 2515 subnet=None, subnet_address_prefix=None, 2516 os_offer=None, os_publisher=None, os_sku=None, os_version=None, 2517 load_balancer_type=None, app_gateway_type=None, vnet_type=None, 2518 public_ip_address_type=None, storage_profile=None, 2519 single_placement_group=None, custom_data=None, secrets=None, platform_fault_domain_count=None, 2520 plan_name=None, plan_product=None, plan_publisher=None, plan_promotion_code=None, license_type=None, 2521 assign_identity=None, identity_scope=None, identity_role='Contributor', 2522 identity_role_id=None, zones=None, priority=None, eviction_policy=None, 2523 application_security_groups=None, ultra_ssd_enabled=None, ephemeral_os_disk=None, 2524 proximity_placement_group=None, aux_subscriptions=None, terminate_notification_time=None, 2525 max_price=None, computer_name_prefix=None, orchestration_mode='Uniform', scale_in_policy=None, 2526 os_disk_encryption_set=None, data_disk_encryption_sets=None, data_disk_iops=None, data_disk_mbps=None, 2527 automatic_repairs_grace_period=None, specialized=None, os_disk_size_gb=None, encryption_at_host=None, 2528 host_group=None, max_batch_instance_percent=None, max_unhealthy_instance_percent=None, 2529 max_unhealthy_upgraded_instance_percent=None, pause_time_between_batches=None, 2530 enable_cross_zone_upgrade=None, prioritize_unhealthy_instances=None, edge_zone=None, 2531 user_data=None, network_api_version=None, enable_spot_restore=None, spot_restore_timeout=None, 2532 capacity_reservation_group=None): 2533 from azure.cli.core.commands.client_factory import get_subscription_id 2534 from azure.cli.core.util import random_string, hash_string 2535 from azure.cli.core.commands.arm import ArmTemplateBuilder 2536 from azure.cli.command_modules.vm._template_builder import (StorageProfile, build_vmss_resource, 2537 build_vnet_resource, build_public_ip_resource, 2538 build_load_balancer_resource, 2539 build_vmss_storage_account_pool_resource, 2540 build_application_gateway_resource, 2541 build_msi_role_assignment, build_nsg_resource) 2542 # Build up the ARM template 2543 master_template = ArmTemplateBuilder() 2544 2545 uniform_str = 'Uniform' 2546 flexible_str = 'Flexible' 2547 if orchestration_mode: 2548 from msrestazure.tools import resource_id, is_valid_resource_id 2549 2550 if disk_info: 2551 storage_sku = disk_info['os'].get('storageAccountType') 2552 2553 subscription_id = get_subscription_id(cmd.cli_ctx) 2554 2555 if os_disk_encryption_set is not None and not is_valid_resource_id(os_disk_encryption_set): 2556 os_disk_encryption_set = resource_id( 2557 subscription=subscription_id, resource_group=resource_group_name, 2558 namespace='Microsoft.Compute', type='diskEncryptionSets', name=os_disk_encryption_set) 2559 2560 if data_disk_encryption_sets is None: 2561 data_disk_encryption_sets = [] 2562 for i, des in enumerate(data_disk_encryption_sets): 2563 if des is not None and not is_valid_resource_id(des): 2564 data_disk_encryption_sets[i] = resource_id( 2565 subscription=subscription_id, resource_group=resource_group_name, 2566 namespace='Microsoft.Compute', type='diskEncryptionSets', name=des) 2567 2568 network_id_template = resource_id( 2569 subscription=subscription_id, resource_group=resource_group_name, 2570 namespace='Microsoft.Network') 2571 2572 vmss_id = resource_id( 2573 subscription=subscription_id, resource_group=resource_group_name, 2574 namespace='Microsoft.Compute', type='virtualMachineScaleSets', name=vmss_name) 2575 2576 scrubbed_name = vmss_name.replace('-', '').lower()[:5] 2577 naming_prefix = '{}{}'.format(scrubbed_name, 2578 hash_string(vmss_id, 2579 length=(9 - len(scrubbed_name)), 2580 force_lower=True)) 2581 2582 # determine final defaults and calculated values 2583 tags = tags or {} 2584 os_disk_name = os_disk_name or ('osdisk_{}'.format(hash_string(vmss_id, length=10)) 2585 if use_unmanaged_disk else None) 2586 load_balancer = load_balancer or '{}LB'.format(vmss_name) 2587 app_gateway = application_gateway or '{}AG'.format(vmss_name) 2588 backend_pool_name = backend_pool_name or '{}BEPool'.format(load_balancer or application_gateway) 2589 2590 vmss_dependencies = [] 2591 2592 # VNET will always be a dependency 2593 if vnet_type == 'new': 2594 vnet_name = vnet_name or '{}VNET'.format(vmss_name) 2595 subnet = subnet or '{}Subnet'.format(vmss_name) 2596 vmss_dependencies.append('Microsoft.Network/virtualNetworks/{}'.format(vnet_name)) 2597 vnet = build_vnet_resource( 2598 cmd, vnet_name, location, tags, vnet_address_prefix, subnet, subnet_address_prefix, edge_zone=edge_zone) 2599 if app_gateway_type: 2600 vnet['properties']['subnets'].append({ 2601 'name': 'appGwSubnet', 2602 'properties': { 2603 'addressPrefix': app_gateway_subnet_address_prefix 2604 } 2605 }) 2606 master_template.add_resource(vnet) 2607 if subnet: 2608 subnet_id = subnet if is_valid_resource_id(subnet) else \ 2609 '{}/virtualNetworks/{}/subnets/{}'.format(network_id_template, vnet_name, subnet) 2610 else: 2611 subnet_id = None 2612 2613 if vnet_name: 2614 gateway_subnet_id = ('{}/virtualNetworks/{}/subnets/appGwSubnet'.format(network_id_template, vnet_name) 2615 if app_gateway_type == 'new' else None) 2616 else: 2617 gateway_subnet_id = None 2618 2619 # public IP is used by either load balancer/application gateway 2620 public_ip_address_id = None 2621 if public_ip_address: 2622 public_ip_address_id = (public_ip_address if is_valid_resource_id(public_ip_address) 2623 else '{}/publicIPAddresses/{}'.format(network_id_template, 2624 public_ip_address)) 2625 2626 def _get_public_ip_address_allocation(value, sku): 2627 IPAllocationMethod = cmd.get_models('IPAllocationMethod', resource_type=ResourceType.MGMT_NETWORK) 2628 if not value: 2629 value = IPAllocationMethod.static.value if (sku and sku.lower() == 'standard') \ 2630 else IPAllocationMethod.dynamic.value 2631 return value 2632 2633 # Handle load balancer creation 2634 if load_balancer_type == 'new': 2635 vmss_dependencies.append('Microsoft.Network/loadBalancers/{}'.format(load_balancer)) 2636 2637 lb_dependencies = [] 2638 if vnet_type == 'new': 2639 lb_dependencies.append('Microsoft.Network/virtualNetworks/{}'.format(vnet_name)) 2640 if public_ip_address_type == 'new': 2641 public_ip_address = public_ip_address or '{}PublicIP'.format(load_balancer) 2642 lb_dependencies.append( 2643 'Microsoft.Network/publicIpAddresses/{}'.format(public_ip_address)) 2644 master_template.add_resource(build_public_ip_resource( 2645 cmd, public_ip_address, location, tags, 2646 _get_public_ip_address_allocation(public_ip_address_allocation, load_balancer_sku), 2647 public_ip_address_dns_name, load_balancer_sku, zones, edge_zone=edge_zone)) 2648 public_ip_address_id = '{}/publicIPAddresses/{}'.format(network_id_template, 2649 public_ip_address) 2650 2651 # calculate default names if not provided 2652 if orchestration_mode.lower() == flexible_str.lower(): 2653 # inbound nat pools are not supported on VMSS Flex 2654 nat_pool_name = None 2655 else: 2656 nat_pool_name = nat_pool_name or '{}NatPool'.format(load_balancer) 2657 2658 if not backend_port: 2659 backend_port = 3389 if os_type == 'windows' else 22 2660 2661 lb_resource = build_load_balancer_resource( 2662 cmd, load_balancer, location, tags, backend_pool_name, nat_pool_name, backend_port, 2663 'loadBalancerFrontEnd', public_ip_address_id, subnet_id, private_ip_address='', 2664 private_ip_allocation='Dynamic', sku=load_balancer_sku, instance_count=instance_count, 2665 disable_overprovision=disable_overprovision, edge_zone=edge_zone) 2666 lb_resource['dependsOn'] = lb_dependencies 2667 master_template.add_resource(lb_resource) 2668 2669 # Per https://docs.microsoft.com/azure/load-balancer/load-balancer-standard-overview#nsg 2670 if load_balancer_sku and load_balancer_sku.lower() == 'standard' and nsg is None and os_type: 2671 nsg_name = '{}NSG'.format(vmss_name) 2672 master_template.add_resource(build_nsg_resource( 2673 None, nsg_name, location, tags, 'rdp' if os_type.lower() == 'windows' else 'ssh')) 2674 nsg = "[resourceId('Microsoft.Network/networkSecurityGroups', '{}')]".format(nsg_name) 2675 vmss_dependencies.append('Microsoft.Network/networkSecurityGroups/{}'.format(nsg_name)) 2676 2677 # Or handle application gateway creation 2678 if app_gateway_type == 'new': 2679 vmss_dependencies.append('Microsoft.Network/applicationGateways/{}'.format(app_gateway)) 2680 2681 ag_dependencies = [] 2682 if vnet_type == 'new': 2683 ag_dependencies.append('Microsoft.Network/virtualNetworks/{}'.format(vnet_name)) 2684 if public_ip_address_type == 'new': 2685 public_ip_address = public_ip_address or '{}PublicIP'.format(app_gateway) 2686 ag_dependencies.append( 2687 'Microsoft.Network/publicIpAddresses/{}'.format(public_ip_address)) 2688 master_template.add_resource(build_public_ip_resource( 2689 cmd, public_ip_address, location, tags, 2690 _get_public_ip_address_allocation(public_ip_address_allocation, None), public_ip_address_dns_name, 2691 None, zones)) 2692 public_ip_address_id = '{}/publicIPAddresses/{}'.format(network_id_template, 2693 public_ip_address) 2694 2695 # calculate default names if not provided 2696 backend_port = backend_port or 80 2697 2698 ag_resource = build_application_gateway_resource( 2699 cmd, app_gateway, location, tags, backend_pool_name, backend_port, 'appGwFrontendIP', 2700 public_ip_address_id, subnet_id, gateway_subnet_id, private_ip_address='', 2701 private_ip_allocation='Dynamic', sku=app_gateway_sku, capacity=app_gateway_capacity) 2702 ag_resource['dependsOn'] = ag_dependencies 2703 master_template.add_variable( 2704 'appGwID', 2705 "[resourceId('Microsoft.Network/applicationGateways', '{}')]".format(app_gateway)) 2706 master_template.add_resource(ag_resource) 2707 2708 # create storage accounts if needed for unmanaged disk storage 2709 if storage_profile == StorageProfile.SAPirImage: 2710 master_template.add_resource(build_vmss_storage_account_pool_resource( 2711 cmd, 'storageLoop', location, tags, storage_sku, edge_zone)) 2712 master_template.add_variable('storageAccountNames', [ 2713 '{}{}'.format(naming_prefix, x) for x in range(5) 2714 ]) 2715 master_template.add_variable('vhdContainers', [ 2716 "[concat('https://', variables('storageAccountNames')[{}], '.blob.{}/{}')]".format( 2717 x, cmd.cli_ctx.cloud.suffixes.storage_endpoint, storage_container_name) for x in range(5) 2718 ]) 2719 vmss_dependencies.append('storageLoop') 2720 2721 backend_address_pool_id = None 2722 inbound_nat_pool_id = None 2723 if load_balancer_type or app_gateway_type: 2724 network_balancer = load_balancer if load_balancer_type else app_gateway 2725 balancer_type = 'loadBalancers' if load_balancer_type else 'applicationGateways' 2726 2727 if is_valid_resource_id(network_balancer): 2728 # backend address pool needed by load balancer or app gateway 2729 backend_address_pool_id = '{}/backendAddressPools/{}'.format(network_balancer, backend_pool_name) 2730 if nat_pool_name: 2731 inbound_nat_pool_id = '{}/inboundNatPools/{}'.format(network_balancer, nat_pool_name) 2732 else: 2733 # backend address pool needed by load balancer or app gateway 2734 backend_address_pool_id = '{}/{}/{}/backendAddressPools/{}'.format( 2735 network_id_template, balancer_type, network_balancer, backend_pool_name) 2736 if nat_pool_name: 2737 inbound_nat_pool_id = '{}/{}/{}/inboundNatPools/{}'.format( 2738 network_id_template, balancer_type, network_balancer, nat_pool_name) 2739 2740 if health_probe and not is_valid_resource_id(health_probe): 2741 health_probe = '{}/loadBalancers/{}/probes/{}'.format(network_id_template, load_balancer, health_probe) 2742 2743 ip_config_name = '{}IPConfig'.format(naming_prefix) 2744 nic_name = '{}Nic'.format(naming_prefix) 2745 2746 if custom_data: 2747 custom_data = read_content_if_is_file(custom_data) 2748 2749 if user_data: 2750 user_data = read_content_if_is_file(user_data) 2751 2752 if secrets: 2753 secrets = _merge_secrets([validate_file_or_dict(secret) for secret in secrets]) 2754 2755 if computer_name_prefix is not None and isinstance(computer_name_prefix, str): 2756 naming_prefix = computer_name_prefix 2757 2758 if orchestration_mode.lower() == uniform_str.lower(): 2759 computer_name_prefix = naming_prefix 2760 2761 if os_version and os_version != 'latest': 2762 logger.warning('You are deploying VMSS pinned to a specific image version from Azure Marketplace. ' 2763 'Consider using "latest" as the image version.') 2764 2765 vmss_resource = build_vmss_resource( 2766 cmd=cmd, name=vmss_name, computer_name_prefix=computer_name_prefix, location=location, tags=tags, 2767 overprovision=not disable_overprovision if orchestration_mode.lower() == uniform_str.lower() else None, 2768 upgrade_policy_mode=upgrade_policy_mode, vm_sku=vm_sku, 2769 instance_count=instance_count, ip_config_name=ip_config_name, nic_name=nic_name, subnet_id=subnet_id, 2770 public_ip_per_vm=public_ip_per_vm, vm_domain_name=vm_domain_name, dns_servers=dns_servers, nsg=nsg, 2771 accelerated_networking=accelerated_networking, admin_username=admin_username, 2772 authentication_type=authentication_type, storage_profile=storage_profile, os_disk_name=os_disk_name, 2773 disk_info=disk_info, os_type=os_type, image=image, admin_password=admin_password, 2774 ssh_key_values=ssh_key_value, ssh_key_path=ssh_dest_key_path, os_publisher=os_publisher, os_offer=os_offer, 2775 os_sku=os_sku, os_version=os_version, backend_address_pool_id=backend_address_pool_id, 2776 inbound_nat_pool_id=inbound_nat_pool_id, health_probe=health_probe, 2777 single_placement_group=single_placement_group, platform_fault_domain_count=platform_fault_domain_count, 2778 custom_data=custom_data, secrets=secrets, license_type=license_type, zones=zones, priority=priority, 2779 eviction_policy=eviction_policy, application_security_groups=application_security_groups, 2780 ultra_ssd_enabled=ultra_ssd_enabled, proximity_placement_group=proximity_placement_group, 2781 terminate_notification_time=terminate_notification_time, max_price=max_price, 2782 scale_in_policy=scale_in_policy, os_disk_encryption_set=os_disk_encryption_set, 2783 data_disk_encryption_sets=data_disk_encryption_sets, data_disk_iops=data_disk_iops, 2784 data_disk_mbps=data_disk_mbps, automatic_repairs_grace_period=automatic_repairs_grace_period, 2785 specialized=specialized, os_disk_size_gb=os_disk_size_gb, encryption_at_host=encryption_at_host, 2786 host_group=host_group, max_batch_instance_percent=max_batch_instance_percent, 2787 max_unhealthy_instance_percent=max_unhealthy_instance_percent, 2788 max_unhealthy_upgraded_instance_percent=max_unhealthy_upgraded_instance_percent, 2789 pause_time_between_batches=pause_time_between_batches, enable_cross_zone_upgrade=enable_cross_zone_upgrade, 2790 prioritize_unhealthy_instances=prioritize_unhealthy_instances, edge_zone=edge_zone, user_data=user_data, 2791 orchestration_mode=orchestration_mode, network_api_version=network_api_version, 2792 enable_spot_restore=enable_spot_restore, spot_restore_timeout=spot_restore_timeout, 2793 capacity_reservation_group=capacity_reservation_group) 2794 2795 vmss_resource['dependsOn'] = vmss_dependencies 2796 2797 if plan_name: 2798 vmss_resource['plan'] = { 2799 'name': plan_name, 2800 'publisher': plan_publisher, 2801 'product': plan_product, 2802 'promotionCode': plan_promotion_code 2803 } 2804 2805 enable_local_identity = None 2806 if assign_identity is not None: 2807 vmss_resource['identity'], _, _, enable_local_identity = _build_identities_info( 2808 assign_identity) 2809 if identity_scope: 2810 role_assignment_guid = str(_gen_guid()) 2811 master_template.add_resource(build_msi_role_assignment(vmss_name, vmss_id, identity_role_id, 2812 role_assignment_guid, identity_scope, False)) 2813 else: 2814 raise CLIError('usage error: --orchestration-mode (Uniform | Flexible)') 2815 2816 master_template.add_resource(vmss_resource) 2817 master_template.add_output('VMSS', vmss_name, 'Microsoft.Compute', 'virtualMachineScaleSets', 2818 output_type='object') 2819 2820 if admin_password: 2821 master_template.add_secure_parameter('adminPassword', admin_password) 2822 2823 template = master_template.build() 2824 parameters = master_template.build_parameters() 2825 2826 # deploy ARM template 2827 deployment_name = 'vmss_deploy_' + random_string(32) 2828 client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES, 2829 aux_subscriptions=aux_subscriptions).deployments 2830 2831 DeploymentProperties = cmd.get_models('DeploymentProperties', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) 2832 properties = DeploymentProperties(template=template, parameters=parameters, mode='incremental') 2833 2834 if validate: 2835 from azure.cli.command_modules.vm._vm_utils import log_pprint_template 2836 log_pprint_template(template) 2837 log_pprint_template(parameters) 2838 2839 Deployment = cmd.get_models('Deployment', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) 2840 deployment = Deployment(properties=properties) 2841 if validate: 2842 if cmd.supported_api_version(min_api='2019-10-01', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES): 2843 validation_poller = client.begin_validate(resource_group_name, deployment_name, deployment) 2844 return LongRunningOperation(cmd.cli_ctx)(validation_poller) 2845 2846 return client.validate(resource_group_name, deployment_name, deployment) 2847 2848 # creates the VMSS deployment 2849 deployment_result = DeploymentOutputLongRunningOperation(cmd.cli_ctx)( 2850 sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, deployment_name, deployment)) 2851 2852 if orchestration_mode.lower() == uniform_str.lower() and assign_identity is not None: 2853 vmss_info = get_vmss(cmd, resource_group_name, vmss_name) 2854 if enable_local_identity and not identity_scope: 2855 _show_missing_access_warning(resource_group_name, vmss_name, 'vmss') 2856 deployment_result['vmss']['identity'] = _construct_identity_info(identity_scope, identity_role, 2857 vmss_info.identity.principal_id, 2858 vmss_info.identity.user_assigned_identities) 2859 return deployment_result 2860 2861 2862def _build_identities_info(identities): 2863 from ._vm_utils import MSI_LOCAL_ID 2864 identities = identities or [] 2865 identity_types = [] 2866 if not identities or MSI_LOCAL_ID in identities: 2867 identity_types.append('SystemAssigned') 2868 external_identities = [x for x in identities if x != MSI_LOCAL_ID] 2869 if external_identities: 2870 identity_types.append('UserAssigned') 2871 identity_types = ','.join(identity_types) 2872 info = {'type': identity_types} 2873 if external_identities: 2874 info['userAssignedIdentities'] = {e: {} for e in external_identities} 2875 return (info, identity_types, external_identities, 'SystemAssigned' in identity_types) 2876 2877 2878def deallocate_vmss(cmd, resource_group_name, vm_scale_set_name, instance_ids=None, no_wait=False): 2879 client = _compute_client_factory(cmd.cli_ctx) 2880 if instance_ids and len(instance_ids) == 1: 2881 return sdk_no_wait(no_wait, client.virtual_machine_scale_set_vms.begin_deallocate, 2882 resource_group_name, vm_scale_set_name, instance_ids[0]) 2883 2884 VirtualMachineScaleSetVMInstanceIDs = cmd.get_models('VirtualMachineScaleSetVMInstanceIDs') 2885 vm_instance_i_ds = VirtualMachineScaleSetVMInstanceIDs(instance_ids=instance_ids) 2886 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_deallocate, 2887 resource_group_name, vm_scale_set_name, vm_instance_i_ds) 2888 2889 2890def delete_vmss_instances(cmd, resource_group_name, vm_scale_set_name, instance_ids, no_wait=False): 2891 client = _compute_client_factory(cmd.cli_ctx) 2892 VirtualMachineScaleSetVMInstanceRequiredIDs = cmd.get_models('VirtualMachineScaleSetVMInstanceRequiredIDs') 2893 instance_ids = VirtualMachineScaleSetVMInstanceRequiredIDs(instance_ids=instance_ids) 2894 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_delete_instances, 2895 resource_group_name, vm_scale_set_name, instance_ids) 2896 2897 2898def get_vmss(cmd, resource_group_name, name, instance_id=None, include_user_data=False): 2899 client = _compute_client_factory(cmd.cli_ctx) 2900 2901 expand = None 2902 if include_user_data: 2903 expand = 'userData' 2904 2905 if instance_id is not None: 2906 if cmd.supported_api_version(min_api='2020-12-01', operation_group='virtual_machine_scale_sets'): 2907 return client.virtual_machine_scale_set_vms.get(resource_group_name, name, instance_id, expand) 2908 return client.virtual_machine_scale_set_vms.get(resource_group_name, name, instance_id) 2909 2910 if cmd.supported_api_version(min_api='2021-03-01', operation_group='virtual_machine_scale_sets'): 2911 return client.virtual_machine_scale_sets.get(resource_group_name, name, expand) 2912 return client.virtual_machine_scale_sets.get(resource_group_name, name) 2913 2914 2915def get_vmss_modified(cmd, resource_group_name, name, instance_id=None): 2916 client = _compute_client_factory(cmd.cli_ctx) 2917 if instance_id is not None: 2918 vms = client.virtual_machine_scale_set_vms.get(resource_group_name, name, instance_id) 2919 # To avoid unnecessary permission check of image 2920 vms.storage_profile.image_reference = None 2921 return vms 2922 vmss = client.virtual_machine_scale_sets.get(resource_group_name, name) 2923 # To avoid unnecessary permission check of image 2924 vmss.virtual_machine_profile.storage_profile.image_reference = None 2925 return vmss 2926 2927 2928def get_vmss_instance_view(cmd, resource_group_name, vm_scale_set_name, instance_id=None): 2929 client = _compute_client_factory(cmd.cli_ctx) 2930 if instance_id: 2931 if instance_id == '*': 2932 2933 return [x.instance_view for x in (client.virtual_machine_scale_set_vms.list( 2934 resource_group_name, vm_scale_set_name, select='instanceView', expand='instanceView'))] 2935 2936 return client.virtual_machine_scale_set_vms.get_instance_view(resource_group_name, vm_scale_set_name, 2937 instance_id) 2938 2939 return client.virtual_machine_scale_sets.get_instance_view(resource_group_name, vm_scale_set_name) 2940 2941 2942def list_vmss(cmd, resource_group_name=None): 2943 client = _compute_client_factory(cmd.cli_ctx) 2944 if resource_group_name: 2945 return client.virtual_machine_scale_sets.list(resource_group_name) 2946 return client.virtual_machine_scale_sets.list_all() 2947 2948 2949def list_vmss_instance_connection_info(cmd, resource_group_name, vm_scale_set_name): 2950 from msrestazure.tools import parse_resource_id 2951 client = _compute_client_factory(cmd.cli_ctx) 2952 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vm_scale_set_name) 2953 # find the load balancer 2954 nic_configs = vmss.virtual_machine_profile.network_profile.network_interface_configurations 2955 primary_nic_config = next((n for n in nic_configs if n.primary), None) 2956 if primary_nic_config is None: 2957 raise CLIError('could not find a primary NIC which is needed to search to load balancer') 2958 ip_configs = primary_nic_config.ip_configurations 2959 ip_config = next((ip for ip in ip_configs if ip.load_balancer_inbound_nat_pools), None) 2960 if not ip_config: 2961 raise CLIError('No load balancer exists to retrieve public IP address') 2962 res_id = ip_config.load_balancer_inbound_nat_pools[0].id 2963 lb_info = parse_resource_id(res_id) 2964 lb_name = lb_info['name'] 2965 lb_rg = lb_info['resource_group'] 2966 2967 # get public ip 2968 network_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_NETWORK) 2969 lb = network_client.load_balancers.get(lb_rg, lb_name) 2970 if getattr(lb.frontend_ip_configurations[0], 'public_ip_address', None): 2971 res_id = lb.frontend_ip_configurations[0].public_ip_address.id 2972 public_ip_info = parse_resource_id(res_id) 2973 public_ip_name = public_ip_info['name'] 2974 public_ip_rg = public_ip_info['resource_group'] 2975 public_ip = network_client.public_ip_addresses.get(public_ip_rg, public_ip_name) 2976 public_ip_address = public_ip.ip_address 2977 2978 # loop around inboundnatrule 2979 instance_addresses = {} 2980 for rule in lb.inbound_nat_rules: 2981 instance_id = parse_resource_id(rule.backend_ip_configuration.id)['child_name_1'] 2982 instance_addresses['instance ' + instance_id] = '{}:{}'.format(public_ip_address, 2983 rule.frontend_port) 2984 2985 return instance_addresses 2986 raise CLIError('The VM scale-set uses an internal load balancer, hence no connection information') 2987 2988 2989def list_vmss_instance_public_ips(cmd, resource_group_name, vm_scale_set_name): 2990 result = cf_public_ip_addresses(cmd.cli_ctx).list_virtual_machine_scale_set_public_ip_addresses( 2991 resource_group_name, vm_scale_set_name) 2992 # filter away over-provisioned instances which are deleted after 'create/update' returns 2993 return [r for r in result if r.ip_address] 2994 2995 2996def reimage_vmss(cmd, resource_group_name, vm_scale_set_name, instance_id=None, no_wait=False): 2997 client = _compute_client_factory(cmd.cli_ctx) 2998 if instance_id: 2999 return sdk_no_wait(no_wait, client.virtual_machine_scale_set_vms.begin_reimage, 3000 resource_group_name, vm_scale_set_name, instance_id) 3001 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_reimage, resource_group_name, vm_scale_set_name) 3002 3003 3004def restart_vmss(cmd, resource_group_name, vm_scale_set_name, instance_ids=None, no_wait=False): 3005 client = _compute_client_factory(cmd.cli_ctx) 3006 VirtualMachineScaleSetVMInstanceRequiredIDs = cmd.get_models('VirtualMachineScaleSetVMInstanceRequiredIDs') 3007 if instance_ids is None: 3008 instance_ids = ['*'] 3009 instance_ids = VirtualMachineScaleSetVMInstanceRequiredIDs(instance_ids=instance_ids) 3010 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_restart, resource_group_name, vm_scale_set_name, 3011 vm_instance_i_ds=instance_ids) 3012 3013 3014# pylint: disable=inconsistent-return-statements 3015def scale_vmss(cmd, resource_group_name, vm_scale_set_name, new_capacity, no_wait=False): 3016 VirtualMachineScaleSet = cmd.get_models('VirtualMachineScaleSet') 3017 client = _compute_client_factory(cmd.cli_ctx) 3018 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vm_scale_set_name) 3019 # pylint: disable=no-member 3020 if vmss.sku.capacity == new_capacity: 3021 return 3022 3023 vmss.sku.capacity = new_capacity 3024 vmss_new = VirtualMachineScaleSet(location=vmss.location, sku=vmss.sku) 3025 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_create_or_update, 3026 resource_group_name, vm_scale_set_name, vmss_new) 3027 3028 3029def start_vmss(cmd, resource_group_name, vm_scale_set_name, instance_ids=None, no_wait=False): 3030 client = _compute_client_factory(cmd.cli_ctx) 3031 VirtualMachineScaleSetVMInstanceRequiredIDs = cmd.get_models('VirtualMachineScaleSetVMInstanceRequiredIDs') 3032 if instance_ids is None: 3033 instance_ids = ['*'] 3034 instance_ids = VirtualMachineScaleSetVMInstanceRequiredIDs(instance_ids=instance_ids) 3035 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_start, 3036 resource_group_name, vm_scale_set_name, vm_instance_i_ds=instance_ids) 3037 3038 3039def stop_vmss(cmd, resource_group_name, vm_scale_set_name, instance_ids=None, no_wait=False, skip_shutdown=False): 3040 client = _compute_client_factory(cmd.cli_ctx) 3041 VirtualMachineScaleSetVMInstanceRequiredIDs = cmd.get_models('VirtualMachineScaleSetVMInstanceRequiredIDs') 3042 if instance_ids is None: 3043 instance_ids = ['*'] 3044 instance_ids = VirtualMachineScaleSetVMInstanceRequiredIDs(instance_ids=instance_ids) 3045 if cmd.supported_api_version(min_api='2020-06-01', operation_group='virtual_machine_scale_sets'): 3046 return sdk_no_wait( 3047 no_wait, client.virtual_machine_scale_sets.begin_power_off, resource_group_name, vm_scale_set_name, 3048 vm_instance_i_ds=instance_ids, skip_shutdown=skip_shutdown) 3049 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_power_off, resource_group_name, 3050 vm_scale_set_name, vm_instance_i_ds=instance_ids) 3051 3052 3053def update_vmss_instances(cmd, resource_group_name, vm_scale_set_name, instance_ids, no_wait=False): 3054 client = _compute_client_factory(cmd.cli_ctx) 3055 VirtualMachineScaleSetVMInstanceRequiredIDs = cmd.get_models('VirtualMachineScaleSetVMInstanceRequiredIDs') 3056 instance_ids = VirtualMachineScaleSetVMInstanceRequiredIDs(instance_ids=instance_ids) 3057 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_update_instances, 3058 resource_group_name, vm_scale_set_name, instance_ids) 3059 3060 3061def update_vmss(cmd, resource_group_name, name, license_type=None, no_wait=False, instance_id=None, 3062 protect_from_scale_in=None, protect_from_scale_set_actions=None, 3063 enable_terminate_notification=None, terminate_notification_time=None, ultra_ssd_enabled=None, 3064 scale_in_policy=None, priority=None, max_price=None, proximity_placement_group=None, 3065 enable_automatic_repairs=None, automatic_repairs_grace_period=None, max_batch_instance_percent=None, 3066 max_unhealthy_instance_percent=None, max_unhealthy_upgraded_instance_percent=None, 3067 pause_time_between_batches=None, enable_cross_zone_upgrade=None, prioritize_unhealthy_instances=None, 3068 user_data=None, enable_spot_restore=None, spot_restore_timeout=None, capacity_reservation_group=None, 3069 **kwargs): 3070 vmss = kwargs['parameters'] 3071 aux_subscriptions = None 3072 # pylint: disable=too-many-boolean-expressions 3073 if vmss and hasattr(vmss, 'virtual_machine_profile') and vmss.virtual_machine_profile and \ 3074 vmss.virtual_machine_profile.storage_profile and \ 3075 vmss.virtual_machine_profile.storage_profile.image_reference and \ 3076 'id' in vmss.virtual_machine_profile.storage_profile.image_reference: 3077 aux_subscriptions = _parse_aux_subscriptions(vmss.virtual_machine_profile.storage_profile.image_reference['id']) 3078 client = _compute_client_factory(cmd.cli_ctx, aux_subscriptions=aux_subscriptions) 3079 3080 VMProtectionPolicy = cmd.get_models('VirtualMachineScaleSetVMProtectionPolicy') 3081 3082 # handle vmss instance update 3083 from azure.cli.core.util import b64encode 3084 3085 if instance_id is not None: 3086 if license_type is not None: 3087 vmss.license_type = license_type 3088 3089 if user_data is not None: 3090 vmss.user_data = b64encode(user_data) 3091 3092 if not vmss.protection_policy: 3093 vmss.protection_policy = VMProtectionPolicy() 3094 3095 if protect_from_scale_in is not None: 3096 vmss.protection_policy.protect_from_scale_in = protect_from_scale_in 3097 3098 if protect_from_scale_set_actions is not None: 3099 vmss.protection_policy.protect_from_scale_set_actions = protect_from_scale_set_actions 3100 3101 return sdk_no_wait(no_wait, client.virtual_machine_scale_set_vms.begin_update, 3102 resource_group_name, name, instance_id, **kwargs) 3103 3104 # else handle vmss update 3105 if license_type is not None: 3106 vmss.virtual_machine_profile.license_type = license_type 3107 3108 if user_data is not None: 3109 vmss.virtual_machine_profile.user_data = b64encode(user_data) 3110 3111 if capacity_reservation_group is not None: 3112 CapacityReservationProfile = cmd.get_models('CapacityReservationProfile') 3113 SubResource = cmd.get_models('SubResource') 3114 if capacity_reservation_group == 'None': 3115 capacity_reservation_group = None 3116 sub_resource = SubResource(id=capacity_reservation_group) 3117 capacity_reservation = CapacityReservationProfile(capacity_reservation_group=sub_resource) 3118 vmss.virtual_machine_profile.capacity_reservation = capacity_reservation 3119 3120 if enable_terminate_notification is not None or terminate_notification_time is not None: 3121 if vmss.virtual_machine_profile.scheduled_events_profile is None: 3122 ScheduledEventsProfile = cmd.get_models('ScheduledEventsProfile') 3123 vmss.virtual_machine_profile.scheduled_events_profile = ScheduledEventsProfile() 3124 TerminateNotificationProfile = cmd.get_models('TerminateNotificationProfile') 3125 vmss.virtual_machine_profile.scheduled_events_profile.terminate_notification_profile =\ 3126 TerminateNotificationProfile(not_before_timeout=terminate_notification_time, 3127 enable=enable_terminate_notification) 3128 3129 if enable_automatic_repairs is not None or automatic_repairs_grace_period is not None: 3130 AutomaticRepairsPolicy = cmd.get_models('AutomaticRepairsPolicy') 3131 vmss.automatic_repairs_policy = \ 3132 AutomaticRepairsPolicy(enabled="true", grace_period=automatic_repairs_grace_period) 3133 3134 if ultra_ssd_enabled is not None: 3135 if cmd.supported_api_version(min_api='2019-03-01', operation_group='virtual_machine_scale_sets'): 3136 if vmss.additional_capabilities is None: 3137 AdditionalCapabilities = cmd.get_models('AdditionalCapabilities') 3138 vmss.additional_capabilities = AdditionalCapabilities(ultra_ssd_enabled=ultra_ssd_enabled) 3139 else: 3140 vmss.additional_capabilities.ultra_ssd_enabled = ultra_ssd_enabled 3141 else: 3142 if vmss.virtual_machine_profile.additional_capabilities is None: 3143 AdditionalCapabilities = cmd.get_models('AdditionalCapabilities') 3144 vmss.virtual_machine_profile.additional_capabilities = AdditionalCapabilities( 3145 ultra_ssd_enabled=ultra_ssd_enabled) 3146 else: 3147 vmss.virtual_machine_profile.additional_capabilities.ultra_ssd_enabled = ultra_ssd_enabled 3148 3149 if scale_in_policy is not None: 3150 ScaleInPolicy = cmd.get_models('ScaleInPolicy') 3151 vmss.scale_in_policy = ScaleInPolicy(rules=scale_in_policy) 3152 3153 if enable_spot_restore is not None: 3154 vmss.spot_restore_policy.enabled = enable_spot_restore 3155 3156 if spot_restore_timeout is not None: 3157 vmss.spot_restore_policy.restore_timeout = spot_restore_timeout 3158 3159 if priority is not None: 3160 vmss.virtual_machine_profile.priority = priority 3161 3162 if max_price is not None: 3163 if vmss.virtual_machine_profile.billing_profile is None: 3164 BillingProfile = cmd.get_models('BillingProfile') 3165 vmss.virtual_machine_profile.billing_profile = BillingProfile(max_price=max_price) 3166 else: 3167 vmss.virtual_machine_profile.billing_profile.max_price = max_price 3168 3169 if proximity_placement_group is not None: 3170 vmss.proximity_placement_group = {'id': proximity_placement_group} 3171 3172 if max_batch_instance_percent is not None or max_unhealthy_instance_percent is not None \ 3173 or max_unhealthy_upgraded_instance_percent is not None or pause_time_between_batches is not None \ 3174 or enable_cross_zone_upgrade is not None or prioritize_unhealthy_instances is not None: 3175 if vmss.upgrade_policy is None: 3176 vmss.upgrade_policy = {'rolling_upgrade_policy': None} 3177 if vmss.upgrade_policy.rolling_upgrade_policy is None: 3178 vmss.upgrade_policy.rolling_upgrade_policy = { 3179 'maxBatchInstancePercent': max_batch_instance_percent, 3180 'maxUnhealthyInstancePercent': max_unhealthy_instance_percent, 3181 'maxUnhealthyUpgradedInstancePercent': max_unhealthy_upgraded_instance_percent, 3182 'pauseTimeBetweenBatches': pause_time_between_batches, 3183 'enableCrossZoneUpgrade': enable_cross_zone_upgrade, 3184 'prioritizeUnhealthyInstances': prioritize_unhealthy_instances 3185 } 3186 else: 3187 vmss.upgrade_policy.rolling_upgrade_policy.max_batch_instance_percent = max_batch_instance_percent 3188 vmss.upgrade_policy.rolling_upgrade_policy.max_unhealthy_instance_percent = max_unhealthy_instance_percent 3189 vmss.upgrade_policy.rolling_upgrade_policy.max_unhealthy_upgraded_instance_percent = \ 3190 max_unhealthy_upgraded_instance_percent 3191 vmss.upgrade_policy.rolling_upgrade_policy.pause_time_between_batches = pause_time_between_batches 3192 vmss.upgrade_policy.rolling_upgrade_policy.enable_cross_zone_upgrade = enable_cross_zone_upgrade 3193 vmss.upgrade_policy.rolling_upgrade_policy.prioritize_unhealthy_instances = prioritize_unhealthy_instances 3194 3195 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_create_or_update, 3196 resource_group_name, name, **kwargs) 3197 3198# endregion 3199 3200 3201# region VirtualMachineScaleSets Diagnostics 3202def set_vmss_diagnostics_extension( 3203 cmd, resource_group_name, vmss_name, settings, protected_settings=None, version=None, 3204 no_auto_upgrade=False): 3205 client = _compute_client_factory(cmd.cli_ctx) 3206 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 3207 # pylint: disable=no-member 3208 is_linux_os = _is_linux_os(vmss.virtual_machine_profile) 3209 vm_extension_name = _LINUX_DIAG_EXT if is_linux_os else _WINDOWS_DIAG_EXT 3210 if is_linux_os and vmss.virtual_machine_profile.extension_profile: # check incompatibles 3211 exts = vmss.virtual_machine_profile.extension_profile.extensions or [] 3212 major_ver = extension_mappings[_LINUX_DIAG_EXT]['version'].split('.')[0] 3213 # For VMSS, we don't do auto-removal like VM because there is no reliable API to wait for 3214 # the removal done before we can install the newer one 3215 if next((e for e in exts if e.name == _LINUX_DIAG_EXT and 3216 not e.type_handler_version.startswith(major_ver + '.')), None): 3217 delete_cmd = 'az vmss extension delete -g {} --vmss-name {} -n {}'.format( 3218 resource_group_name, vmss_name, vm_extension_name) 3219 raise CLIError("There is an incompatible version of diagnostics extension installed. " 3220 "Please remove it by running '{}', and retry. 'az vmss update-instances'" 3221 " might be needed if with manual upgrade policy".format(delete_cmd)) 3222 3223 poller = set_vmss_extension(cmd, resource_group_name, vmss_name, vm_extension_name, 3224 extension_mappings[vm_extension_name]['publisher'], 3225 version or extension_mappings[vm_extension_name]['version'], 3226 settings, 3227 protected_settings, 3228 no_auto_upgrade) 3229 3230 result = LongRunningOperation(cmd.cli_ctx)(poller) 3231 UpgradeMode = cmd.get_models('UpgradeMode') 3232 if vmss.upgrade_policy.mode == UpgradeMode.manual: 3233 poller2 = update_vmss_instances(cmd, resource_group_name, vmss_name, ['*']) 3234 LongRunningOperation(cmd.cli_ctx)(poller2) 3235 return result 3236# endregion 3237 3238 3239# region VirtualMachineScaleSets Disks (Managed) 3240def attach_managed_data_disk_to_vmss(cmd, resource_group_name, vmss_name, size_gb=None, instance_id=None, lun=None, 3241 caching=None, disk=None, sku=None): 3242 3243 def _init_data_disk(storage_profile, lun, existing_disk=None): 3244 data_disks = storage_profile.data_disks or [] 3245 if lun is None: 3246 lun = _get_disk_lun(data_disks) 3247 if existing_disk is None: 3248 data_disk = DataDisk(lun=lun, create_option=DiskCreateOptionTypes.empty, disk_size_gb=size_gb, 3249 caching=caching, managed_disk=ManagedDiskParameters(storage_account_type=sku)) 3250 else: 3251 data_disk = DataDisk(lun=lun, create_option=DiskCreateOptionTypes.attach, caching=caching, 3252 managed_disk=ManagedDiskParameters(id=existing_disk, storage_account_type=sku)) 3253 3254 data_disks.append(data_disk) 3255 storage_profile.data_disks = data_disks 3256 3257 DiskCreateOptionTypes, ManagedDiskParameters = cmd.get_models( 3258 'DiskCreateOptionTypes', 'ManagedDiskParameters') 3259 if disk is None: 3260 DataDisk = cmd.get_models('VirtualMachineScaleSetDataDisk') 3261 else: 3262 DataDisk = cmd.get_models('DataDisk') 3263 3264 client = _compute_client_factory(cmd.cli_ctx) 3265 if instance_id is None: 3266 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 3267 # Avoid unnecessary permission error 3268 vmss.virtual_machine_profile.storage_profile.image_reference = None 3269 # pylint: disable=no-member 3270 _init_data_disk(vmss.virtual_machine_profile.storage_profile, lun) 3271 return client.virtual_machine_scale_sets.begin_create_or_update(resource_group_name, vmss_name, vmss) 3272 3273 vmss_vm = client.virtual_machine_scale_set_vms.get(resource_group_name, vmss_name, instance_id) 3274 # Avoid unnecessary permission error 3275 vmss_vm.storage_profile.image_reference = None 3276 _init_data_disk(vmss_vm.storage_profile, lun, disk) 3277 return client.virtual_machine_scale_set_vms.begin_update(resource_group_name, vmss_name, instance_id, vmss_vm) 3278 3279 3280def detach_disk_from_vmss(cmd, resource_group_name, vmss_name, lun, instance_id=None): 3281 client = _compute_client_factory(cmd.cli_ctx) 3282 if instance_id is None: 3283 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 3284 # Avoid unnecessary permission error 3285 vmss.virtual_machine_profile.storage_profile.image_reference = None 3286 # pylint: disable=no-member 3287 data_disks = vmss.virtual_machine_profile.storage_profile.data_disks 3288 else: 3289 vmss_vm = client.virtual_machine_scale_set_vms.get(resource_group_name, vmss_name, instance_id) 3290 # Avoid unnecessary permission error 3291 vmss_vm.storage_profile.image_reference = None 3292 data_disks = vmss_vm.storage_profile.data_disks 3293 3294 if not data_disks: 3295 raise CLIError("Data disk doesn't exist") 3296 3297 leftovers = [d for d in data_disks if d.lun != lun] 3298 if len(data_disks) == len(leftovers): 3299 raise CLIError("Could not find the data disk with lun '{}'".format(lun)) 3300 3301 if instance_id is None: 3302 vmss.virtual_machine_profile.storage_profile.data_disks = leftovers 3303 return client.virtual_machine_scale_sets.begin_create_or_update(resource_group_name, vmss_name, vmss) 3304 vmss_vm.storage_profile.data_disks = leftovers 3305 return client.virtual_machine_scale_set_vms.begin_update(resource_group_name, vmss_name, instance_id, vmss_vm) 3306# endregion 3307 3308 3309# region VirtualMachineScaleSets Extensions 3310def delete_vmss_extension(cmd, resource_group_name, vmss_name, extension_name): 3311 client = _compute_client_factory(cmd.cli_ctx) 3312 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 3313 # Avoid unnecessary permission error 3314 vmss.virtual_machine_profile.storage_profile.image_reference = None 3315 # pylint: disable=no-member 3316 if not vmss.virtual_machine_profile.extension_profile: 3317 raise CLIError('Scale set has no extensions to delete') 3318 3319 keep_list = [e for e in vmss.virtual_machine_profile.extension_profile.extensions 3320 if e.name != extension_name] 3321 if len(keep_list) == len(vmss.virtual_machine_profile.extension_profile.extensions): 3322 raise CLIError('Extension {} not found'.format(extension_name)) 3323 3324 vmss.virtual_machine_profile.extension_profile.extensions = keep_list 3325 3326 return client.virtual_machine_scale_sets.begin_create_or_update(resource_group_name, vmss_name, vmss) 3327 3328 3329# pylint: disable=inconsistent-return-statements 3330def get_vmss_extension(cmd, resource_group_name, vmss_name, extension_name): 3331 client = _compute_client_factory(cmd.cli_ctx) 3332 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 3333 # pylint: disable=no-member 3334 if not vmss.virtual_machine_profile.extension_profile: 3335 return 3336 return next((e for e in vmss.virtual_machine_profile.extension_profile.extensions 3337 if e.name == extension_name), None) 3338 3339 3340def list_vmss_extensions(cmd, resource_group_name, vmss_name): 3341 client = _compute_client_factory(cmd.cli_ctx) 3342 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 3343 # pylint: disable=no-member 3344 if vmss.virtual_machine_profile and vmss.virtual_machine_profile.extension_profile: 3345 return vmss.virtual_machine_profile.extension_profile.extensions 3346 return None 3347 3348 3349def set_vmss_extension(cmd, resource_group_name, vmss_name, extension_name, publisher, version=None, 3350 settings=None, protected_settings=None, no_auto_upgrade=False, force_update=False, 3351 no_wait=False, extension_instance_name=None, provision_after_extensions=None, 3352 enable_auto_upgrade=None): 3353 if not extension_instance_name: 3354 extension_instance_name = extension_name 3355 3356 client = _compute_client_factory(cmd.cli_ctx) 3357 vmss = client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 3358 # Avoid unnecessary permission error 3359 vmss.virtual_machine_profile.storage_profile.image_reference = None 3360 VirtualMachineScaleSetExtension, VirtualMachineScaleSetExtensionProfile = cmd.get_models( 3361 'VirtualMachineScaleSetExtension', 'VirtualMachineScaleSetExtensionProfile') 3362 3363 # pylint: disable=no-member 3364 version = _normalize_extension_version(cmd.cli_ctx, publisher, extension_name, version, vmss.location) 3365 extension_profile = vmss.virtual_machine_profile.extension_profile 3366 if extension_profile: 3367 extensions = extension_profile.extensions 3368 if extensions: 3369 extension_profile.extensions = [x for x in extensions if 3370 x.type_properties_type.lower() != extension_name.lower() or x.publisher.lower() != publisher.lower()] # pylint: disable=line-too-long 3371 3372 if cmd.supported_api_version(min_api='2019-07-01', operation_group='virtual_machine_scale_sets'): 3373 ext = VirtualMachineScaleSetExtension(name=extension_instance_name, 3374 publisher=publisher, 3375 type_properties_type=extension_name, 3376 protected_settings=protected_settings, 3377 type_handler_version=version, 3378 settings=settings, 3379 auto_upgrade_minor_version=(not no_auto_upgrade), 3380 provision_after_extensions=provision_after_extensions, 3381 enable_automatic_upgrade=enable_auto_upgrade) 3382 else: 3383 ext = VirtualMachineScaleSetExtension(name=extension_instance_name, 3384 publisher=publisher, 3385 type=extension_name, 3386 protected_settings=protected_settings, 3387 type_handler_version=version, 3388 settings=settings, 3389 auto_upgrade_minor_version=(not no_auto_upgrade), 3390 provision_after_extensions=provision_after_extensions, 3391 enable_automatic_upgrade=enable_auto_upgrade) 3392 3393 if force_update: 3394 ext.force_update_tag = str(_gen_guid()) 3395 3396 if not vmss.virtual_machine_profile.extension_profile: 3397 vmss.virtual_machine_profile.extension_profile = VirtualMachineScaleSetExtensionProfile(extensions=[]) 3398 vmss.virtual_machine_profile.extension_profile.extensions.append(ext) 3399 3400 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_create_or_update, 3401 resource_group_name, vmss_name, vmss) 3402 3403 3404def set_orchestration_service_state(cmd, resource_group_name, vm_scale_set_name, service_name, action, no_wait=False): 3405 # currently service_name has only one available value "AutomaticRepairs". And SDK does not accept service_name, 3406 # instead SDK assign it to "AutomaticRepairs" in its own logic. As there may be more service name to be supported, 3407 # we define service_name as a required parameter here to avoid introducing a breaking change in the future. 3408 client = _compute_client_factory(cmd.cli_ctx) 3409 OrchestrationServiceStateInput = cmd.get_models('OrchestrationServiceStateInput') 3410 state_input = OrchestrationServiceStateInput(service_name=service_name, action=action) 3411 return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_set_orchestration_service_state, 3412 resource_group_name, vm_scale_set_name, state_input) 3413 3414 3415def upgrade_vmss_extension(cmd, resource_group_name, vm_scale_set_name, no_wait=False): 3416 client = _compute_client_factory(cmd.cli_ctx) 3417 return sdk_no_wait(no_wait, client.virtual_machine_scale_set_rolling_upgrades.begin_start_extension_upgrade, 3418 resource_group_name, vm_scale_set_name) 3419# endregion 3420 3421 3422# region VirtualMachineScaleSets RunCommand 3423def vmss_run_command_invoke(cmd, resource_group_name, vmss_name, command_id, instance_id, scripts=None, parameters=None): # pylint: disable=line-too-long 3424 return run_command_invoke(cmd, resource_group_name, vmss_name, command_id, scripts, parameters, instance_id) 3425# endregion 3426 3427 3428# region VirtualMachineScaleSets Identity 3429def remove_vmss_identity(cmd, resource_group_name, vmss_name, identities=None): 3430 client = _compute_client_factory(cmd.cli_ctx) 3431 3432 def _get_vmss(_, resource_group_name, vmss_name): 3433 return client.virtual_machine_scale_sets.get(resource_group_name, vmss_name) 3434 3435 def _set_vmss(resource_group_name, name, vmss_instance): 3436 VirtualMachineScaleSetUpdate = cmd.get_models('VirtualMachineScaleSetUpdate', 3437 operation_group='virtual_machine_scale_sets') 3438 vmss_update = VirtualMachineScaleSetUpdate(identity=vmss_instance.identity) 3439 return client.virtual_machine_scale_sets.begin_update(resource_group_name, vmss_name, vmss_update) 3440 3441 if identities is None: 3442 from ._vm_utils import MSI_LOCAL_ID 3443 identities = [MSI_LOCAL_ID] 3444 3445 return _remove_identities(cmd, resource_group_name, vmss_name, identities, 3446 _get_vmss, 3447 _set_vmss) 3448# endregion 3449 3450 3451# region image galleries 3452def list_image_galleries(cmd, resource_group_name=None): 3453 client = _compute_client_factory(cmd.cli_ctx) 3454 if resource_group_name: 3455 return client.galleries.list_by_resource_group(resource_group_name) 3456 return client.galleries.list() 3457 3458 3459# from azure.mgmt.compute.models import Gallery, SharingProfile 3460def update_image_galleries(cmd, resource_group_name, gallery_name, gallery, permissions=None, 3461 soft_delete=None, **kwargs): 3462 if permissions: 3463 if gallery.sharing_profile is None: 3464 SharingProfile = cmd.get_models('SharingProfile', operation_group='shared_galleries') 3465 gallery.sharing_profile = SharingProfile(permissions=permissions) 3466 else: 3467 gallery.sharing_profile.permissions = permissions 3468 if soft_delete is not None: 3469 gallery.soft_delete_policy.is_soft_delete_enabled = soft_delete 3470 3471 client = _compute_client_factory(cmd.cli_ctx) 3472 3473 return client.galleries.begin_create_or_update(resource_group_name, gallery_name, gallery, **kwargs) 3474 3475 3476def create_image_gallery(cmd, resource_group_name, gallery_name, description=None, 3477 location=None, no_wait=False, tags=None, permissions=None, soft_delete=None): 3478 Gallery = cmd.get_models('Gallery') 3479 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 3480 gallery = Gallery(description=description, location=location, tags=(tags or {})) 3481 if soft_delete is not None: 3482 gallery.soft_delete_policy = {'is_soft_delete_enabled': soft_delete} 3483 client = _compute_client_factory(cmd.cli_ctx) 3484 if permissions: 3485 SharingProfile = cmd.get_models('SharingProfile', operation_group='shared_galleries') 3486 gallery.sharing_profile = SharingProfile(permissions=permissions) 3487 3488 return sdk_no_wait(no_wait, client.galleries.begin_create_or_update, resource_group_name, gallery_name, gallery) 3489 3490 3491def create_gallery_image(cmd, resource_group_name, gallery_name, gallery_image_name, os_type, publisher, offer, sku, 3492 os_state='Generalized', end_of_life_date=None, privacy_statement_uri=None, 3493 release_note_uri=None, eula=None, description=None, location=None, 3494 minimum_cpu_core=None, maximum_cpu_core=None, minimum_memory=None, maximum_memory=None, 3495 disallowed_disk_types=None, plan_name=None, plan_publisher=None, plan_product=None, tags=None, 3496 hyper_v_generation='V1', features=None): 3497 # pylint: disable=line-too-long 3498 GalleryImage, GalleryImageIdentifier, RecommendedMachineConfiguration, ResourceRange, Disallowed, ImagePurchasePlan, GalleryImageFeature = cmd.get_models( 3499 'GalleryImage', 'GalleryImageIdentifier', 'RecommendedMachineConfiguration', 'ResourceRange', 'Disallowed', 'ImagePurchasePlan', 'GalleryImageFeature') 3500 client = _compute_client_factory(cmd.cli_ctx) 3501 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 3502 3503 end_of_life_date = fix_gallery_image_date_info(end_of_life_date) 3504 recommendation = None 3505 if any([minimum_cpu_core, maximum_cpu_core, minimum_memory, maximum_memory]): 3506 cpu_recommendation, memory_recommendation = None, None 3507 if any([minimum_cpu_core, maximum_cpu_core]): 3508 cpu_recommendation = ResourceRange(min=minimum_cpu_core, max=maximum_cpu_core) 3509 if any([minimum_memory, maximum_memory]): 3510 memory_recommendation = ResourceRange(min=minimum_memory, max=maximum_memory) 3511 recommendation = RecommendedMachineConfiguration(v_cp_us=cpu_recommendation, memory=memory_recommendation) 3512 purchase_plan = None 3513 if any([plan_name, plan_publisher, plan_product]): 3514 purchase_plan = ImagePurchasePlan(name=plan_name, publisher=plan_publisher, product=plan_product) 3515 3516 feature_list = None 3517 if features: 3518 feature_list = [] 3519 for item in features.split(): 3520 try: 3521 key, value = item.split('=', 1) 3522 feature_list.append(GalleryImageFeature(name=key, value=value)) 3523 except ValueError: 3524 raise CLIError('usage error: --features KEY=VALUE [KEY=VALUE ...]') 3525 3526 image = GalleryImage(identifier=GalleryImageIdentifier(publisher=publisher, offer=offer, sku=sku), 3527 os_type=os_type, os_state=os_state, end_of_life_date=end_of_life_date, 3528 recommended=recommendation, disallowed=Disallowed(disk_types=disallowed_disk_types), 3529 purchase_plan=purchase_plan, location=location, eula=eula, tags=(tags or {}), 3530 hyper_v_generation=hyper_v_generation, features=feature_list) 3531 return client.gallery_images.begin_create_or_update(resource_group_name, gallery_name, gallery_image_name, image) 3532 3533 3534def _add_aux_subscription(aux_subscriptions, resource_id): 3535 if resource_id: 3536 aux_subs = _parse_aux_subscriptions(resource_id) 3537 if aux_subs and aux_subs[0] not in aux_subscriptions: 3538 aux_subscriptions.extend(aux_subs) 3539 3540 3541def _get_image_version_aux_subscription(managed_image, os_snapshot, data_snapshots): 3542 aux_subscriptions = [] 3543 _add_aux_subscription(aux_subscriptions, managed_image) 3544 _add_aux_subscription(aux_subscriptions, os_snapshot) 3545 if data_snapshots: 3546 for data_snapshot in data_snapshots: 3547 _add_aux_subscription(aux_subscriptions, data_snapshot) 3548 return aux_subscriptions if aux_subscriptions else None 3549 3550 3551def create_image_version(cmd, resource_group_name, gallery_name, gallery_image_name, gallery_image_version, 3552 location=None, target_regions=None, storage_account_type=None, 3553 end_of_life_date=None, exclude_from_latest=None, replica_count=None, tags=None, 3554 os_snapshot=None, data_snapshots=None, managed_image=None, data_snapshot_luns=None, 3555 target_region_encryption=None, os_vhd_uri=None, os_vhd_storage_account=None, 3556 data_vhds_uris=None, data_vhds_luns=None, data_vhds_storage_accounts=None, 3557 replication_mode=None): 3558 # print(target_regions) 3559 from msrestazure.tools import resource_id, is_valid_resource_id 3560 from azure.cli.core.commands.client_factory import get_subscription_id 3561 3562 ImageVersionPublishingProfile, GalleryArtifactSource, ManagedArtifact, ImageVersion, TargetRegion = cmd.get_models( 3563 'GalleryImageVersionPublishingProfile', 'GalleryArtifactSource', 'ManagedArtifact', 'GalleryImageVersion', 3564 'TargetRegion') 3565 aux_subscriptions = _get_image_version_aux_subscription(managed_image, os_snapshot, data_snapshots) 3566 client = _compute_client_factory(cmd.cli_ctx, aux_subscriptions=aux_subscriptions) 3567 3568 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 3569 end_of_life_date = fix_gallery_image_date_info(end_of_life_date) 3570 if managed_image and not is_valid_resource_id(managed_image): 3571 managed_image = resource_id(subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 3572 namespace='Microsoft.Compute', type='images', name=managed_image) 3573 if os_snapshot and not is_valid_resource_id(os_snapshot): 3574 os_snapshot = resource_id(subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 3575 namespace='Microsoft.Compute', type='snapshots', name=os_snapshot) 3576 if data_snapshots: 3577 for i, s in enumerate(data_snapshots): 3578 if not is_valid_resource_id(data_snapshots[i]): 3579 data_snapshots[i] = resource_id( 3580 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 3581 namespace='Microsoft.Compute', type='snapshots', name=s) 3582 source = GalleryArtifactSource(managed_image=ManagedArtifact(id=managed_image)) 3583 profile = ImageVersionPublishingProfile(exclude_from_latest=exclude_from_latest, 3584 end_of_life_date=end_of_life_date, 3585 target_regions=target_regions or [TargetRegion(name=location)], 3586 source=source, replica_count=replica_count, 3587 storage_account_type=storage_account_type) 3588 if replication_mode is not None: 3589 profile.replication_mode = replication_mode 3590 if cmd.supported_api_version(min_api='2019-07-01', operation_group='gallery_image_versions'): 3591 if managed_image is None and os_snapshot is None and os_vhd_uri is None: 3592 raise CLIError('usage error: Please provide --managed-image or --os-snapshot or --vhd') 3593 GalleryImageVersionStorageProfile = cmd.get_models('GalleryImageVersionStorageProfile') 3594 GalleryArtifactVersionSource = cmd.get_models('GalleryArtifactVersionSource') 3595 GalleryOSDiskImage = cmd.get_models('GalleryOSDiskImage') 3596 GalleryDataDiskImage = cmd.get_models('GalleryDataDiskImage') 3597 source = os_disk_image = data_disk_images = None 3598 if managed_image is not None: 3599 source = GalleryArtifactVersionSource(id=managed_image) 3600 if os_snapshot is not None: 3601 os_disk_image = GalleryOSDiskImage(source=GalleryArtifactVersionSource(id=os_snapshot)) 3602 if data_snapshot_luns and not data_snapshots: 3603 raise CLIError('usage error: --data-snapshot-luns must be used together with --data-snapshots') 3604 if data_snapshots: 3605 if data_snapshot_luns and len(data_snapshots) != len(data_snapshot_luns): 3606 raise CLIError('usage error: Length of --data-snapshots and --data-snapshot-luns should be equal.') 3607 if not data_snapshot_luns: 3608 data_snapshot_luns = list(range(len(data_snapshots))) 3609 data_disk_images = [] 3610 for i, s in enumerate(data_snapshots): 3611 data_disk_images.append(GalleryDataDiskImage(source=GalleryArtifactVersionSource(id=s), 3612 lun=data_snapshot_luns[i])) 3613 # from vhd, only support os image now 3614 if cmd.supported_api_version(min_api='2020-09-30', operation_group='gallery_image_versions'): 3615 # OS disk 3616 if os_vhd_uri and os_vhd_storage_account is None or os_vhd_uri is None and os_vhd_storage_account: 3617 raise ValidationError('--os-vhd-uri and --os-vhd-storage-account should be used together.') 3618 if os_vhd_uri and os_vhd_storage_account: 3619 if not is_valid_resource_id(os_vhd_storage_account): 3620 os_vhd_storage_account = resource_id( 3621 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 3622 namespace='Microsoft.Storage', type='storageAccounts', name=os_vhd_storage_account) 3623 os_disk_image = GalleryOSDiskImage(source=GalleryArtifactVersionSource( 3624 id=os_vhd_storage_account, uri=os_vhd_uri)) 3625 3626 # Data disks 3627 if data_vhds_uris and data_vhds_storage_accounts is None or \ 3628 data_vhds_uris is None and data_vhds_storage_accounts: 3629 raise ValidationError('--data-vhds-uris and --data-vhds-storage-accounts should be used together.') 3630 if data_vhds_luns and data_vhds_uris is None: 3631 raise ValidationError('--data-vhds-luns must be used together with --data-vhds-uris') 3632 if data_vhds_uris: 3633 # Generate LUNs 3634 if data_vhds_luns is None: 3635 # 0, 1, 2, ... 3636 data_vhds_luns = list(range(len(data_vhds_uris))) 3637 # Check length 3638 len_data_vhds_uris = len(data_vhds_uris) 3639 len_data_vhds_luns = len(data_vhds_luns) 3640 len_data_vhds_storage_accounts = len(data_vhds_storage_accounts) 3641 if len_data_vhds_uris != len_data_vhds_luns or len_data_vhds_uris != len_data_vhds_storage_accounts: 3642 raise ValidationError('Length of --data-vhds-uris, --data-vhds-luns, --data-vhds-storage-accounts ' 3643 'must be same.') 3644 # Generate full storage account ID 3645 for i, storage_account in enumerate(data_vhds_storage_accounts): 3646 if not is_valid_resource_id(storage_account): 3647 data_vhds_storage_accounts[i] = resource_id( 3648 subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 3649 namespace='Microsoft.Storage', type='storageAccounts', name=storage_account) 3650 if data_disk_images is None: 3651 data_disk_images = [] 3652 for uri, lun, account in zip(data_vhds_uris, data_vhds_luns, data_vhds_storage_accounts): 3653 data_disk_images.append(GalleryDataDiskImage( 3654 source=GalleryArtifactVersionSource(id=account, uri=uri), lun=lun)) 3655 3656 storage_profile = GalleryImageVersionStorageProfile(source=source, os_disk_image=os_disk_image, 3657 data_disk_images=data_disk_images) 3658 image_version = ImageVersion(publishing_profile=profile, location=location, tags=(tags or {}), 3659 storage_profile=storage_profile) 3660 else: 3661 if managed_image is None: 3662 raise CLIError('usage error: Please provide --managed-image') 3663 image_version = ImageVersion(publishing_profile=profile, location=location, tags=(tags or {})) 3664 3665 return client.gallery_image_versions.begin_create_or_update( 3666 resource_group_name=resource_group_name, 3667 gallery_name=gallery_name, 3668 gallery_image_name=gallery_image_name, 3669 gallery_image_version_name=gallery_image_version, 3670 gallery_image_version=image_version 3671 ) 3672 3673 3674def fix_gallery_image_date_info(date_info): 3675 # here we add needed time, if only date is provided, so the setting can be accepted by servie end 3676 if date_info and 't' not in date_info.lower(): 3677 date_info += 'T12:59:59Z' 3678 return date_info 3679 3680 3681# pylint: disable=line-too-long 3682def get_image_version_to_update(cmd, resource_group_name, gallery_name, gallery_image_name, gallery_image_version_name): 3683 client = _compute_client_factory(cmd.cli_ctx) 3684 version = client.gallery_image_versions.get(resource_group_name, gallery_name, gallery_image_name, gallery_image_version_name) 3685 # To avoid unnecessary permission check of image 3686 version.storage_profile.source = None 3687 if version.storage_profile.os_disk_image and version.storage_profile.os_disk_image.source: 3688 version.storage_profile.os_disk_image.source = None 3689 if version.storage_profile.data_disk_images: 3690 for i in range(len(version.storage_profile.data_disk_images)): 3691 if version.storage_profile.data_disk_images[i].source: 3692 version.storage_profile.data_disk_images[i].source = None 3693 return version 3694 3695 3696def update_image_version(cmd, resource_group_name, gallery_name, gallery_image_name, gallery_image_version_name, 3697 target_regions=None, replica_count=None, no_wait=False, **kwargs): 3698 image_version = kwargs['gallery_image_version'] 3699 3700 if target_regions: 3701 image_version.publishing_profile.target_regions = target_regions 3702 if replica_count: 3703 image_version.publishing_profile.replica_count = replica_count 3704 if image_version.storage_profile.source is not None: 3705 image_version.storage_profile.os_disk_image = image_version.storage_profile.data_disk_images = None 3706 3707 client = _compute_client_factory(cmd.cli_ctx) 3708 3709 return sdk_no_wait(no_wait, client.gallery_image_versions.begin_create_or_update, resource_group_name, gallery_name, 3710 gallery_image_name, gallery_image_version_name, **kwargs) 3711# endregion 3712 3713 3714# region proximity placement groups 3715def create_proximity_placement_group(cmd, client, proximity_placement_group_name, resource_group_name, 3716 ppg_type=None, location=None, tags=None): 3717 from knack.arguments import CaseInsensitiveList 3718 3719 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 3720 3721 ProximityPlacementGroup, PPGType = cmd.get_models('ProximityPlacementGroup', 'ProximityPlacementGroupType') 3722 choices = CaseInsensitiveList([x.value for x in PPGType]) 3723 3724 if ppg_type and ppg_type not in choices: 3725 logger.info("Valid choices: %s", str(choices)) 3726 raise CLIError("Usage error: invalid value for --type/-t") 3727 3728 ppg_params = ProximityPlacementGroup(name=proximity_placement_group_name, proximity_placement_group_type=ppg_type, 3729 location=location, tags=(tags or {})) 3730 3731 return client.create_or_update(resource_group_name=resource_group_name, 3732 proximity_placement_group_name=proximity_placement_group_name, parameters=ppg_params) 3733 3734 3735def list_proximity_placement_groups(client, resource_group_name=None): 3736 if resource_group_name: 3737 return client.list_by_resource_group(resource_group_name=resource_group_name) 3738 return client.list_by_subscription() 3739# endregion 3740 3741 3742# region dedicated host 3743def create_dedicated_host_group(cmd, client, host_group_name, resource_group_name, platform_fault_domain_count, 3744 automatic_placement=None, location=None, zones=None, tags=None): 3745 DedicatedHostGroup = cmd.get_models('DedicatedHostGroup') 3746 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 3747 3748 host_group_params = DedicatedHostGroup(location=location, platform_fault_domain_count=platform_fault_domain_count, 3749 support_automatic_placement=automatic_placement, zones=zones, tags=tags) 3750 3751 return client.create_or_update(resource_group_name, host_group_name, parameters=host_group_params) 3752 3753 3754def list_dedicated_host_groups(cmd, client, resource_group_name=None): 3755 if resource_group_name: 3756 return client.list_by_resource_group(resource_group_name) 3757 return client.list_by_subscription() 3758 3759 3760def get_dedicated_host_group_instance_view(client, host_group_name, resource_group_name): 3761 return client.get(resource_group_name, host_group_name, expand="instanceView") 3762 3763 3764def create_dedicated_host(cmd, client, host_group_name, host_name, resource_group_name, sku, platform_fault_domain=None, 3765 auto_replace_on_failure=None, license_type=None, location=None, tags=None): 3766 DedicatedHostType = cmd.get_models('DedicatedHost') 3767 SkuType = cmd.get_models('Sku') 3768 3769 location = location or _get_resource_group_location(cmd.cli_ctx, resource_group_name) 3770 sku = SkuType(name=sku) 3771 3772 host_params = DedicatedHostType(location=location, platform_fault_domain=platform_fault_domain, 3773 auto_replace_on_failure=auto_replace_on_failure, license_type=license_type, 3774 sku=sku, tags=tags) 3775 3776 return client.begin_create_or_update(resource_group_name, host_group_name, host_name, parameters=host_params) 3777 3778 3779def get_dedicated_host_instance_view(client, host_group_name, host_name, resource_group_name): 3780 return client.get(resource_group_name, host_group_name, host_name, expand="instanceView") 3781 3782# endregion 3783 3784 3785# region VMMonitor 3786def _get_log_analytics_client(cmd): 3787 from ._client_factory import cf_log_analytics 3788 from azure.cli.core.commands.client_factory import get_subscription_id 3789 subscription_id = get_subscription_id(cmd.cli_ctx) 3790 return cf_log_analytics(cmd.cli_ctx, subscription_id) 3791 3792 3793def _prepare_workspace(cmd, resource_group_name, workspace): 3794 from msrestazure.tools import is_valid_resource_id 3795 3796 from azure.core.exceptions import HttpResponseError 3797 3798 workspace_id = None 3799 if not is_valid_resource_id(workspace): 3800 workspace_name = workspace 3801 log_client = _get_log_analytics_client(cmd) 3802 workspace_result = None 3803 try: 3804 workspace_result = log_client.workspaces.get(resource_group_name, workspace_name) 3805 except HttpResponseError: 3806 from azure.mgmt.loganalytics.models import Workspace, WorkspaceSku, WorkspaceSkuNameEnum 3807 sku = WorkspaceSku(name=WorkspaceSkuNameEnum.per_gb2018.value) 3808 retention_time = 30 # default value 3809 location = _get_resource_group_location(cmd.cli_ctx, resource_group_name) 3810 workspace_instance = Workspace(location=location, 3811 sku=sku, 3812 retention_in_days=retention_time) 3813 workspace_result = LongRunningOperation(cmd.cli_ctx)(log_client.workspaces.begin_create_or_update( 3814 resource_group_name, 3815 workspace_name, 3816 workspace_instance)) 3817 workspace_id = workspace_result.id 3818 else: 3819 workspace_id = workspace 3820 return workspace_id 3821 3822 3823def _set_data_source_for_workspace(cmd, os_type, resource_group_name, workspace_name): 3824 from ._client_factory import cf_log_analytics_data_sources 3825 from azure.cli.core.commands.client_factory import get_subscription_id 3826 from azure.mgmt.loganalytics.models import DataSource 3827 from azure.core.exceptions import HttpResponseError 3828 3829 subscription_id = get_subscription_id(cmd.cli_ctx) 3830 data_sources_client = cf_log_analytics_data_sources(cmd.cli_ctx, subscription_id) 3831 data_source_name_template = "DataSource_{}_{}" 3832 3833 default_data_sources = None 3834 if os_type.lower() == 'linux': 3835 from ._workspace_data_source_settings import default_linux_data_sources 3836 default_data_sources = default_linux_data_sources 3837 elif os_type.lower() == 'windows': 3838 from ._workspace_data_source_settings import default_windows_data_sources 3839 default_data_sources = default_windows_data_sources 3840 3841 if default_data_sources is not None: 3842 for data_source_kind, data_source_settings in default_data_sources.items(): 3843 for data_source_setting in data_source_settings: 3844 data_source = DataSource(kind=data_source_kind, 3845 properties=data_source_setting) 3846 data_source_name = data_source_name_template.format(data_source_kind, _gen_guid()) 3847 try: 3848 data_sources_client.create_or_update(resource_group_name, 3849 workspace_name, 3850 data_source_name, 3851 data_source) 3852 except HttpResponseError as ex: 3853 logger.warning("Failed to set data source due to %s. " 3854 "Skip this step and need manual work later.", ex.message) 3855 else: 3856 logger.warning("Unsupported OS type. Skip the default settings for log analytics workspace.") 3857 3858 3859def execute_query_for_vm(cmd, client, resource_group_name, vm_name, analytics_query, timespan=None): 3860 """Executes a query against the Log Analytics workspace linked with a vm.""" 3861 from azure.loganalytics.models import QueryBody 3862 vm = get_vm(cmd, resource_group_name, vm_name) 3863 workspace = None 3864 extension_resources = vm.resources or [] 3865 for resource in extension_resources: 3866 if resource.name == "MicrosoftMonitoringAgent" or resource.name == "OmsAgentForLinux": 3867 workspace = resource.settings.get('workspaceId', None) 3868 if workspace is None: 3869 raise CLIError('Cannot find the corresponding log analytics workspace. ' 3870 'Please check the status of log analytics workpsace.') 3871 return client.query(workspace, QueryBody(query=analytics_query, timespan=timespan)) 3872 3873 3874def _set_log_analytics_workspace_extension(cmd, resource_group_name, vm, vm_name, workspace_name): 3875 is_linux_os = _is_linux_os(vm) 3876 vm_extension_name = _LINUX_OMS_AGENT_EXT if is_linux_os else _WINDOWS_OMS_AGENT_EXT 3877 log_client = _get_log_analytics_client(cmd) 3878 customer_id = log_client.workspaces.get(resource_group_name, workspace_name).customer_id 3879 settings = { 3880 'workspaceId': customer_id, 3881 'stopOnMultipleConnections': 'true' 3882 } 3883 primary_shared_key = log_client.shared_keys.get_shared_keys(resource_group_name, workspace_name).primary_shared_key 3884 protected_settings = { 3885 'workspaceKey': primary_shared_key, 3886 } 3887 return set_extension(cmd, resource_group_name, vm_name, vm_extension_name, 3888 extension_mappings[vm_extension_name]['publisher'], 3889 extension_mappings[vm_extension_name]['version'], 3890 settings, 3891 protected_settings) 3892# endregion 3893 3894 3895# disk encryption set 3896def create_disk_encryption_set( 3897 cmd, client, resource_group_name, disk_encryption_set_name, key_url, source_vault, encryption_type=None, 3898 location=None, tags=None, no_wait=False, enable_auto_key_rotation=None): 3899 from msrestazure.tools import resource_id, is_valid_resource_id 3900 from azure.cli.core.commands.client_factory import get_subscription_id 3901 DiskEncryptionSet, EncryptionSetIdentity, KeyVaultAndKeyReference, SourceVault = cmd.get_models( 3902 'DiskEncryptionSet', 'EncryptionSetIdentity', 'KeyVaultAndKeyReference', 'SourceVault') 3903 encryption_set_identity = EncryptionSetIdentity(type='SystemAssigned') 3904 if not is_valid_resource_id(source_vault): 3905 source_vault = resource_id(subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 3906 namespace='Microsoft.KeyVault', type='vaults', name=source_vault) 3907 source_vault = SourceVault(id=source_vault) 3908 key_vault_and_key_reference = KeyVaultAndKeyReference(source_vault=source_vault, key_url=key_url) 3909 disk_encryption_set = DiskEncryptionSet(location=location, tags=tags, identity=encryption_set_identity, 3910 active_key=key_vault_and_key_reference, encryption_type=encryption_type, 3911 rotation_to_latest_key_version_enabled=enable_auto_key_rotation) 3912 return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, disk_encryption_set_name, 3913 disk_encryption_set) 3914 3915 3916def list_disk_encryption_sets(cmd, client, resource_group_name=None): 3917 if resource_group_name: 3918 return client.list_by_resource_group(resource_group_name) 3919 return client.list() 3920 3921 3922def update_disk_encryption_set(cmd, instance, client, resource_group_name, key_url=None, source_vault=None, 3923 enable_auto_key_rotation=None): 3924 from msrestazure.tools import resource_id, is_valid_resource_id 3925 from azure.cli.core.commands.client_factory import get_subscription_id 3926 if not is_valid_resource_id(source_vault): 3927 source_vault = resource_id(subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name, 3928 namespace='Microsoft.KeyVault', type='vaults', name=source_vault) 3929 if key_url: 3930 instance.active_key.key_url = key_url 3931 if source_vault: 3932 instance.active_key.source_vault.id = source_vault 3933 if enable_auto_key_rotation is not None: 3934 instance.rotation_to_latest_key_version_enabled = enable_auto_key_rotation 3935 return instance 3936 3937# endregion 3938 3939 3940# region Disk Access 3941def create_disk_access(cmd, client, resource_group_name, disk_access_name, location=None, tags=None, no_wait=False): 3942 DiskAccess = cmd.get_models('DiskAccess') 3943 disk_access = DiskAccess(location=location, tags=tags) 3944 return sdk_no_wait(no_wait, client.begin_create_or_update, 3945 resource_group_name, disk_access_name, disk_access) 3946 3947 3948def list_disk_accesses(cmd, client, resource_group_name=None): 3949 if resource_group_name: 3950 return client.list_by_resource_group(resource_group_name) 3951 return client.list() 3952 3953 3954def set_disk_access(cmd, client, parameters, resource_group_name, disk_access_name, tags=None, no_wait=False): 3955 location = _get_resource_group_location(cmd.cli_ctx, resource_group_name) 3956 DiskAccess = cmd.get_models('DiskAccess') 3957 disk_access = DiskAccess(location=location, tags=tags) 3958 return sdk_no_wait(no_wait, client.begin_create_or_update, 3959 resource_group_name, disk_access_name, disk_access) 3960 3961# endregion 3962 3963 3964# region install patches 3965def install_vm_patches(cmd, client, resource_group_name, vm_name, maximum_duration, reboot_setting, classifications_to_include_win=None, classifications_to_include_linux=None, kb_numbers_to_include=None, kb_numbers_to_exclude=None, 3966 exclude_kbs_requiring_reboot=None, package_name_masks_to_include=None, package_name_masks_to_exclude=None, no_wait=False): 3967 VMInstallPatchesParameters, WindowsParameters, LinuxParameters = cmd.get_models('VirtualMachineInstallPatchesParameters', 'WindowsParameters', 'LinuxParameters') 3968 windows_parameters = WindowsParameters(classifications_to_include=classifications_to_include_win, kb_numbers_to_inclunde=kb_numbers_to_include, kb_numbers_to_exclude=kb_numbers_to_exclude, exclude_kbs_requirig_reboot=exclude_kbs_requiring_reboot) 3969 linux_parameters = LinuxParameters(classifications_to_include=classifications_to_include_linux, package_name_masks_to_include=package_name_masks_to_include, package_name_masks_to_exclude=package_name_masks_to_exclude) 3970 install_patches_input = VMInstallPatchesParameters(maximum_duration=maximum_duration, reboot_setting=reboot_setting, linux_parameters=linux_parameters, windows_parameters=windows_parameters) 3971 3972 return sdk_no_wait(no_wait, client.begin_install_patches, resource_group_name=resource_group_name, vm_name=vm_name, install_patches_input=install_patches_input) 3973 3974# endregion 3975 3976 3977def sig_shared_gallery_list(client, location, shared_to=None): 3978 # Keep it here as it will add subscription in the future and we need to set it to None to make it work 3979 if shared_to == 'subscription': 3980 shared_to = None 3981 return client.list(location=location, 3982 shared_to=shared_to) 3983 3984 3985def sig_share_update(cmd, client, resource_group_name, gallery_name, subscription_ids=None, tenant_ids=None, 3986 op_type=None): 3987 SharingProfileGroup, SharingUpdate, SharingProfileGroupTypes = cmd.get_models( 3988 'SharingProfileGroup', 'SharingUpdate', 'SharingProfileGroupTypes', operation_group='shared_galleries') 3989 if subscription_ids is None and tenant_ids is None: 3990 raise RequiredArgumentMissingError('At least one of subscription ids or tenant ids must be provided') 3991 groups = [] 3992 if subscription_ids: 3993 groups.append(SharingProfileGroup(type=SharingProfileGroupTypes.SUBSCRIPTIONS, ids=subscription_ids)) 3994 if tenant_ids: 3995 groups.append(SharingProfileGroup(type=SharingProfileGroupTypes.AAD_TENANTS, ids=tenant_ids)) 3996 sharing_update = SharingUpdate(operation_type=op_type, groups=groups) 3997 return client.begin_update(resource_group_name=resource_group_name, 3998 gallery_name=gallery_name, 3999 sharing_update=sharing_update) 4000 4001 4002def sig_share_reset(cmd, client, resource_group_name, gallery_name): 4003 SharingUpdate, SharingUpdateOperationTypes = cmd.get_models('SharingUpdate', 'SharingUpdateOperationTypes', 4004 operation_group='shared_galleries') 4005 sharing_update = SharingUpdate(operation_type=SharingUpdateOperationTypes.RESET) 4006 return client.begin_update(resource_group_name=resource_group_name, 4007 gallery_name=gallery_name, 4008 sharing_update=sharing_update) 4009 4010 4011def sig_shared_image_definition_list(client, location, gallery_unique_name, shared_to=None): 4012 # Keep it here as it will add subscription in the future and we need to set it to None to make it work 4013 if shared_to == 'subscription': 4014 shared_to = None 4015 return client.list(location=location, 4016 gallery_unique_name=gallery_unique_name, 4017 shared_to=shared_to) 4018 4019 4020def sig_shared_image_version_list(client, location, gallery_unique_name, gallery_image_name, shared_to=None): 4021 # Keep it here as it will add subscription in the future and we need to set it to None to make it work 4022 if shared_to == 'subscription': 4023 shared_to = None 4024 return client.list(location=location, gallery_unique_name=gallery_unique_name, 4025 gallery_image_name=gallery_image_name, shared_to=shared_to) 4026 4027 4028def get_gallery_instance(cmd, resource_group_name, gallery_name): 4029 from ._client_factory import cf_vm_cl 4030 client = cf_vm_cl(cmd.cli_ctx) 4031 SelectPermissions = cmd.get_models('SelectPermissions', operation_group='shared_galleries') 4032 return client.galleries.get(resource_group_name, gallery_name, select=SelectPermissions.PERMISSIONS) 4033 4034 4035def create_capacity_reservation_group(cmd, client, resource_group_name, capacity_reservation_group_name, location=None, 4036 tags=None, zones=None): 4037 CapacityReservationGroup = cmd.get_models('CapacityReservationGroup') 4038 capacity_reservation_group = CapacityReservationGroup(location=location, tags=tags, zones=zones) 4039 return client.create_or_update(resource_group_name=resource_group_name, 4040 capacity_reservation_group_name=capacity_reservation_group_name, 4041 parameters=capacity_reservation_group) 4042 4043 4044def update_capacity_reservation_group(cmd, client, resource_group_name, capacity_reservation_group_name, tags=None): 4045 CapacityReservationGroupUpdate = cmd.get_models('CapacityReservationGroupUpdate') 4046 capacity_reservation_group = CapacityReservationGroupUpdate(tags=tags) 4047 return client.update(resource_group_name=resource_group_name, 4048 capacity_reservation_group_name=capacity_reservation_group_name, 4049 parameters=capacity_reservation_group) 4050 4051 4052def show_capacity_reservation_group(client, resource_group_name, capacity_reservation_group_name, 4053 instance_view=None): 4054 expand = None 4055 if instance_view: 4056 expand = 'instanceView' 4057 return client.get(resource_group_name=resource_group_name, 4058 capacity_reservation_group_name=capacity_reservation_group_name, 4059 expand=expand) 4060 4061 4062def list_capacity_reservation_group(client, resource_group_name, vm_instance=None, vmss_instance=None): 4063 4064 expand = None 4065 if vm_instance: 4066 expand = "virtualMachines/$ref" 4067 if vmss_instance: 4068 if expand is None: 4069 expand = "virtualMachineScaleSetVMs/$ref" 4070 else: 4071 expand = expand + ",virtualMachineScaleSetVMs/$ref" 4072 4073 if resource_group_name: 4074 return client.list_by_resource_group(resource_group_name=resource_group_name, expand=expand) 4075 return client.list_by_subscription(expand=expand) 4076 4077 4078def create_capacity_reservation(cmd, client, resource_group_name, capacity_reservation_group_name, 4079 capacity_reservation_name, location=None, sku_name=None, capacity=None, 4080 zone=None, tags=None): 4081 Sku = cmd.get_models('Sku') 4082 sku = Sku(name=sku_name, capacity=capacity) 4083 CapacityReservation = cmd.get_models('CapacityReservation') 4084 capacity_reservation = CapacityReservation(location=location, sku=sku, zones=zone, tags=tags) 4085 return client.begin_create_or_update(resource_group_name=resource_group_name, 4086 capacity_reservation_group_name=capacity_reservation_group_name, 4087 capacity_reservation_name=capacity_reservation_name, 4088 parameters=capacity_reservation) 4089 4090 4091def update_capacity_reservation(cmd, client, resource_group_name, capacity_reservation_group_name, 4092 capacity_reservation_name, capacity=None, tags=None): 4093 Sku = cmd.get_models('Sku') 4094 sku = Sku(capacity=capacity) 4095 4096 # If only the data of SKU capacity is updated, the original tags will be cleared. 4097 # Therefore, before the service fixes this issue, we add this temporary logic 4098 if tags is None: 4099 capacity_reservation = client.get(resource_group_name=resource_group_name, 4100 capacity_reservation_group_name=capacity_reservation_group_name, 4101 capacity_reservation_name=capacity_reservation_name) 4102 tags = capacity_reservation.tags 4103 4104 CapacityReservationUpdate = cmd.get_models('CapacityReservationUpdate') 4105 capacity_reservation_update = CapacityReservationUpdate(sku=sku, tags=tags) 4106 return client.begin_update(resource_group_name=resource_group_name, 4107 capacity_reservation_group_name=capacity_reservation_group_name, 4108 capacity_reservation_name=capacity_reservation_name, 4109 parameters=capacity_reservation_update) 4110 4111 4112def show_capacity_reservation(client, resource_group_name, capacity_reservation_group_name, capacity_reservation_name, 4113 instance_view=None): 4114 expand = None 4115 if instance_view: 4116 expand = 'instanceView' 4117 return client.get(resource_group_name=resource_group_name, 4118 capacity_reservation_group_name=capacity_reservation_group_name, 4119 capacity_reservation_name=capacity_reservation_name, expand=expand) 4120 4121 4122def list_capacity_reservation(client, resource_group_name, capacity_reservation_group_name): 4123 return client.list_by_capacity_reservation_group(resource_group_name=resource_group_name, 4124 capacity_reservation_group_name=capacity_reservation_group_name) 4125