Skip to content
2 changes: 1 addition & 1 deletion src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def default_api_version(self):
'disks': '2020-12-01',
'disk_encryption_sets': '2020-12-01',
'disk_accesses': '2020-05-01',
'snapshots': '2020-05-01',
'snapshots': '2020-12-01',
'galleries': '2019-12-01',
'gallery_images': '2020-09-30',
'gallery_image_versions': '2019-12-01',
Expand Down
12 changes: 11 additions & 1 deletion src/azure-cli/azure/cli/command_modules/vm/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from azure.cli.command_modules.monitor.actions import get_period_type


# pylint: disable=too-many-statements, too-many-branches, too-many-locals
# pylint: disable=too-many-statements, too-many-branches, too-many-locals, too-many-lines
def load_arguments(self, _):
# Model imports
StorageAccountTypes = self.get_models('StorageAccountTypes')
Expand Down Expand Up @@ -97,6 +97,12 @@ def load_arguments(self, _):
help='Specify the scale-in policy (space delimited) that decides which virtual machines are chosen for removal when a Virtual Machine Scale Set is scaled-in.'
)

edge_zone_type = CLIArgumentType(
help='Name of edge zone extended location.',
min_api='2020-12-01',
is_preview=True
)

# region MixedScopes
for scope in ['vm', 'disk', 'snapshot', 'image', 'sig']:
with self.argument_context(scope) as c:
Expand Down Expand Up @@ -149,6 +155,7 @@ def load_arguments(self, _):
c.argument('gallery_image_reference_lun', type=int, help='If the disk is created from an image\'s data disk, this is an index that indicates which of the data disks in the image to use. For OS disks, this field is null')
c.argument('logical_sector_size', type=int, help='Logical sector size in bytes for Ultra disks. Supported values are 512 ad 4096. 4096 is the default.')
c.argument('tier', help='Performance tier of the disk (e.g, P4, S10) as described here: https://azure.microsoft.com/en-us/pricing/details/managed-disks/. Does not apply to Ultra disks.')
c.argument('edge_zone', edge_zone_type)
# endregion

# region Snapshots
Expand All @@ -158,6 +165,7 @@ def load_arguments(self, _):
c.argument('sku', arg_type=snapshot_sku)
c.argument('incremental', arg_type=get_three_state_flag(), min_api='2019-03-01',
help='Whether a snapshot is incremental. Incremental snapshots on the same disk occupy less space than full snapshots and can be diffed')
c.argument('edge_zone', edge_zone_type)
# endregion

# region Images
Expand All @@ -179,6 +187,7 @@ def load_arguments(self, _):
help="Storage caching type for the image's data disk.")
c.argument('hyper_v_generation', arg_type=hyper_v_gen_sku, min_api="2019-03-01", help='The hypervisor generation of the Virtual Machine created from the image.')
c.ignore('source_virtual_machine', 'os_blob_uri', 'os_disk', 'os_snapshot', 'data_blob_uris', 'data_disks', 'data_snapshots')
c.argument('edge_zone', edge_zone_type, )
# endregion

# region Image Templates
Expand Down Expand Up @@ -727,6 +736,7 @@ def load_arguments(self, _):
c.argument('secrets', multi_ids_type, help='One or many Key Vault secrets as JSON strings or files via `@{path}` containing `[{ "sourceVault": { "id": "value" }, "vaultCertificates": [{ "certificateUrl": "value", "certificateStore": "cert store name (only on windows)"}] }]`', type=file_type, completer=FilesCompleter())
c.argument('assign_identity', nargs='*', arg_group='Managed Service Identity', help="accept system or user assigned identities separated by spaces. Use '[system]' to refer system assigned identity, or a resource id to refer user assigned identity. Check out help for more examples")
c.ignore('aux_subscriptions')
c.argument('edge_zone', edge_zone_type)

with self.argument_context(scope, arg_group='Authentication') as c:
c.argument('generate_ssh_keys', action='store_true', help='Generate SSH public and private key files if missing. The keys will be stored in the ~/.ssh directory')
Expand Down
15 changes: 13 additions & 2 deletions src/azure-cli/azure/cli/command_modules/vm/_template_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def build_vm_resource( # pylint: disable=too-many-locals, too-many-statements
enable_agent=None, vmss=None, os_disk_encryption_set=None, data_disk_encryption_sets=None, specialized=None,
encryption_at_host=None, dedicated_host_group=None, enable_auto_update=None, patch_mode=None,
enable_hotpatching=None, platform_fault_domain=None, security_type=None, enable_secure_boot=None,
enable_vtpm=None, count=None):
enable_vtpm=None, count=None, edge_zone=None):

os_caching = disk_info['os'].get('caching')

Expand Down Expand Up @@ -528,15 +528,21 @@ def _build_storage_profile():
'dependsOn': [],
'properties': vm_properties,
}

if zone:
vm['zones'] = zone

if count:
vm['copy'] = {
'name': 'vmcopy',
'mode': 'parallel',
'count': count
}
vm['name'] = "[concat('{}', copyIndex())]".format(name)

if edge_zone:
vm['extendedLocation'] = edge_zone

return vm


Expand Down Expand Up @@ -760,7 +766,7 @@ def build_vmss_resource(cmd, name, naming_prefix, location, tags, overprovision,
specialized=None, os_disk_size_gb=None, encryption_at_host=None, host_group=None,
max_batch_instance_percent=None, max_unhealthy_instance_percent=None,
max_unhealthy_upgraded_instance_percent=None, pause_time_between_batches=None,
enable_cross_zone_upgrade=None, prioritize_unhealthy_instances=None):
enable_cross_zone_upgrade=None, prioritize_unhealthy_instances=None, edge_zone=None):

# Build IP configuration
ip_configuration = {
Expand Down Expand Up @@ -1010,8 +1016,13 @@ def build_vmss_resource(cmd, name, naming_prefix, location, tags, overprovision,
},
'properties': vmss_properties
}

if zones:
vmss['zones'] = zones

if edge_zone:
vmss['extendedLocation'] = edge_zone

return vmss


Expand Down
12 changes: 12 additions & 0 deletions src/azure-cli/azure/cli/command_modules/vm/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,7 @@ def _resolve_role_id(cli_ctx, role, scope):
def process_vm_create_namespace(cmd, namespace):
validate_tags(namespace)
_validate_location(cmd, namespace, namespace.zone, namespace.size)
validate_edge_zone(cmd, namespace)
if namespace.count is not None:
_validate_count(namespace)
validate_asg_names_or_ids(cmd, namespace)
Expand Down Expand Up @@ -1440,6 +1441,7 @@ def process_vmss_create_namespace(cmd, namespace):
else:
namespace.vm_sku = 'Standard_D1_v2'
_validate_location(cmd, namespace, namespace.zones, namespace.vm_sku)
validate_edge_zone(cmd, namespace)
validate_asg_names_or_ids(cmd, namespace)
_validate_vm_create_storage_profile(cmd, namespace, for_scale_set=True)
_validate_vm_vmss_create_vnet(cmd, namespace, for_scale_set=True)
Expand Down Expand Up @@ -1496,6 +1498,7 @@ def validate_vmss_disk(cmd, namespace):
def process_disk_or_snapshot_create_namespace(cmd, namespace):
from msrestazure.azure_exceptions import CloudError
validate_tags(namespace)
validate_edge_zone(cmd, namespace)
if namespace.source:
usage_error = 'usage error: --source {SNAPSHOT | DISK} | --source VHD_BLOB_URI [--source-storage-account-id ID]'
try:
Expand All @@ -1510,6 +1513,7 @@ def process_disk_or_snapshot_create_namespace(cmd, namespace):
def process_image_create_namespace(cmd, namespace):
from msrestazure.tools import parse_resource_id
validate_tags(namespace)
validate_edge_zone(cmd, namespace)
source_from_vm = False
try:
# try capturing from VM, a most common scenario
Expand Down Expand Up @@ -1805,3 +1809,11 @@ def _validate_count(namespace):
]
if any(param for param in banned_params):
raise ValidationError('When --count is specified, {} are not allowed'.format(', '.join(params_str)))


def validate_edge_zone(cmd, namespace): # pylint: disable=unused-argument
if namespace.edge_zone:
namespace.edge_zone = {
'name': namespace.edge_zone,
'type': 'EdgeZone'
}
21 changes: 14 additions & 7 deletions src/azure-cli/azure/cli/command_modules/vm/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ def create_managed_disk(cmd, resource_group_name, disk_name, location=None, # p
image_reference=None, image_reference_lun=None,
gallery_image_reference=None, gallery_image_reference_lun=None,
network_access_policy=None, disk_access=None, logical_sector_size=None,
tier=None, enable_bursting=None):
tier=None, enable_bursting=None, edge_zone=None):
from msrestazure.tools import resource_id, is_valid_resource_id
from azure.cli.core.commands.client_factory import get_subscription_id

Expand Down Expand Up @@ -396,6 +396,8 @@ def create_managed_disk(cmd, resource_group_name, disk_name, location=None, # p
disk.tier = tier
if enable_bursting is not None:
disk.bursting_enabled = enable_bursting
if edge_zone:
disk.extended_location = edge_zone

client = _compute_client_factory(cmd.cli_ctx)
return sdk_no_wait(no_wait, client.disks.begin_create_or_update, resource_group_name, disk_name, disk)
Expand Down Expand Up @@ -465,7 +467,7 @@ def create_image(cmd, resource_group_name, name, source, os_type=None, data_disk
os_blob_uri=None, data_blob_uris=None,
os_snapshot=None, data_snapshots=None,
os_disk=None, os_disk_caching=None, data_disks=None, data_disk_caching=None,
tags=None, zone_resilient=None):
tags=None, zone_resilient=None, edge_zone=None):
ImageOSDisk, ImageDataDisk, ImageStorageProfile, Image, SubResource, OperatingSystemStateTypes = cmd.get_models(
'ImageOSDisk', 'ImageDataDisk', 'ImageStorageProfile', 'Image', 'SubResource', 'OperatingSystemStateTypes')

Expand Down Expand Up @@ -507,6 +509,9 @@ def create_image(cmd, resource_group_name, name, source, os_type=None, data_disk
if hyper_v_generation:
image.hyper_v_generation = hyper_v_generation

if edge_zone:
image.extended_location = edge_zone

client = _compute_client_factory(cmd.cli_ctx)
return client.images.begin_create_or_update(resource_group_name, name, image)

Expand All @@ -532,7 +537,7 @@ def create_snapshot(cmd, resource_group_name, snapshot_name, location=None, size
# below are generated internally from 'source'
source_blob_uri=None, source_disk=None, source_snapshot=None, source_storage_account_id=None,
hyper_v_generation=None, tags=None, no_wait=False, disk_encryption_set=None,
encryption_type=None, network_access_policy=None, disk_access=None):
encryption_type=None, network_access_policy=None, disk_access=None, edge_zone=None):
from msrestazure.tools import resource_id, is_valid_resource_id
from azure.cli.core.commands.client_factory import get_subscription_id

Expand Down Expand Up @@ -583,6 +588,8 @@ def create_snapshot(cmd, resource_group_name, snapshot_name, location=None, size
snapshot.network_access_policy = network_access_policy
if disk_access is not None:
snapshot.disk_access_id = disk_access
if edge_zone:
snapshot.extended_location = edge_zone

client = _compute_client_factory(cmd.cli_ctx)
return sdk_no_wait(no_wait, client.snapshots.begin_create_or_update, resource_group_name, snapshot_name, snapshot)
Expand Down Expand Up @@ -720,7 +727,7 @@ def create_vm(cmd, vm_name, resource_group_name, image=None, size='Standard_DS1_
os_disk_encryption_set=None, data_disk_encryption_sets=None, specialized=None,
encryption_at_host=None, enable_auto_update=None, patch_mode=None, ssh_key_name=None,
enable_hotpatching=None, platform_fault_domain=None, security_type=None, enable_secure_boot=None,
enable_vtpm=None, count=None):
enable_vtpm=None, count=None, edge_zone=None):
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.core.util import random_string, hash_string
from azure.cli.core.commands.arm import ArmTemplateBuilder
Expand Down Expand Up @@ -914,7 +921,7 @@ def create_vm(cmd, vm_name, resource_group_name, image=None, size='Standard_DS1_
encryption_at_host=encryption_at_host, dedicated_host_group=dedicated_host_group,
enable_auto_update=enable_auto_update, patch_mode=patch_mode, enable_hotpatching=enable_hotpatching,
platform_fault_domain=platform_fault_domain, security_type=security_type, enable_secure_boot=enable_secure_boot,
enable_vtpm=enable_vtpm, count=count)
enable_vtpm=enable_vtpm, count=count, edge_zone=edge_zone)

vm_resource['dependsOn'] = vm_dependencies

Expand Down Expand Up @@ -2421,7 +2428,7 @@ def create_vmss(cmd, vmss_name, resource_group_name, image=None,
automatic_repairs_grace_period=None, specialized=None, os_disk_size_gb=None, encryption_at_host=None,
host_group=None, max_batch_instance_percent=None, max_unhealthy_instance_percent=None,
max_unhealthy_upgraded_instance_percent=None, pause_time_between_batches=None,
enable_cross_zone_upgrade=None, prioritize_unhealthy_instances=None):
enable_cross_zone_upgrade=None, prioritize_unhealthy_instances=None, edge_zone=None):
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.core.util import random_string, hash_string
from azure.cli.core.commands.arm import ArmTemplateBuilder
Expand Down Expand Up @@ -2660,7 +2667,7 @@ def _get_public_ip_address_allocation(value, sku):
max_unhealthy_instance_percent=max_unhealthy_instance_percent,
max_unhealthy_upgraded_instance_percent=max_unhealthy_upgraded_instance_percent,
pause_time_between_batches=pause_time_between_batches, enable_cross_zone_upgrade=enable_cross_zone_upgrade,
prioritize_unhealthy_instances=prioritize_unhealthy_instances)
prioritize_unhealthy_instances=prioritize_unhealthy_instances, edge_zone=edge_zone)

vmss_resource['dependsOn'] = vmss_dependencies

Expand Down
Loading