Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions src/azure-cli/azure/cli/command_modules/vm/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,54 @@ def load_arguments(self, _):
c.argument('skuprofile_vmsizes', nargs='+', min_api='2024-07-01', help='A list of VM sizes in the scale set. See https://azure.microsoft.com/pricing/details/virtual-machines/ for size info.')
c.argument('skuprofile_allostrat', options_list=['--skuprofile-allocation-strategy', '--sku-allocat-strat'], arg_type=get_enum_type(['LowestPrice', 'CapacityOptimized', 'Prioritized']), min_api='2024-07-01', help='Allocation strategy for vm sizes in SKU profile.')
c.argument('skuprofile_rank', nargs='+', min_api='2024-11-01', help='A list for ranks associated with the SKU profile vm sizes.')
c.argument(
'zone_placement_policy',
arg_type=get_enum_type(['Auto']),
min_api='2024-07-01',
help='Specify the policy for availability zone placement of the virtual machine scale set. '
'When set to Auto, the platform automatically selects the availability zones.'
)
c.argument(
'include_zones',
nargs='+',
min_api='2024-07-01',
help='Specify a list of availability zones that must be considered for placement when '
'--zone-placement-policy is set to Auto. '
'If not specified, all availability zones in the region are considered.'
)
c.argument(
'exclude_zones',
nargs='+',
min_api='2024-07-01',
help='Specify a list of availability zones that must be excluded from placement when '
'--zone-placement-policy is set to Auto. '
'If not specified, no availability zones are excluded.'
)
c.argument(
'max_zone_count',
type=int,
min_api='2024-07-01',
help='Specify the maximum number of availability zones to use when '
'--zone-placement-policy is set to Auto. '
'If not specified, all available zones in the region may be used.'
)
c.argument(
'instance_percent_policy',
options_list=['--instance-percent-policy', '--ipp'],
arg_type=get_three_state_flag(),
min_api='2024-07-01',
help='Specify whether maximum percentage of virtual machine instances per zone policy '
'should be enabled on the virtual machine scale set.'
)
c.argument(
'max_instance_percent',
options_list=['--max-instance-percent', '--value-max-instance-percent-per-zone'],
type=int,
min_api='2024-07-01',
help='Specify the maximum percentage of virtual machine instances that can be allocated '
'to a single availability zone in the virtual machine scale set. '
'Valid values are integers between 1 and 100.'
)

with self.argument_context('vmss create', arg_group='Network Balancer') as c:
c.argument('application_gateway', help='Name to use when creating a new application gateway (default) or referencing an existing one. Can also reference an existing application gateway by ID or specify "" for none.', options_list=['--app-gateway'])
Expand Down Expand Up @@ -825,6 +873,31 @@ def load_arguments(self, _):
c.argument('skuprofile_vmsizes', nargs='+', min_api='2024-07-01', help='A list of VM sizes in the scale set. See https://azure.microsoft.com/pricing/details/virtual-machines/ for size info.')
c.argument('skuprofile_allostrat', options_list=['--skuprofile-allocation-strategy', '--sku-allocat-strat'], arg_type=get_enum_type(['LowestPrice', 'CapacityOptimized', 'Prioritized']), min_api='2024-07-01', help='Allocation strategy for vm sizes in SKU profile.')
c.argument('skuprofile_rank', nargs='+', min_api='2024-11-01', help='A list for ranks associated with the SKU profile vm sizes.')
c.argument(
'max_zone_count',
type=int,
min_api='2024-07-01',
help='Specify the maximum number of availability zones to use for this scale set. '
'This setting is only honored for scale sets that use automatic zone placement. '
'If not specified, all available zones in the region may be used.'
)
c.argument(
'instance_percent_policy',
options_list=['--instance-percent-policy', '--ipp'],
arg_type=get_three_state_flag(),
min_api='2024-07-01',
help='Specify whether maximum percentage of virtual machine instances per zone policy '
'should be enabled on the virtual machine scale set.'
)
c.argument(
'max_instance_percent',
options_list=['--max-instance-percent', '--value-max-instance-percent-per-zone'],
type=int,
min_api='2024-07-01',
help='Specify the maximum percentage of virtual machine instances that can be allocated '
'to a single availability zone in the virtual machine scale set. '
'Valid values are integers between 1 and 100.'
)

with self.argument_context('vmss update', min_api='2018-10-01', arg_group='Automatic Repairs') as c:

Expand Down
34 changes: 32 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 @@ -1054,7 +1054,9 @@ def build_vmss_resource(cmd, name, computer_name_prefix, location, tags, overpro
wire_server_access_control_profile_reference_id=None,
imds_access_control_profile_reference_id=None, enable_automatic_zone_balancing=None,
automatic_zone_balancing_strategy=None, automatic_zone_balancing_behavior=None,
enable_automatic_repairs=None):
enable_automatic_repairs=None, zone_placement_policy=None, include_zones=None,
exclude_zones=None, max_zone_count=None, instance_percent_policy=None,
max_instance_percent=None):

# Build IP configuration
ip_configuration = {}
Expand Down Expand Up @@ -1564,6 +1566,24 @@ def build_vmss_resource(cmd, name, computer_name_prefix, location, tags, overpro
if automatic_zone_rebalancing_policy:
resiliency_policy['automaticZoneRebalancingPolicy'] = automatic_zone_rebalancing_policy

zone_allocation_policy = {}
if max_zone_count is not None:
zone_allocation_policy['maxZoneCount'] = max_zone_count

if instance_percent_policy is not None or max_instance_percent is not None:
policy = {}

if instance_percent_policy is not None:
policy['enabled'] = instance_percent_policy

if max_instance_percent is not None:
policy['value'] = max_instance_percent

zone_allocation_policy['maxInstancePercentPerZonePolicy'] = policy

if zone_allocation_policy:
resiliency_policy['zoneAllocationPolicy'] = zone_allocation_policy

if resiliency_policy:
vmss_properties['resiliencyPolicy'] = resiliency_policy

Expand Down Expand Up @@ -1687,7 +1707,7 @@ def build_vmss_resource(cmd, name, computer_name_prefix, location, tags, overpro
'name': name,
'location': location,
'tags': tags,
'apiVersion': cmd.get_api_version(ResourceType.MGMT_COMPUTE, operation_group='virtual_machine_scale_sets'),
'apiVersion': '2025-04-01',
'dependsOn': [],
'properties': vmss_properties
}
Expand All @@ -1707,6 +1727,16 @@ def build_vmss_resource(cmd, name, computer_name_prefix, location, tags, overpro
if edge_zone:
vmss['extendedLocation'] = edge_zone

placement = {}
if zone_placement_policy is not None:
placement['zonePlacementPolicy'] = zone_placement_policy
if include_zones is not None:
placement['includeZones'] = include_zones
if exclude_zones is not None:
placement['excludeZones'] = exclude_zones
if placement:
vmss['Placement'] = placement

return vmss


Expand Down
87 changes: 87 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 @@ -1824,6 +1824,7 @@ def process_vmss_create_namespace(cmd, namespace):
raise ArgumentUsageError('usage error: please specify the --image when you want to specify the VM SKU')

_validate_trusted_launch(namespace)
_validate_vmss_create_auto_zone_placement(namespace)
if namespace.image:

if namespace.vm_sku is None:
Expand Down Expand Up @@ -1920,6 +1921,7 @@ def process_vmss_create_namespace(cmd, namespace):
_validate_vmss_terminate_notification(cmd, namespace)
_validate_vmss_create_automatic_repairs(cmd, namespace)
_validate_vmss_create_host_group(cmd, namespace)
_validate_vmss_create_auto_zone_placement(namespace)

if namespace.secrets:
_validate_secrets(namespace.secrets, namespace.os_type)
Expand Down Expand Up @@ -2636,6 +2638,91 @@ def _validate_vmss_create_host_group(cmd, namespace):
)


def _validate_vmss_create_auto_zone_placement(namespace):
zpp = getattr(namespace, 'zone_placement_policy', None)
zones = getattr(namespace, 'zones', None)
zone_balance = getattr(namespace, 'zone_balance', None)
max_zone_count = getattr(namespace, 'max_zone_count', None)
disable_overprovision = getattr(namespace, 'disable_overprovision', None)
ppg = getattr(namespace, 'ppg', None)
crg = getattr(namespace, 'capacity_reservation_group', None)
orchestration_mode = getattr(namespace, 'orchestration_mode', None)
instance_percent_policy = getattr(namespace, 'instance_percent_policy', None)
max_instance_percent = getattr(namespace, 'max_instance_percent', None)

# "zones", zonePlacementPolicy cannot be enabled if "zones" list exists on the scale set
if zpp and zones:
raise CLIError(
"usage error: --zone-placement-policy cannot be used with --zones. "
"Specify either fixed zones (--zones) or automatic zone placement (--zone-placement-policy)."
)

# zonePlacementPolicy allowed values
if zpp and str(zpp).lower() != 'auto':
raise CLIError(
"usage error: unsupported value for --zone-placement-policy. Only 'Auto' is supported."
)

# max-zone-count must be positive
if max_zone_count is not None and max_zone_count <= 0:
raise CLIError(
"usage error: --max-zone-count must be a positive integer."
)

# zoneBalance=true requires maxZoneCount
if zone_balance is True and max_zone_count is None:
raise CLIError(
"usage error: --zone-balance requires --max-zone-count to be specified."
)

# Zones=Auto does not support overprovisioning
if zpp and orchestration_mode and orchestration_mode.lower() == 'uniform':
if not disable_overprovision:
raise CLIError(
"usage error: zone placement policy does not support overprovisioning. "
"Set --disable-overprovision when using --zone-placement-policy Auto."
)

# zones=Auto does not support Proximity Placement Group
if zpp and ppg:
raise CLIError(
"usage error: zone placement policy does not support proximity placement groups."
)

# zones=Auto does not support Capacity Reservation Group
if zpp and crg:
raise CLIError(
"usage error: zone placement policy does not support capacity reservation groups."
)

if instance_percent_policy is not None:
# enable=true requires value
if instance_percent_policy is True and max_instance_percent is None:
raise CLIError(
"usage error: --instance-percent-policy true requires "
"(--max-instance-percent / --value-max-instance-percent-per-zone)."
)

# enable=false should not be combined with value
if instance_percent_policy is False and max_instance_percent is not None:
raise CLIError(
"usage error: (--max-instance-percent / --value-max-instance-percent-per-zone) cannot be used when "
"--instance-percent-policy is false."
)

# value range
if max_instance_percent is not None:
if instance_percent_policy is None:
raise CLIError(
"usage error: (--max-instance-percent / --value-max-instance-percent-per-zone) cannot be used when "
"--instance-percent-policy is not set."
)

if max_instance_percent < 1 or max_instance_percent > 100:
raise CLIError("usage error: (--max-instance-percent / --value-max-instance-percent-per-zone) must be an "
"integer between 1 and 100.")


def _validate_count(namespace):
if namespace.count < 2 or namespace.count > 250:
raise ValidationError(
Expand Down
32 changes: 29 additions & 3 deletions src/azure-cli/azure/cli/command_modules/vm/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -3708,7 +3708,8 @@ def create_vmss(cmd, vmss_name, resource_group_name, image=None,
imds_mode=None, add_proxy_agent_extension=None, wire_server_access_control_profile_reference_id=None,
imds_access_control_profile_reference_id=None, enable_automatic_zone_balancing=None,
automatic_zone_balancing_strategy=None, automatic_zone_balancing_behavior=None,
enable_automatic_repairs=None):
enable_automatic_repairs=None, zone_placement_policy=None, include_zones=None,
exclude_zones=None, max_zone_count=None, instance_percent_policy=None, max_instance_percent=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 @@ -4032,7 +4033,9 @@ def _get_public_ip_address_allocation(value, sku):
enable_automatic_zone_balancing=enable_automatic_zone_balancing,
automatic_zone_balancing_strategy=automatic_zone_balancing_strategy,
automatic_zone_balancing_behavior=automatic_zone_balancing_behavior,
enable_automatic_repairs=enable_automatic_repairs)
enable_automatic_repairs=enable_automatic_repairs, zone_placement_policy=zone_placement_policy,
include_zones=include_zones, exclude_zones=exclude_zones, max_zone_count=max_zone_count,
instance_percent_policy=instance_percent_policy, max_instance_percent=max_instance_percent)

vmss_resource['dependsOn'] = vmss_dependencies

Expand Down Expand Up @@ -4576,7 +4579,8 @@ def update_vmss(cmd, resource_group_name, name, license_type=None, no_wait=False
wire_server_mode=None, imds_mode=None, add_proxy_agent_extension=None,
wire_server_access_control_profile_reference_id=None,
imds_access_control_profile_reference_id=None, enable_automatic_zone_balancing=None,
automatic_zone_balancing_strategy=None, automatic_zone_balancing_behavior=None, **kwargs):
automatic_zone_balancing_strategy=None, automatic_zone_balancing_behavior=None, max_zone_count=None,
instance_percent_policy=None, max_instance_percent=None, **kwargs):
from .operations.vmss_vms import convert_show_result_to_snake_case as vmss_vms_convert_show_result_to_snake_case
from .operations.vmss import convert_show_result_to_snake_case as vmss_convert_show_result_to_snake_case
vmss = kwargs['parameters']
Expand Down Expand Up @@ -5034,6 +5038,28 @@ def _output(self, *args, **kwargs):
vmss["vm_scale_set_name"] = name
vmss["no_wait"] = no_wait

if max_zone_count is not None or instance_percent_policy is not None or max_instance_percent is not None:
if vmss.get("resiliency_policy", None) is None:
vmss["resiliency_policy"] = {}
if vmss["resiliency_policy"].get("zone_allocation_policy", None) is None:
vmss["resiliency_policy"]["zone_allocation_policy"] = {}

if max_zone_count is not None:
vmss["resiliency_policy"]["zone_allocation_policy"]["max_zone_count"] = max_zone_count

if instance_percent_policy is not None or max_instance_percent is not None:
if vmss["resiliency_policy"]["zone_allocation_policy"].get("max_instance_percent_per_zone_policy",
None) is None:
vmss["resiliency_policy"]["zone_allocation_policy"]["max_instance_percent_per_zone_policy"] = {}

if instance_percent_policy is not None:
vmss["resiliency_policy"]["zone_allocation_policy"]["max_instance_percent_per_zone_policy"][
"enabled"] = instance_percent_policy

if max_instance_percent is not None:
vmss["resiliency_policy"]["zone_allocation_policy"]["max_instance_percent_per_zone_policy"][
"value"] = max_instance_percent

from .operations.vmss import VMSSCreate
return VMSSCreate(cli_ctx=cmd.cli_ctx)(command_args=vmss)

Expand Down
Loading
Loading