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