From 6347220e859fe0cc2e81c4cdb1acf3512cb59748 Mon Sep 17 00:00:00 2001 From: kai ru <69238381+kairu-ms@users.noreply.github.com> Date: Thu, 7 Jul 2022 16:35:09 +0800 Subject: [PATCH] [Core] `aaz`: Feature Atomic Azure Command support argument validation (#23126) * add argument formats * add test for AAZStrArgFormat * Use OrderedDict for _fields of AAZObjectType * test and fix base argument formats * fix bug with None value * add test for AAZResourceLocationArgFormat * add test for AAZResourceIdArgFormat * add test for AAZSubscriptionIdArgFormat * fix style issues * fix test issues * move test into resource module * fix style issue --- .../azure/cli/core/aaz/__init__.py | 2 + src/azure-cli-core/azure/cli/core/aaz/_arg.py | 44 +- .../azure/cli/core/aaz/_arg_fmt.py | 472 ++++++++++++++++++ .../azure/cli/core/aaz/_command_ctx.py | 42 +- .../azure/cli/core/aaz/_field_type.py | 23 +- .../azure/cli/core/aaz/_field_value.py | 29 +- .../azure/cli/core/aaz/exceptions.py | 18 + .../azure/cli/core/tests/test_aaz_arg_fmt.py | 440 ++++++++++++++++ .../test_aaz_resource_id_arg_fmt.yaml | 162 ++++++ .../test_aaz_resource_location_arg_fmt.yaml | 306 ++++++++++++ .../test_aaz_subscription_id_arg_format.yaml | 42 ++ .../latest/test_aaz_resource_arg_format.py | 220 ++++++++ 12 files changed, 1761 insertions(+), 39 deletions(-) create mode 100644 src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py create mode 100644 src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py create mode 100644 src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_resource_id_arg_fmt.yaml create mode 100644 src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_resource_location_arg_fmt.yaml create mode 100644 src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_subscription_id_arg_format.yaml create mode 100644 src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_aaz_resource_arg_format.py diff --git a/src/azure-cli-core/azure/cli/core/aaz/__init__.py b/src/azure-cli-core/azure/cli/core/aaz/__init__.py index 2645f161168..5528e42768a 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/__init__.py +++ b/src/azure-cli-core/azure/cli/core/aaz/__init__.py @@ -12,6 +12,8 @@ from ._arg import has_value, AAZArgumentsSchema, AAZArgEnum, AAZStrArg, AAZIntArg, AAZObjectArg, AAZDictArg, \ AAZFloatArg, AAZBaseArg, AAZBoolArg, AAZListArg, AAZResourceGroupNameArg, AAZResourceLocationArg, \ AAZResourceIdArg, AAZSubscriptionIdArg +from ._arg_fmt import AAZStrArgFormat, AAZIntArgFormat, AAZFloatArgFormat, AAZBoolArgFormat, AAZObjectArgFormat, \ + AAZDictArgFormat, AAZListArgFormat, AAZResourceLocationArgFormat, AAZResourceIdArgFormat, AAZSubscriptionIdArgFormat from ._base import AAZValuePatch, AAZUndefined from ._command import AAZCommand, AAZCommandGroup, register_command, register_command_group, load_aaz_command_table from ._field_type import AAZIntType, AAZFloatType, AAZStrType, AAZBoolType, AAZDictType, AAZListType, AAZObjectType diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg.py b/src/azure-cli-core/azure/cli/core/aaz/_arg.py index 846a48f400e..63f708f4e68 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_arg.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg.py @@ -13,6 +13,8 @@ from ._field_type import AAZObjectType, AAZStrType, AAZIntType, AAZBoolType, AAZFloatType, AAZListType, AAZDictType, \ AAZSimpleType from ._field_value import AAZObject +from ._arg_fmt import AAZObjectArgFormat, AAZListArgFormat, AAZDictArgFormat, AAZSubscriptionIdArgFormat, \ + AAZResourceLocationArgFormat, AAZResourceIdArgFormat # pylint: disable=redefined-builtin, protected-access @@ -20,6 +22,10 @@ class AAZArgumentsSchema(AAZObjectType): """ Arguments' schema should be defined as fields of it """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._fmt = AAZObjectArgFormat() + def __call__(self, data=None): return AAZObject( schema=self, @@ -64,7 +70,7 @@ class AAZBaseArg(AAZBaseType): # pylint: disable=too-many-instance-attributes """Base argument""" def __init__(self, options=None, required=False, help=None, arg_group=None, is_preview=False, is_experimental=False, - id_part=None, default=AAZUndefined, blank=AAZUndefined, nullable=False): + id_part=None, default=AAZUndefined, blank=AAZUndefined, nullable=False, fmt=None): """ :param options: argument optional names. @@ -78,6 +84,7 @@ def __init__(self, options=None, required=False, help=None, arg_group=None, is_p :param default: when the argument flag is not appeared, the default value will be used. :param blank: when the argument flag is used without value data, the blank value will be used. :param nullable: argument can accept `None` as value + :param fmt: argument format """ super().__init__(options=options, nullable=nullable) self._help = {} # the key in self._help can be 'name', 'short-summary', 'long-summary', 'populator-commands' @@ -96,6 +103,7 @@ def __init__(self, options=None, required=False, help=None, arg_group=None, is_p self._id_part = id_part self._default = default self._blank = blank + self._fmt = fmt def to_cmd_arg(self, name): """ convert AAZArg to CLICommandArgument """ @@ -133,10 +141,9 @@ def _type_in_help(self): class AAZSimpleTypeArg(AAZBaseArg, AAZSimpleType): """Argument accept simple value""" - def __init__(self, enum=None, enum_case_sensitive=False, fmt=None, **kwargs): + def __init__(self, enum=None, enum_case_sensitive=False, **kwargs): super().__init__(**kwargs) self.enum = AAZArgEnum(enum, case_sensitive=enum_case_sensitive) if enum else None - self._fmt = fmt def to_cmd_arg(self, name): arg = super().to_cmd_arg(name) @@ -213,8 +220,8 @@ def to_cmd_arg(self, name): class AAZObjectArg(AAZCompoundTypeArg, AAZObjectType): def __init__(self, fmt=None, **kwargs): - super().__init__(**kwargs) - self._fmt = fmt + fmt = fmt or AAZObjectArgFormat() + super().__init__(fmt=fmt, **kwargs) def to_cmd_arg(self, name): arg = super().to_cmd_arg(name) @@ -238,8 +245,8 @@ def _type_in_help(self): class AAZDictArg(AAZCompoundTypeArg, AAZDictType): def __init__(self, fmt=None, **kwargs): - super().__init__(**kwargs) - self._fmt = fmt + fmt = fmt or AAZDictArgFormat() + super().__init__(fmt=fmt, **kwargs) def to_cmd_arg(self, name): arg = super().to_cmd_arg(name) @@ -263,8 +270,8 @@ def _type_in_help(self): class AAZListArg(AAZCompoundTypeArg, AAZListType): def __init__(self, fmt=None, singular_options=None, **kwargs): - super().__init__(**kwargs) - self._fmt = fmt + fmt = fmt or AAZListArgFormat() + super().__init__(fmt=fmt, **kwargs) self.singular_options = singular_options def to_cmd_arg(self, name): @@ -324,11 +331,13 @@ def __init__( self, options=('--location', '-l'), help="Location. Values from: `az account list-locations`. " "You can configure the default location using `az configure --defaults location=`.", + fmt=None, **kwargs): - super(AAZResourceLocationArg, self).__init__( + fmt = fmt or AAZResourceLocationArgFormat() + super().__init__( options=options, help=help, - fmt=None, # TODO: add ResourceLocation Format, which can transform value with space + fmt=fmt, **kwargs ) @@ -348,17 +357,23 @@ def to_cmd_arg(self, name): class AAZResourceIdArg(AAZStrArg): """ResourceId Argument""" - # TODO: Resource Id arg can support both name and id. And can construct id from name by ResourceId Format + + def __init__(self, fmt=None, **kwargs): + fmt = fmt or AAZResourceIdArgFormat() + super().__init__(fmt=fmt, **kwargs) class AAZSubscriptionIdArg(AAZStrArg): def __init__( - self, help="Name or ID of subscription.", + self, help="Name or ID of subscription. You can configure the default subscription " + "using `az account set -s NAME_OR_ID`", + fmt=None, **kwargs): + fmt = fmt or AAZSubscriptionIdArgFormat() super().__init__( help=help, - fmt=None, # TODO: add format, which can transform name to subscription id + fmt=fmt, **kwargs ) @@ -366,7 +381,6 @@ def to_cmd_arg(self, name): from azure.cli.core._completers import get_subscription_id_list arg = super().to_cmd_arg(name) arg.completer = get_subscription_id_list - return arg diff --git a/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py b/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py new file mode 100644 index 00000000000..f006f9f99c6 --- /dev/null +++ b/src/azure-cli-core/azure/cli/core/aaz/_arg_fmt.py @@ -0,0 +1,472 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long, too-few-public-methods + +import abc +import re +import math + +from knack.log import get_logger + +from ._command_ctx import AAZCommandCtx +from ._field_type import AAZSimpleType +from ._field_value import AAZUndefined, AAZSimpleValue, AAZDict, AAZList, AAZObject +from .exceptions import AAZInvalidArgValueError + +logger = get_logger(__name__) + + +class AAZBaseArgFormat: + + @abc.abstractmethod + def __call__(self, ctx: AAZCommandCtx, value): + raise NotImplementedError() + + +class AAZStrArgFormat(AAZBaseArgFormat): + + def __init__(self, pattern=None, max_length=None, min_length=None): + self._pattern = pattern + self._max_length = max_length + self._min_length = min_length + if self._pattern is not None: + self._compiled_pattern = re.compile(pattern) + else: + self._compiled_pattern = None + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, str) + if self._min_length is not None and len(data) < self._min_length: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' length is less than {self._min_length} ") + if self._max_length is not None and len(data) > self._max_length: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' length is greater than {self._min_length}") + if self._pattern is not None and not self._compiled_pattern.fullmatch(data): + raise AAZInvalidArgValueError( + f"FormatError: '{data}' does not fully match regular expression pattern '{self._pattern}'") + + return value + + +class AAZIntArgFormat(AAZBaseArgFormat): + + def __init__(self, multiple_of=None, maximum=None, minimum=None): + self._multiple_of = multiple_of + self._maximum = maximum + self._minimum = minimum + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, int) + if self._multiple_of is not None and data % self._multiple_of != 0: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' is not multiple of {self._multiple_of}") + if self._minimum is not None and data < self._minimum: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' is less than {self._minimum}") + if self._maximum is not None and data > self._maximum: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' is greater than {self._maximum}") + return value + + +class AAZFloatArgFormat(AAZBaseArgFormat): + + def __init__(self, multiple_of=None, maximum=None, exclusive_maximum=None, minimum=None, exclusive_minimum=None, + rel_tol=1e-09): + self._multiple_of = multiple_of + self._maximum = maximum + self._minimum = minimum + self._exclusive_maximum = exclusive_maximum + self._exclusive_minimum = exclusive_minimum + self._rel_tol = rel_tol + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, float) + + if self._multiple_of is not None: + q = int(data / self._multiple_of) + if not math.isclose(data, q * self._multiple_of, rel_tol=self._rel_tol) and \ + not math.isclose(data, (q + 1) * self._multiple_of, rel_tol=self._rel_tol): + raise AAZInvalidArgValueError( + f"FormatError: '{data}' is not multiple of {self._multiple_of}") + + if self._minimum is not None: + if math.isclose(data, self._minimum, rel_tol=self._rel_tol): + data = self._minimum + + if self._exclusive_minimum: + if data <= self._minimum: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' is not greater than {self._minimum}") + elif data < self._minimum: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' is less than {self._minimum}") + + if self._maximum is not None: + if math.isclose(data, self._maximum, rel_tol=self._rel_tol): + data = self._maximum + + if self._exclusive_maximum: + if data >= self._maximum: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' is not less than {self._maximum}") + elif data > self._maximum: + raise AAZInvalidArgValueError( + f"FormatError: '{data}' is greater than {self._maximum}") + + value._data = data + return value + + +class AAZBoolArgFormat(AAZBaseArgFormat): + + def __init__(self, reverse=False): + self._reverse = reverse # reverse it's value + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, bool) + if self._reverse: + data = not data + + value._data = data + return value + + +class AAZObjectArgFormat(AAZBaseArgFormat): + + def __init__(self, max_properties=None, min_properties=None): + self._max_properties = max_properties + self._min_properties = min_properties + + def __call__(self, ctx, value): + assert isinstance(value, AAZObject) + data = value._data + if data == AAZUndefined or data is None: + return value + + assert isinstance(data, dict) + + # Because _fields of object schema is OrderedDict + # aaz-dev should register arguments in order of their interdependence. + for field_name, field_schema in value._schema._fields.items(): + if not field_schema._fmt: + continue + if field_name in data: + try: + value[field_name] = field_schema._fmt(ctx, value[field_name]) + except AAZInvalidArgValueError as err: + err.indexes = [f'{field_name}', *err.indexes] + raise err from err + else: + # build AAZUndefined value and fmt it. + v = field_schema._ValueCls(field_schema, AAZUndefined) + try: + v = field_schema._fmt(ctx, v) + except AAZInvalidArgValueError as err: + err.indexes = [f'{field_name}', *err.indexes] + raise err from err + if v._data != AAZUndefined: + # when result has real data, assign it + value[field_name] = v + + if value._is_patch: + return value + + if self._min_properties and len(data) < self._min_properties: + raise AAZInvalidArgValueError( + f"FormatError: object length is less than {self._min_properties}") + + if self._max_properties and len(data) > self._max_properties: + raise AAZInvalidArgValueError( + f"FormatError: object length is greater than {self._max_properties}") + + return value + + +class AAZDictArgFormat(AAZBaseArgFormat): + + def __init__(self, max_properties=None, min_properties=None): + self._max_properties = max_properties + self._min_properties = min_properties + + def __call__(self, ctx, value): + assert isinstance(value, AAZDict) + data = value._data + if data == AAZUndefined or data is None: + return value + + assert isinstance(data, dict) + element_schema = value._schema.Element + element_fmt = element_schema._fmt + if element_fmt: + for key in data: + try: + value[key] = element_fmt(ctx, value[key]) + except AAZInvalidArgValueError as err: + err.indexes = [f'[{key}]', *err.indexes] + raise err from err + + if value._is_patch: + return value + + if self._min_properties and len(value) < self._min_properties: + raise AAZInvalidArgValueError( + f"FormatError: dict length is less than {self._min_properties}") + + if self._max_properties and len(value) > self._max_properties: + raise AAZInvalidArgValueError( + f"FormatError: dict length is greater than {self._max_properties}") + + return value + + +class AAZListArgFormat(AAZBaseArgFormat): + + def __init__(self, unique=None, max_length=None, min_length=None): + self._unique = unique + self._max_length = max_length + self._min_length = min_length + + def __call__(self, ctx, value): + assert isinstance(value, AAZList) + data = value._data + if data == AAZUndefined or data is None: + return value + + assert isinstance(data, dict) + element_schema = value._schema.Element + element_fmt = element_schema._fmt + if element_fmt: + for idx in data: + try: + value[idx] = element_fmt(ctx, value[idx]) + except AAZInvalidArgValueError as err: + err.indexes = [f'[{idx}]', *err.indexes] + raise err from err + + if value._is_patch: + return value + + if self._unique and isinstance(element_schema, AAZSimpleType): + unique_elements = set() + for idx in data: + e = value[idx] + if e._is_patch: + continue + if e._data in unique_elements: + raise AAZInvalidArgValueError( + f"FormatError: '{e._data}' duplicate in list.", + indexes=[f'[{idx}]'] + ) + unique_elements.add(e._data) + + if self._min_length is not None and len(value) < self._min_length: + raise AAZInvalidArgValueError( + f"FormatError: list length is less than {self._min_length} ") + + if self._max_length is not None and len(value) > self._max_length: + raise AAZInvalidArgValueError( + f"FormatError: list length is greater than {self._max_length}") + + return value + + +class AAZResourceLocationArgFormat(AAZBaseArgFormat): + + def __init__(self, resource_group_arg=None): + self._resource_group_arg = resource_group_arg + + def __call__(self, ctx, value): + from ._command_ctx import get_subscription_locations + assert isinstance(value, AAZSimpleValue) + + data = value._data + if data is None or value._is_patch: + return value + + if data == AAZUndefined: + data = self.get_location_from_resource_group(ctx) + + if data == AAZUndefined: + return value + + assert isinstance(data, str) + if ' ' in data: + for location in get_subscription_locations(ctx): + if location.display_name.lower() == data.lower(): + # if display name is provided, attempt to convert to short form name + data = location.name + break + + value._data = data + return value + + def get_location_from_resource_group(self, ctx): + from ._command_ctx import get_resource_group_location + + if not self._resource_group_arg: + return AAZUndefined + + rg_name = ctx.args[self._resource_group_arg].to_serialized_data() + if rg_name == AAZUndefined: + return AAZUndefined + + location = get_resource_group_location(ctx, rg_name) + if location != AAZUndefined: + logger.debug("using location '%s' from resource group '%s'", location, rg_name) + + return location + + +class AAZResourceIdArgFormat(AAZBaseArgFormat): + + class _Template: + """ + Example template: + /subscriptions/{subscription}/resourceGroups/{resource_group}/providers/Microsoft.Network/virtualNetworks/{vnet_name}/subnets/{} + + A template string is consisted of several segments joined by '/'. + Placeholder segment is wrapped by '{}'. + + """ + + def __init__(self, template): + self._template = template + self._segments = template.split('/') + blank_placeholders = 0 + for segment in self._segments: + if segment.startswith('{') != segment.endswith('}'): + raise ValueError(f"Invalid segment: {segment}") + if segment == '{}': + blank_placeholders += 1 + + # when there's only one '{}' in placeholder, user can only pass the value of that segment. + self.support_name_segment_only = blank_placeholders == 1 + + def __call__(self, ctx, data): + data_segments = data.split('/') + if len(data_segments) != len(self._segments) and len(data_segments) == 1 and self.support_name_segment_only: + # build resource_id by template + data_segments = [] + for segment in self._segments: + if segment == '{}': + data_segments.append(data) + elif segment.startswith('{'): + # use argument value + arg_name = segment[1:-1] + if arg_name == 'subscription': + arg_data = ctx.subscription_id + else: + arg_data = ctx.args[arg_name].to_serialized_data() + + if arg_data == AAZUndefined or arg_data is None: + raise AAZInvalidArgValueError( + f"FormatError: Failed to construct resource id: argument '{arg_name}' is not defined") + if not isinstance(arg_data, str): + raise AAZInvalidArgValueError( + f"FormatError: Failed to construct resource id: argument '{arg_name}' is not string") + + data_segments.append(arg_data) + else: + data_segments.append(segment) + data = '/'.join(data_segments) + # return data + + # verify resource id + if len(data_segments) != len(self._segments): + raise AAZInvalidArgValueError( + f"FormatError: resource id should be in '{self._template}' format." + ) + for data_segment, segment in zip(data_segments, self._segments): + if not segment.startswith('{') and data_segment.lower() != segment.lower(): + raise AAZInvalidArgValueError( + f"FormatError: resource id should be in '{self._template}' format." + ) + + return data + + def __init__(self, template=None): + """ + + :param template: template property is used to verify a resource Id or construct resource Id. + """ + self._template = None + if template: + self._template = self._Template(template) + + def __call__(self, ctx, value): + from azure.mgmt.core.tools import parse_resource_id + + assert isinstance(value, AAZSimpleValue) + + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + if self._template: + data = self._template(ctx, data) + + parsed_id = parse_resource_id(data) + subscription_id = parsed_id.get('subscription', None) + if subscription_id: + # update subscription_id to support cross tenants + ctx.update_aux_subscriptions(subscription_id) + + value._data = data + return value + + +class AAZSubscriptionIdArgFormat(AAZBaseArgFormat): + + def __call__(self, ctx, value): + assert isinstance(value, AAZSimpleValue) + + data = value._data + if data == AAZUndefined or data is None or value._is_patch: + return value + + assert isinstance(data, str) + # convert subscription name to id + subscriptions_list = ctx.profile.load_cached_subscriptions() + sub_id = None + match_val = data.lower() + for sub in subscriptions_list: + # match by id + if sub['id'].lower() == match_val: + sub_id = sub['id'] + break + if not sub_id: + for sub in subscriptions_list: + # match by name + if sub['name'].lower() == match_val: + sub_id = sub['id'] + break + if sub_id: + ctx.update_aux_subscriptions(sub_id) + value._data = sub_id + else: + logger.warning("Subscription '%s' not recognized.", value._data) + return value diff --git a/src/azure-cli-core/azure/cli/core/aaz/_command_ctx.py b/src/azure-cli-core/azure/cli/core/aaz/_command_ctx.py index 22d5ece2b74..964918644eb 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_command_ctx.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_command_ctx.py @@ -36,9 +36,11 @@ def __init__(self, cli_ctx, schema, command_args, no_wait_arg=None): # support paging self.next_link = AAZUndefined + self._aux_subscriptions = set() + self._aux_tenants = set() + def format_args(self): - # TODO: apply format for argument values - pass + self.args._schema._fmt(ctx=self, value=self.args) def get_login_credential(self): credential, _, _ = self._profile.get_login_credentials( @@ -83,12 +85,40 @@ def subscription_id(self): self._subscription_id = get_subscription_id(cli_ctx=self._cli_ctx) return self._subscription_id + @property + def profile(self): + return self._profile + + def update_aux_subscriptions(self, subscription_id): + if subscription_id == self._subscription_id: + return + self._aux_subscriptions.add(subscription_id) + + def update_aux_tenants(self, tenant_id): + self._aux_tenants.add(tenant_id) + @property def aux_subscriptions(self): - # TODO: fetch aux_subscription base on args - return None + return list(self._aux_subscriptions) or None @property def aux_tenants(self): - # TODO: fetch aux_subscription base on args - return None + return list(self._aux_tenants) or None + + +def get_subscription_locations(ctx: AAZCommandCtx): + from azure.cli.core.commands.parameters import get_subscription_locations as _get_subscription_locations + return _get_subscription_locations(ctx._cli_ctx) + + +def get_resource_group_location(ctx: AAZCommandCtx, rg_name: str): + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from azure.cli.core.profiles import ResourceType + from azure.core.exceptions import ResourceNotFoundError + + resource_client = get_mgmt_service_client(ctx._cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) + try: + rg = resource_client.resource_groups.get(rg_name) + except ResourceNotFoundError: + return AAZUndefined + return rg.location diff --git a/src/azure-cli-core/azure/cli/core/aaz/_field_type.py b/src/azure-cli-core/azure/cli/core/aaz/_field_type.py index 72f469dc1e3..9cc5ec88355 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_field_type.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_field_type.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +from collections import OrderedDict from ._base import AAZBaseType, AAZValuePatch, AAZUndefined from ._field_value import AAZObject, AAZDict, AAZList, AAZSimpleValue from .exceptions import AAZUnknownFieldError, AAZConflictFieldDefinitionError, AAZValuePrecisionLossError, \ @@ -23,7 +24,8 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def process_data(self, data, **kwargs): - if data is None: + if data == None: # noqa: E711, pylint: disable=singleton-comparison + # data can be None or AAZSimpleValue == None if self._nullable: return None return AAZValuePatch.build(self) @@ -56,7 +58,8 @@ class AAZFloatType(AAZSimpleType): DataType = float def process_data(self, data, **kwargs): - if data is None: + if data == None: # noqa: E711, pylint: disable=singleton-comparison + # data can be None or AAZSimpleValue == None if self._nullable: return None return AAZValuePatch.build(self) @@ -94,12 +97,15 @@ class AAZObjectType(AAZBaseType): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self._fields = {} + # It's important to keep the order of fields. + # This feature can help to resolve arguments interdependent problem. + # aaz-dev should register fields based on the interdependent order. + self._fields = OrderedDict() self._fields_alias_map = {} # key is the option, value is field # Polymorphism support self._discriminator_field_name = None - self._discriminators = {} + self._discriminators = OrderedDict() def __getitem__(self, key): name = self.get_attr_name(key) @@ -159,7 +165,8 @@ def get_attr_name(self, key): return None def process_data(self, data, **kwargs): - if data is None: + if data == None: # noqa: E711, pylint: disable=singleton-comparison + # data can be None or AAZSimpleValue == None if self._nullable: return None return AAZValuePatch.build(self) @@ -270,7 +277,8 @@ def __getitem__(self, key): return self.Element def process_data(self, data, **kwargs): - if data is None: + if data == None: # noqa: E711, pylint: disable=singleton-comparison + # data can be None or AAZSimpleValue == None if self._nullable: return None return AAZValuePatch.build(self) @@ -323,7 +331,8 @@ def __getitem__(self, key): return self.Element def process_data(self, data, **kwargs): - if data is None: + if data == None: # noqa: E711, pylint: disable=singleton-comparison + # data can be None or AAZSimpleValue == None if self._nullable: return None return AAZValuePatch.build(self) diff --git a/src/azure-cli-core/azure/cli/core/aaz/_field_value.py b/src/azure-cli-core/azure/cli/core/aaz/_field_value.py index 5be79e0c9a7..e47b8858035 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/_field_value.py +++ b/src/azure-cli-core/azure/cli/core/aaz/_field_value.py @@ -57,7 +57,7 @@ class AAZObject(AAZBaseValue): def __init__(self, schema, data): super().__init__(schema, data) - assert isinstance(self._data, dict) or self._data is None + assert isinstance(self._data, dict) or self._data is None or self._data == AAZUndefined def __getitem__(self, key): attr_schema, name = self._get_attr_schema_and_name(key) @@ -116,9 +116,10 @@ def __ne__(self, other): return not self == other def to_serialized_data(self, processor=None, **kwargs): - if self._data is None: + if self._data == AAZUndefined: + result = AAZUndefined + elif self._data is None: result = None - else: result = {} schemas = [self._schema] @@ -137,7 +138,7 @@ def to_serialized_data(self, processor=None, **kwargs): result[name] = v if not result and self._is_patch: - return AAZUndefined + result = AAZUndefined if processor: result = processor(self._schema, result) @@ -162,7 +163,7 @@ def __init__(self, schema, data): from ._field_type import AAZDictType assert isinstance(schema, AAZDictType) super().__init__(schema, data) - assert isinstance(self._data, dict) or self._data is None + assert isinstance(self._data, dict) or self._data is None or self._data == AAZUndefined def __getitem__(self, key) -> AAZBaseValue: item_schema = self._schema.Element @@ -223,7 +224,9 @@ def items(self): yield key, self[key] def to_serialized_data(self, processor=None, **kwargs): - if self._data is None: + if self._data == AAZUndefined: + result = AAZUndefined + elif self._data is None: result = None else: result = {} @@ -234,7 +237,8 @@ def to_serialized_data(self, processor=None, **kwargs): result[key] = v if not result and self._is_patch: - return AAZUndefined + result = AAZUndefined + if processor: result = processor(self._schema, result) return result @@ -246,9 +250,9 @@ def __init__(self, schema, data): from ._field_type import AAZListType assert isinstance(schema, AAZListType) super().__init__(schema, data) - assert isinstance(self._data, dict) or self._data is None # the key is the idx + assert isinstance(self._data, dict) or self._data is None or self._data == AAZUndefined # the key is the idx self._len = 0 - if self._data is not None: + if self._data is not None and self._data != AAZUndefined: for idx in self._data: if idx + 1 > self._len: self._len = idx + 1 @@ -345,7 +349,9 @@ def clear(self): def to_serialized_data(self, processor=None, keep_undefined_in_list=False, # pylint: disable=arguments-differ **kwargs): - if self._data is None: + if self._data == AAZUndefined: + result = AAZUndefined + elif self._data is None: result = None else: result = [] @@ -364,7 +370,8 @@ def to_serialized_data(self, processor=None, keep_undefined_in_list=False, # py result = [] if not result and self._is_patch: - return AAZUndefined + result = AAZUndefined + if processor: result = processor(self._schema, result) return result diff --git a/src/azure-cli-core/azure/cli/core/aaz/exceptions.py b/src/azure-cli-core/azure/cli/core/aaz/exceptions.py index 48d7a09ca31..3bb6a1c241f 100644 --- a/src/azure-cli-core/azure/cli/core/aaz/exceptions.py +++ b/src/azure-cli-core/azure/cli/core/aaz/exceptions.py @@ -59,3 +59,21 @@ def __str__(self): class AAZInvalidValueError(ValueError): pass + + +class AAZInvalidArgValueError(ValueError): + + def __init__(self, msg, indexes=None): + super().__init__() + self.msg = msg + self.indexes = indexes or [] + + def __str__(self): + if self.indexes: + idx = self.indexes[0] + for i in self.indexes[1:]: + if not i.startswith('['): + idx += '.' + idx += i + return f"Argument Value Error: {idx}: {self.msg}" + return f"Argument Value Error: {self.msg}" diff --git a/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py new file mode 100644 index 00000000000..82e2c783cd6 --- /dev/null +++ b/src/azure-cli-core/azure/cli/core/tests/test_aaz_arg_fmt.py @@ -0,0 +1,440 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import json +import unittest + +from azure.cli.core import azclierror +from azure.cli.core.aaz import exceptions as aazerror +from azure.cli.core.aaz._command_ctx import AAZCommandCtx +from azure.cli.core.aaz import AAZArgumentsSchema +from azure.cli.core.mock import DummyCli + + +class TestAAZArgBaseFmt(unittest.TestCase): + + @staticmethod + def format_arg(schema, data): + ctx = AAZCommandCtx( + cli_ctx=DummyCli(), schema=schema, + command_args=data + ) + ctx.format_args() + return ctx.args + + def test_str_fmt(self): + from azure.cli.core.aaz import AAZStrArg, AAZStrArgFormat + schema = AAZArgumentsSchema() + schema.str1 = AAZStrArg( + fmt=AAZStrArgFormat( + pattern="[a-z]+", + max_length=8, + min_length=2, + ), + nullable=True + ) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"str1": ""}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"str1": "1234"}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"str1": "abcdefghi"}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"str1": "aBCD"}) + + args = self.format_arg(schema, {"str1": "abcdefgh"}) + self.assertEqual(args.str1, "abcdefgh") + + args = self.format_arg(schema, {"str1": "abcd"}) + self.assertEqual(args.str1, "abcd") + + args = self.format_arg(schema, {"str1": None}) + self.assertEqual(args.str1, None) + + def test_int_fmt(self): + from azure.cli.core.aaz import AAZIntArg, AAZIntArgFormat + + schema = AAZArgumentsSchema() + schema.int1 = AAZIntArg( + fmt=AAZIntArgFormat( + multiple_of=10, + maximum=30, + minimum=20, + ), + nullable=True + ) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"int1": 10}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"int1": 25}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"int1": 40}) + + args = self.format_arg(schema, {"int1": 20}) + self.assertEqual(args.int1, 20) + + args = self.format_arg(schema, {"int1": 30}) + self.assertEqual(args.int1, 30) + + args = self.format_arg(schema, {"int1": None}) + self.assertEqual(args.int1, None) + + def test_float_fmt(self): + from azure.cli.core.aaz import AAZFloatArg, AAZFloatArgFormat + + schema = AAZArgumentsSchema() + schema.flt1 = AAZFloatArg( + fmt=AAZFloatArgFormat( + multiple_of=1.1, + maximum=33, + minimum=22, + exclusive_maximum=True, + exclusive_minimum=True, + ) + ) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"flt1": 1.1}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"flt1": 22}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"flt1": 33}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"flt1": 23}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"flt1": 23.099}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"flt1": 31.901}) + + args = self.format_arg(schema, {"flt1": 23.1}) + self.assertEqual(args.flt1, 23.1) + + args = self.format_arg(schema, {"flt1": 31.9}) + self.assertEqual(args.flt1, 31.9) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"flt1": 22.0000000001}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, {"flt1": 32.9999999999}) + + schema = AAZArgumentsSchema() + schema.flt1 = AAZFloatArg( + fmt=AAZFloatArgFormat( + multiple_of=1.1, + maximum=33, + minimum=22, + exclusive_maximum=False, + exclusive_minimum=False, + ), + nullable=True + ) + args = self.format_arg(schema, {"flt1": 22.0000000001}) + self.assertEqual(args.flt1, 22) + + args = self.format_arg(schema, {"flt1": 32.9999999999}) + self.assertEqual(args.flt1, 33) + + args = self.format_arg(schema, {"flt1": None}) + self.assertEqual(args.flt1, None) + + def test_bool_fmt(self): + from azure.cli.core.aaz import AAZBoolArg, AAZBoolArgFormat + + schema = AAZArgumentsSchema() + schema.bool = AAZBoolArg( + fmt=AAZBoolArgFormat(reverse=True), + nullable=True, + ) + + args = self.format_arg(schema, {"bool": True}) + self.assertEqual(args.bool, False) + + args = self.format_arg(schema, {"bool": False}) + self.assertEqual(args.bool, True) + + args = self.format_arg(schema, {"bool": None}) + self.assertEqual(args.bool, None) + + def test_object_fmt(self): + from azure.cli.core.aaz import AAZObjectArgFormat, AAZStrArgFormat, AAZBoolArgFormat, AAZIntArgFormat, \ + AAZDictArgFormat, AAZListArgFormat, AAZFloatArgFormat + from azure.cli.core.aaz import AAZObjectArg, AAZStrArg, AAZBoolArg, AAZIntArg, AAZDictArg, AAZListArg, \ + AAZFloatArg + + schema = AAZArgumentsSchema() + schema.properties = AAZObjectArg(fmt=AAZObjectArgFormat( + max_properties=3, + min_properties=2, + )) + schema.properties.name = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z]+')) + schema.properties.enabled = AAZBoolArg(fmt=AAZBoolArgFormat(reverse=True)) + schema.properties.count = AAZIntArg(fmt=AAZIntArgFormat(minimum=0)) + schema.properties.vnet = AAZObjectArg() + schema.properties.vnet.name = AAZStrArg() + schema.properties.vnet.name2 = AAZStrArg() + + args = self.format_arg(schema, { + "properties": { + "name": "abcd", + "enabled": True, + "count": 100 + } + }) + self.assertEqual(args.properties.to_serialized_data(), { + "name": "abcd", + "enabled": False, + "count": 100 + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "properties": { + "name": "abcd", + } + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "properties": { + "name": "abcd", + "enabled": False, + "count": 100, + "vnet": { + "name": "test", + } + } + }) + + schema = AAZArgumentsSchema() + schema.properties = AAZObjectArg(nullable=True) + schema.properties.name = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z]+')) + schema.properties.enabled = AAZBoolArg(fmt=AAZBoolArgFormat(reverse=True)) + schema.properties.count = AAZIntArg(fmt=AAZIntArgFormat(minimum=0)) + schema.properties.vnet = AAZObjectArg(nullable=True) + schema.properties.vnet.name = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z]+')) + + schema.properties.tags = AAZDictArg(fmt=AAZDictArgFormat()) + schema.properties.tags.Element = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z]+')) + schema.properties.actions = AAZListArg(fmt=AAZListArgFormat()) + schema.properties.actions.Element = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z]+')) + schema.properties.sub = AAZObjectArg(fmt=AAZObjectArgFormat()) + schema.properties.name2 = AAZStrArg(fmt=AAZStrArgFormat()) + schema.properties.int2 = AAZIntArg(fmt=AAZIntArgFormat()) + schema.properties.bool2 = AAZBoolArg(fmt=AAZBoolArgFormat()) + schema.properties.float2 = AAZFloatArg(fmt=AAZFloatArgFormat()) + + args = self.format_arg(schema, { + "properties": {} + }) + self.assertEqual(args.properties.to_serialized_data(), {}) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "properties": { + "name": "a1234", + } + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "properties": { + "count": -10, + } + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "properties": { + "vnet": { + "name": "test11", + } + } + }) + + args = self.format_arg(schema, {"properties": None}) + self.assertEqual(args.properties, None) + + args = self.format_arg(schema, { + "properties": { + "vnet": None + } + }) + self.assertEqual(args, { + "properties": { + "vnet": None + } + }) + + def test_dict_fmt(self): + from azure.cli.core.aaz import AAZObjectArgFormat, AAZDictArgFormat, AAZStrArgFormat, AAZIntArgFormat + from azure.cli.core.aaz import AAZObjectArg, AAZDictArg, AAZStrArg, AAZIntArg + + schema = AAZArgumentsSchema() + schema.tags = AAZDictArg(fmt=AAZDictArgFormat(max_properties=3, min_properties=2), nullable=True) + schema.tags.Element = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z0-9]+')) + schema.actions = AAZDictArg() + schema.actions.Element = AAZObjectArg(nullable=True) + schema.actions.Element.name = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z]+')) + schema.actions.Element.name2 = AAZStrArg(nullable=True) + + args = self.format_arg(schema, { + "tags": { + "flag1": "v1", + "flag2": "v2", + }, + "actions": { + "a": { + "name": "abc" + }, + "b": { + "name": "c" + } + } + }) + self.assertEqual(args.to_serialized_data(), { + "tags": { + "flag1": "v1", + "flag2": "v2", + }, + "actions": { + "a": { + "name": "abc" + }, + "b": { + "name": "c" + } + } + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "tags": { + "flag1": "v1", + }, + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "tags": { + "flag1": "v1", + "flag2": "v2", + "flag3": "v3", + "flag4": "v4", + }, + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "actions": { + "a": { + "name": "abc11" + }, + }, + }) + + args = self.format_arg(schema, { + "tags": None, + "actions": { + "a": { + "name": "abc" + }, + "b": { + "name": "c", + "name2": None, + }, + "c": None + } + }) + self.assertEqual(args.to_serialized_data(), { + "tags": None, + "actions": { + "a": { + "name": "abc" + }, + "b": { + "name": "c", + "name2": None, + }, + "c": None + } + }) + + def test_list_fmt(self): + from azure.cli.core.aaz import AAZObjectArgFormat, AAZListArgFormat, AAZStrArgFormat, AAZIntArgFormat + from azure.cli.core.aaz import AAZObjectArg, AAZListArg, AAZStrArg, AAZIntArg + + schema = AAZArgumentsSchema() + schema.tags = AAZListArg(fmt=AAZListArgFormat(unique=True, max_length=3, min_length=2)) + schema.tags.Element = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z0-9]+'), nullable=True) + schema.actions = AAZListArg(nullable=True) + schema.actions.Element = AAZObjectArg() + schema.actions.Element.name = AAZStrArg(fmt=AAZStrArgFormat(pattern='[a-z]+')) + + args = self.format_arg(schema, { + "tags": ['v1', 'v2'], + "actions": [ + { + "name": "abc" + }, + { + "name": "c" + } + ] + }) + self.assertEqual(args.to_serialized_data(), { + "tags": ['v1', 'v2'], + "actions": [ + { + "name": "abc" + }, + { + "name": "c" + } + ] + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "tags": ['v1',], + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "tags": ['v1', 'v2', 'v3', 'v4'], + }) + + with self.assertRaises(aazerror.AAZInvalidArgValueError): + self.format_arg(schema, { + "tags": ['v1', 'v2', 'v2'], + }) + + args = self.format_arg(schema, { + "tags": ['v1', 'v2'], + }) + self.assertEqual(args.to_serialized_data(), { + "tags": ['v1', 'v2'], + }) + + args = self.format_arg(schema, { + "tags": ['v1', 'v2', None], + "actions": None, + }) + self.assertEqual(args.to_serialized_data(), { + "tags": ['v1', 'v2', None], + "actions": None, + }) diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_resource_id_arg_fmt.yaml b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_resource_id_arg_fmt.yaml new file mode 100644 index 00000000000..8200c721437 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_resource_id_arg_fmt.yaml @@ -0,0 +1,162 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - unknown + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.38.0 azsdk-python-azure-mgmt-resource/21.1.0b1 Python/3.8.10 (Windows-10-10.0.22622-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/test_resource_id_arg_fmt000001?api-version=2021-04-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test_resource_id_arg_fmt000001","name":"test_resource_id_arg_fmt000001","type":"Microsoft.Resources/resourceGroups","location":"eastus2","tags":{"product":"azurecli","cause":"automation","date":"2022-07-07T06:43:15Z"},"properties":{"provisioningState":"Succeeded"}}' + headers: + cache-control: + - no-cache + content-length: + - '339' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 07 Jul 2022 06:43:20 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - unknown + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.38.0 azsdk-python-azure-mgmt-resource/21.1.0b1 Python/3.8.10 (Windows-10-10.0.22622-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/test_resource_id_arg_fmt000001?api-version=2021-04-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test_resource_id_arg_fmt000001","name":"test_resource_id_arg_fmt000001","type":"Microsoft.Resources/resourceGroups","location":"eastus2","tags":{"product":"azurecli","cause":"automation","date":"2022-07-07T06:43:15Z"},"properties":{"provisioningState":"Succeeded"}}' + headers: + cache-control: + - no-cache + content-length: + - '339' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 07 Jul 2022 06:43:20 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - unknown + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.38.0 azsdk-python-azure-mgmt-resource/21.1.0b1 Python/3.8.10 (Windows-10-10.0.22622-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/test_resource_id_arg_fmt000001?api-version=2021-04-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test_resource_id_arg_fmt000001","name":"test_resource_id_arg_fmt000001","type":"Microsoft.Resources/resourceGroups","location":"eastus2","tags":{"product":"azurecli","cause":"automation","date":"2022-07-07T06:43:15Z"},"properties":{"provisioningState":"Succeeded"}}' + headers: + cache-control: + - no-cache + content-length: + - '339' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 07 Jul 2022 06:43:20 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - unknown + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.38.0 azsdk-python-azure-mgmt-resource/21.1.0b1 Python/3.8.10 (Windows-10-10.0.22622-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/test_resource_id_arg_fmt000001?api-version=2021-04-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test_resource_id_arg_fmt000001","name":"test_resource_id_arg_fmt000001","type":"Microsoft.Resources/resourceGroups","location":"eastus2","tags":{"product":"azurecli","cause":"automation","date":"2022-07-07T06:43:15Z"},"properties":{"provisioningState":"Succeeded"}}' + headers: + cache-control: + - no-cache + content-length: + - '339' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 07 Jul 2022 06:43:21 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +version: 1 diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_resource_location_arg_fmt.yaml b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_resource_location_arg_fmt.yaml new file mode 100644 index 00000000000..a1b29e28162 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_resource_location_arg_fmt.yaml @@ -0,0 +1,306 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - unknown + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.38.0 azsdk-python-azure-mgmt-resource/21.1.0b1 Python/3.8.10 (Windows-10-10.0.22622-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/test_resource_location_arg_fmt000001?api-version=2021-04-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test_resource_location_arg_fmt000001","name":"test_resource_location_arg_fmt000001","type":"Microsoft.Resources/resourceGroups","location":"eastus2","tags":{"product":"azurecli","cause":"automation","date":"2022-07-07T06:43:27Z"},"properties":{"provisioningState":"Succeeded"}}' + headers: + cache-control: + - no-cache + content-length: + - '351' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 07 Jul 2022 06:43:30 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - unknown + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.38.0 azsdk-python-azure-mgmt-resource/21.1.0b1 Python/3.8.10 (Windows-10-10.0.22622-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/locations?api-version=2019-11-01 + response: + body: + string: "{\"value\":[{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus\",\"name\":\"eastus\",\"displayName\":\"East + US\",\"regionalDisplayName\":\"(US) East US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-79.8164\",\"latitude\":\"37.3719\",\"physicalLocation\":\"Virginia\",\"pairedRegion\":[{\"name\":\"westus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2\",\"name\":\"eastus2\",\"displayName\":\"East + US 2\",\"regionalDisplayName\":\"(US) East US 2\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-78.3889\",\"latitude\":\"36.6681\",\"physicalLocation\":\"Virginia\",\"pairedRegion\":[{\"name\":\"centralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southcentralus\",\"name\":\"southcentralus\",\"displayName\":\"South + Central US\",\"regionalDisplayName\":\"(US) South Central US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-98.5\",\"latitude\":\"29.4167\",\"physicalLocation\":\"Texas\",\"pairedRegion\":[{\"name\":\"northcentralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northcentralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus2\",\"name\":\"westus2\",\"displayName\":\"West + US 2\",\"regionalDisplayName\":\"(US) West US 2\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-119.852\",\"latitude\":\"47.233\",\"physicalLocation\":\"Washington\",\"pairedRegion\":[{\"name\":\"westcentralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westcentralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus3\",\"name\":\"westus3\",\"displayName\":\"West + US 3\",\"regionalDisplayName\":\"(US) West US 3\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-112.074036\",\"latitude\":\"33.448376\",\"physicalLocation\":\"Phoenix\",\"pairedRegion\":[{\"name\":\"eastus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiaeast\",\"name\":\"australiaeast\",\"displayName\":\"Australia + East\",\"regionalDisplayName\":\"(Asia Pacific) Australia East\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"151.2094\",\"latitude\":\"-33.86\",\"physicalLocation\":\"New + South Wales\",\"pairedRegion\":[{\"name\":\"australiasoutheast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiasoutheast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southeastasia\",\"name\":\"southeastasia\",\"displayName\":\"Southeast + Asia\",\"regionalDisplayName\":\"(Asia Pacific) Southeast Asia\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"103.833\",\"latitude\":\"1.283\",\"physicalLocation\":\"Singapore\",\"pairedRegion\":[{\"name\":\"eastasia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastasia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northeurope\",\"name\":\"northeurope\",\"displayName\":\"North + Europe\",\"regionalDisplayName\":\"(Europe) North Europe\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"-6.2597\",\"latitude\":\"53.3478\",\"physicalLocation\":\"Ireland\",\"pairedRegion\":[{\"name\":\"westeurope\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westeurope\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedencentral\",\"name\":\"swedencentral\",\"displayName\":\"Sweden + Central\",\"regionalDisplayName\":\"(Europe) Sweden Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"17.14127\",\"latitude\":\"60.67488\",\"physicalLocation\":\"G\xE4vle\",\"pairedRegion\":[{\"name\":\"swedensouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedensouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uksouth\",\"name\":\"uksouth\",\"displayName\":\"UK + South\",\"regionalDisplayName\":\"(Europe) UK South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"-0.799\",\"latitude\":\"50.941\",\"physicalLocation\":\"London\",\"pairedRegion\":[{\"name\":\"ukwest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/ukwest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westeurope\",\"name\":\"westeurope\",\"displayName\":\"West + Europe\",\"regionalDisplayName\":\"(Europe) West Europe\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"4.9\",\"latitude\":\"52.3667\",\"physicalLocation\":\"Netherlands\",\"pairedRegion\":[{\"name\":\"northeurope\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northeurope\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralus\",\"name\":\"centralus\",\"displayName\":\"Central + US\",\"regionalDisplayName\":\"(US) Central US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-93.6208\",\"latitude\":\"41.5908\",\"physicalLocation\":\"Iowa\",\"pairedRegion\":[{\"name\":\"eastus2\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafricanorth\",\"name\":\"southafricanorth\",\"displayName\":\"South + Africa North\",\"regionalDisplayName\":\"(Africa) South Africa North\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Africa\",\"longitude\":\"28.218370\",\"latitude\":\"-25.731340\",\"physicalLocation\":\"Johannesburg\",\"pairedRegion\":[{\"name\":\"southafricawest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafricawest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralindia\",\"name\":\"centralindia\",\"displayName\":\"Central + India\",\"regionalDisplayName\":\"(Asia Pacific) Central India\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"73.9197\",\"latitude\":\"18.5822\",\"physicalLocation\":\"Pune\",\"pairedRegion\":[{\"name\":\"southindia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southindia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastasia\",\"name\":\"eastasia\",\"displayName\":\"East + Asia\",\"regionalDisplayName\":\"(Asia Pacific) East Asia\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"114.188\",\"latitude\":\"22.267\",\"physicalLocation\":\"Hong + Kong\",\"pairedRegion\":[{\"name\":\"southeastasia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southeastasia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japaneast\",\"name\":\"japaneast\",\"displayName\":\"Japan + East\",\"regionalDisplayName\":\"(Asia Pacific) Japan East\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"139.77\",\"latitude\":\"35.68\",\"physicalLocation\":\"Tokyo, + Saitama\",\"pairedRegion\":[{\"name\":\"japanwest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japanwest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/koreacentral\",\"name\":\"koreacentral\",\"displayName\":\"Korea + Central\",\"regionalDisplayName\":\"(Asia Pacific) Korea Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"126.9780\",\"latitude\":\"37.5665\",\"physicalLocation\":\"Seoul\",\"pairedRegion\":[{\"name\":\"koreasouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/koreasouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canadacentral\",\"name\":\"canadacentral\",\"displayName\":\"Canada + Central\",\"regionalDisplayName\":\"(Canada) Canada Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Canada\",\"longitude\":\"-79.383\",\"latitude\":\"43.653\",\"physicalLocation\":\"Toronto\",\"pairedRegion\":[{\"name\":\"canadaeast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canadaeast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/francecentral\",\"name\":\"francecentral\",\"displayName\":\"France + Central\",\"regionalDisplayName\":\"(Europe) France Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"2.3730\",\"latitude\":\"46.3772\",\"physicalLocation\":\"Paris\",\"pairedRegion\":[{\"name\":\"francesouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/francesouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germanywestcentral\",\"name\":\"germanywestcentral\",\"displayName\":\"Germany + West Central\",\"regionalDisplayName\":\"(Europe) Germany West Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"8.682127\",\"latitude\":\"50.110924\",\"physicalLocation\":\"Frankfurt\",\"pairedRegion\":[{\"name\":\"germanynorth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germanynorth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norwayeast\",\"name\":\"norwayeast\",\"displayName\":\"Norway + East\",\"regionalDisplayName\":\"(Europe) Norway East\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"10.752245\",\"latitude\":\"59.913868\",\"physicalLocation\":\"Norway\",\"pairedRegion\":[{\"name\":\"norwaywest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norwaywest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerlandnorth\",\"name\":\"switzerlandnorth\",\"displayName\":\"Switzerland + North\",\"regionalDisplayName\":\"(Europe) Switzerland North\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"8.564572\",\"latitude\":\"47.451542\",\"physicalLocation\":\"Zurich\",\"pairedRegion\":[{\"name\":\"switzerlandwest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerlandwest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/brazilsouth\",\"name\":\"brazilsouth\",\"displayName\":\"Brazil + South\",\"regionalDisplayName\":\"(South America) Brazil South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"South + America\",\"longitude\":\"-46.633\",\"latitude\":\"-23.55\",\"physicalLocation\":\"Sao + Paulo State\",\"pairedRegion\":[{\"name\":\"southcentralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southcentralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2euap\",\"name\":\"eastus2euap\",\"displayName\":\"East + US 2 EUAP\",\"regionalDisplayName\":\"(US) East US 2 EUAP\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-78.3889\",\"latitude\":\"36.6681\",\"pairedRegion\":[{\"name\":\"centraluseuap\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centraluseuap\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralusstage\",\"name\":\"centralusstage\",\"displayName\":\"Central + US (Stage)\",\"regionalDisplayName\":\"(US) Central US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastusstage\",\"name\":\"eastusstage\",\"displayName\":\"East + US (Stage)\",\"regionalDisplayName\":\"(US) East US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2stage\",\"name\":\"eastus2stage\",\"displayName\":\"East + US 2 (Stage)\",\"regionalDisplayName\":\"(US) East US 2 (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northcentralusstage\",\"name\":\"northcentralusstage\",\"displayName\":\"North + Central US (Stage)\",\"regionalDisplayName\":\"(US) North Central US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southcentralusstage\",\"name\":\"southcentralusstage\",\"displayName\":\"South + Central US (Stage)\",\"regionalDisplayName\":\"(US) South Central US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westusstage\",\"name\":\"westusstage\",\"displayName\":\"West + US (Stage)\",\"regionalDisplayName\":\"(US) West US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus2stage\",\"name\":\"westus2stage\",\"displayName\":\"West + US 2 (Stage)\",\"regionalDisplayName\":\"(US) West US 2 (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/asia\",\"name\":\"asia\",\"displayName\":\"Asia\",\"regionalDisplayName\":\"Asia\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/asiapacific\",\"name\":\"asiapacific\",\"displayName\":\"Asia + Pacific\",\"regionalDisplayName\":\"Asia Pacific\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australia\",\"name\":\"australia\",\"displayName\":\"Australia\",\"regionalDisplayName\":\"Australia\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/brazil\",\"name\":\"brazil\",\"displayName\":\"Brazil\",\"regionalDisplayName\":\"Brazil\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canada\",\"name\":\"canada\",\"displayName\":\"Canada\",\"regionalDisplayName\":\"Canada\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/europe\",\"name\":\"europe\",\"displayName\":\"Europe\",\"regionalDisplayName\":\"Europe\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/france\",\"name\":\"france\",\"displayName\":\"France\",\"regionalDisplayName\":\"France\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germany\",\"name\":\"germany\",\"displayName\":\"Germany\",\"regionalDisplayName\":\"Germany\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/global\",\"name\":\"global\",\"displayName\":\"Global\",\"regionalDisplayName\":\"Global\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/india\",\"name\":\"india\",\"displayName\":\"India\",\"regionalDisplayName\":\"India\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japan\",\"name\":\"japan\",\"displayName\":\"Japan\",\"regionalDisplayName\":\"Japan\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/korea\",\"name\":\"korea\",\"displayName\":\"Korea\",\"regionalDisplayName\":\"Korea\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norway\",\"name\":\"norway\",\"displayName\":\"Norway\",\"regionalDisplayName\":\"Norway\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/singapore\",\"name\":\"singapore\",\"displayName\":\"Singapore\",\"regionalDisplayName\":\"Singapore\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafrica\",\"name\":\"southafrica\",\"displayName\":\"South + Africa\",\"regionalDisplayName\":\"South Africa\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerland\",\"name\":\"switzerland\",\"displayName\":\"Switzerland\",\"regionalDisplayName\":\"Switzerland\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uae\",\"name\":\"uae\",\"displayName\":\"United + Arab Emirates\",\"regionalDisplayName\":\"United Arab Emirates\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uk\",\"name\":\"uk\",\"displayName\":\"United + Kingdom\",\"regionalDisplayName\":\"United Kingdom\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/unitedstates\",\"name\":\"unitedstates\",\"displayName\":\"United + States\",\"regionalDisplayName\":\"United States\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/unitedstateseuap\",\"name\":\"unitedstateseuap\",\"displayName\":\"United + States EUAP\",\"regionalDisplayName\":\"United States EUAP\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastasiastage\",\"name\":\"eastasiastage\",\"displayName\":\"East + Asia (Stage)\",\"regionalDisplayName\":\"(Asia Pacific) East Asia (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southeastasiastage\",\"name\":\"southeastasiastage\",\"displayName\":\"Southeast + Asia (Stage)\",\"regionalDisplayName\":\"(Asia Pacific) Southeast Asia (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northcentralus\",\"name\":\"northcentralus\",\"displayName\":\"North + Central US\",\"regionalDisplayName\":\"(US) North Central US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\",\"longitude\":\"-87.6278\",\"latitude\":\"41.8819\",\"physicalLocation\":\"Illinois\",\"pairedRegion\":[{\"name\":\"southcentralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southcentralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus\",\"name\":\"westus\",\"displayName\":\"West + US\",\"regionalDisplayName\":\"(US) West US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\",\"longitude\":\"-122.417\",\"latitude\":\"37.783\",\"physicalLocation\":\"California\",\"pairedRegion\":[{\"name\":\"eastus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/jioindiawest\",\"name\":\"jioindiawest\",\"displayName\":\"Jio + India West\",\"regionalDisplayName\":\"(Asia Pacific) Jio India West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"70.05773\",\"latitude\":\"22.470701\",\"physicalLocation\":\"Jamnagar\",\"pairedRegion\":[{\"name\":\"jioindiacentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/jioindiacentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uaenorth\",\"name\":\"uaenorth\",\"displayName\":\"UAE + North\",\"regionalDisplayName\":\"(Middle East) UAE North\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Middle + East\",\"longitude\":\"55.316666\",\"latitude\":\"25.266666\",\"physicalLocation\":\"Dubai\",\"pairedRegion\":[{\"name\":\"uaecentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uaecentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centraluseuap\",\"name\":\"centraluseuap\",\"displayName\":\"Central + US EUAP\",\"regionalDisplayName\":\"(US) Central US EUAP\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\",\"longitude\":\"-93.6208\",\"latitude\":\"41.5908\",\"pairedRegion\":[{\"name\":\"eastus2euap\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2euap\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westcentralus\",\"name\":\"westcentralus\",\"displayName\":\"West + Central US\",\"regionalDisplayName\":\"(US) West Central US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\",\"longitude\":\"-110.234\",\"latitude\":\"40.890\",\"physicalLocation\":\"Wyoming\",\"pairedRegion\":[{\"name\":\"westus2\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus2\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafricawest\",\"name\":\"southafricawest\",\"displayName\":\"South + Africa West\",\"regionalDisplayName\":\"(Africa) South Africa West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Africa\",\"longitude\":\"18.843266\",\"latitude\":\"-34.075691\",\"physicalLocation\":\"Cape + Town\",\"pairedRegion\":[{\"name\":\"southafricanorth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafricanorth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiacentral\",\"name\":\"australiacentral\",\"displayName\":\"Australia + Central\",\"regionalDisplayName\":\"(Asia Pacific) Australia Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"149.1244\",\"latitude\":\"-35.3075\",\"physicalLocation\":\"Canberra\",\"pairedRegion\":[{\"name\":\"australiacentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiacentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiacentral2\",\"name\":\"australiacentral2\",\"displayName\":\"Australia + Central 2\",\"regionalDisplayName\":\"(Asia Pacific) Australia Central 2\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"149.1244\",\"latitude\":\"-35.3075\",\"physicalLocation\":\"Canberra\",\"pairedRegion\":[{\"name\":\"australiacentral2\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiacentral2\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiasoutheast\",\"name\":\"australiasoutheast\",\"displayName\":\"Australia + Southeast\",\"regionalDisplayName\":\"(Asia Pacific) Australia Southeast\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"144.9631\",\"latitude\":\"-37.8136\",\"physicalLocation\":\"Victoria\",\"pairedRegion\":[{\"name\":\"australiaeast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiaeast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japanwest\",\"name\":\"japanwest\",\"displayName\":\"Japan + West\",\"regionalDisplayName\":\"(Asia Pacific) Japan West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"135.5022\",\"latitude\":\"34.6939\",\"physicalLocation\":\"Osaka\",\"pairedRegion\":[{\"name\":\"japaneast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japaneast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/jioindiacentral\",\"name\":\"jioindiacentral\",\"displayName\":\"Jio + India Central\",\"regionalDisplayName\":\"(Asia Pacific) Jio India Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"79.08886\",\"latitude\":\"21.146633\",\"physicalLocation\":\"Nagpur\",\"pairedRegion\":[{\"name\":\"jioindiawest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/jioindiawest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/koreasouth\",\"name\":\"koreasouth\",\"displayName\":\"Korea + South\",\"regionalDisplayName\":\"(Asia Pacific) Korea South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"129.0756\",\"latitude\":\"35.1796\",\"physicalLocation\":\"Busan\",\"pairedRegion\":[{\"name\":\"koreacentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/koreacentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southindia\",\"name\":\"southindia\",\"displayName\":\"South + India\",\"regionalDisplayName\":\"(Asia Pacific) South India\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"80.1636\",\"latitude\":\"12.9822\",\"physicalLocation\":\"Chennai\",\"pairedRegion\":[{\"name\":\"centralindia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralindia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westindia\",\"name\":\"westindia\",\"displayName\":\"West + India\",\"regionalDisplayName\":\"(Asia Pacific) West India\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"72.868\",\"latitude\":\"19.088\",\"physicalLocation\":\"Mumbai\",\"pairedRegion\":[{\"name\":\"southindia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southindia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canadaeast\",\"name\":\"canadaeast\",\"displayName\":\"Canada + East\",\"regionalDisplayName\":\"(Canada) Canada East\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Canada\",\"longitude\":\"-71.217\",\"latitude\":\"46.817\",\"physicalLocation\":\"Quebec\",\"pairedRegion\":[{\"name\":\"canadacentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canadacentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/francesouth\",\"name\":\"francesouth\",\"displayName\":\"France + South\",\"regionalDisplayName\":\"(Europe) France South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"2.1972\",\"latitude\":\"43.8345\",\"physicalLocation\":\"Marseille\",\"pairedRegion\":[{\"name\":\"francecentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/francecentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germanynorth\",\"name\":\"germanynorth\",\"displayName\":\"Germany + North\",\"regionalDisplayName\":\"(Europe) Germany North\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"8.806422\",\"latitude\":\"53.073635\",\"physicalLocation\":\"Berlin\",\"pairedRegion\":[{\"name\":\"germanywestcentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germanywestcentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norwaywest\",\"name\":\"norwaywest\",\"displayName\":\"Norway + West\",\"regionalDisplayName\":\"(Europe) Norway West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"5.733107\",\"latitude\":\"58.969975\",\"physicalLocation\":\"Norway\",\"pairedRegion\":[{\"name\":\"norwayeast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norwayeast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedensouth\",\"name\":\"swedensouth\",\"displayName\":\"Sweden + South\",\"regionalDisplayName\":\"(Europe) Sweden South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"13.0007\",\"latitude\":\"55.6059\",\"physicalLocation\":\"Malmo\",\"pairedRegion\":[{\"name\":\"swedencentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedencentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerlandwest\",\"name\":\"switzerlandwest\",\"displayName\":\"Switzerland + West\",\"regionalDisplayName\":\"(Europe) Switzerland West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"6.143158\",\"latitude\":\"46.204391\",\"physicalLocation\":\"Geneva\",\"pairedRegion\":[{\"name\":\"switzerlandnorth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerlandnorth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/ukwest\",\"name\":\"ukwest\",\"displayName\":\"UK + West\",\"regionalDisplayName\":\"(Europe) UK West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"-3.084\",\"latitude\":\"53.427\",\"physicalLocation\":\"Cardiff\",\"pairedRegion\":[{\"name\":\"uksouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uksouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uaecentral\",\"name\":\"uaecentral\",\"displayName\":\"UAE + Central\",\"regionalDisplayName\":\"(Middle East) UAE Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Middle + East\",\"longitude\":\"54.366669\",\"latitude\":\"24.466667\",\"physicalLocation\":\"Abu + Dhabi\",\"pairedRegion\":[{\"name\":\"uaenorth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uaenorth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/brazilsoutheast\",\"name\":\"brazilsoutheast\",\"displayName\":\"Brazil + Southeast\",\"regionalDisplayName\":\"(South America) Brazil Southeast\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"South + America\",\"longitude\":\"-43.2075\",\"latitude\":\"-22.90278\",\"physicalLocation\":\"Rio\",\"pairedRegion\":[{\"name\":\"brazilsouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/brazilsouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/polandcentral\",\"name\":\"polandcentral\",\"displayName\":\"Poland + Central\",\"regionalDisplayName\":\"(Europe) Poland Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"14.6512702\",\"latitude\":\"51.8685079\",\"physicalLocation\":\"Warsaw\",\"pairedRegion\":[{\"name\":\"swedencentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedencentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/qatarcentral\",\"name\":\"qatarcentral\",\"displayName\":\"Qatar + Central\",\"regionalDisplayName\":\"(Middle East) Qatar Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Middle + East\",\"longitude\":\"51.439327\",\"latitude\":\"25.551462\",\"physicalLocation\":\"Doha\",\"pairedRegion\":[{\"name\":\"westeurope\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westeurope\"}]}}]}" + headers: + cache-control: + - no-cache + content-length: + - '30397' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 07 Jul 2022 06:43:32 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - unknown + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.38.0 azsdk-python-azure-mgmt-resource/21.1.0b1 Python/3.8.10 (Windows-10-10.0.22622-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/locations?api-version=2019-11-01 + response: + body: + string: "{\"value\":[{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus\",\"name\":\"eastus\",\"displayName\":\"East + US\",\"regionalDisplayName\":\"(US) East US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-79.8164\",\"latitude\":\"37.3719\",\"physicalLocation\":\"Virginia\",\"pairedRegion\":[{\"name\":\"westus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2\",\"name\":\"eastus2\",\"displayName\":\"East + US 2\",\"regionalDisplayName\":\"(US) East US 2\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-78.3889\",\"latitude\":\"36.6681\",\"physicalLocation\":\"Virginia\",\"pairedRegion\":[{\"name\":\"centralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southcentralus\",\"name\":\"southcentralus\",\"displayName\":\"South + Central US\",\"regionalDisplayName\":\"(US) South Central US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-98.5\",\"latitude\":\"29.4167\",\"physicalLocation\":\"Texas\",\"pairedRegion\":[{\"name\":\"northcentralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northcentralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus2\",\"name\":\"westus2\",\"displayName\":\"West + US 2\",\"regionalDisplayName\":\"(US) West US 2\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-119.852\",\"latitude\":\"47.233\",\"physicalLocation\":\"Washington\",\"pairedRegion\":[{\"name\":\"westcentralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westcentralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus3\",\"name\":\"westus3\",\"displayName\":\"West + US 3\",\"regionalDisplayName\":\"(US) West US 3\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-112.074036\",\"latitude\":\"33.448376\",\"physicalLocation\":\"Phoenix\",\"pairedRegion\":[{\"name\":\"eastus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiaeast\",\"name\":\"australiaeast\",\"displayName\":\"Australia + East\",\"regionalDisplayName\":\"(Asia Pacific) Australia East\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"151.2094\",\"latitude\":\"-33.86\",\"physicalLocation\":\"New + South Wales\",\"pairedRegion\":[{\"name\":\"australiasoutheast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiasoutheast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southeastasia\",\"name\":\"southeastasia\",\"displayName\":\"Southeast + Asia\",\"regionalDisplayName\":\"(Asia Pacific) Southeast Asia\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"103.833\",\"latitude\":\"1.283\",\"physicalLocation\":\"Singapore\",\"pairedRegion\":[{\"name\":\"eastasia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastasia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northeurope\",\"name\":\"northeurope\",\"displayName\":\"North + Europe\",\"regionalDisplayName\":\"(Europe) North Europe\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"-6.2597\",\"latitude\":\"53.3478\",\"physicalLocation\":\"Ireland\",\"pairedRegion\":[{\"name\":\"westeurope\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westeurope\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedencentral\",\"name\":\"swedencentral\",\"displayName\":\"Sweden + Central\",\"regionalDisplayName\":\"(Europe) Sweden Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"17.14127\",\"latitude\":\"60.67488\",\"physicalLocation\":\"G\xE4vle\",\"pairedRegion\":[{\"name\":\"swedensouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedensouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uksouth\",\"name\":\"uksouth\",\"displayName\":\"UK + South\",\"regionalDisplayName\":\"(Europe) UK South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"-0.799\",\"latitude\":\"50.941\",\"physicalLocation\":\"London\",\"pairedRegion\":[{\"name\":\"ukwest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/ukwest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westeurope\",\"name\":\"westeurope\",\"displayName\":\"West + Europe\",\"regionalDisplayName\":\"(Europe) West Europe\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"4.9\",\"latitude\":\"52.3667\",\"physicalLocation\":\"Netherlands\",\"pairedRegion\":[{\"name\":\"northeurope\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northeurope\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralus\",\"name\":\"centralus\",\"displayName\":\"Central + US\",\"regionalDisplayName\":\"(US) Central US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-93.6208\",\"latitude\":\"41.5908\",\"physicalLocation\":\"Iowa\",\"pairedRegion\":[{\"name\":\"eastus2\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafricanorth\",\"name\":\"southafricanorth\",\"displayName\":\"South + Africa North\",\"regionalDisplayName\":\"(Africa) South Africa North\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Africa\",\"longitude\":\"28.218370\",\"latitude\":\"-25.731340\",\"physicalLocation\":\"Johannesburg\",\"pairedRegion\":[{\"name\":\"southafricawest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafricawest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralindia\",\"name\":\"centralindia\",\"displayName\":\"Central + India\",\"regionalDisplayName\":\"(Asia Pacific) Central India\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"73.9197\",\"latitude\":\"18.5822\",\"physicalLocation\":\"Pune\",\"pairedRegion\":[{\"name\":\"southindia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southindia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastasia\",\"name\":\"eastasia\",\"displayName\":\"East + Asia\",\"regionalDisplayName\":\"(Asia Pacific) East Asia\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"114.188\",\"latitude\":\"22.267\",\"physicalLocation\":\"Hong + Kong\",\"pairedRegion\":[{\"name\":\"southeastasia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southeastasia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japaneast\",\"name\":\"japaneast\",\"displayName\":\"Japan + East\",\"regionalDisplayName\":\"(Asia Pacific) Japan East\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"139.77\",\"latitude\":\"35.68\",\"physicalLocation\":\"Tokyo, + Saitama\",\"pairedRegion\":[{\"name\":\"japanwest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japanwest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/koreacentral\",\"name\":\"koreacentral\",\"displayName\":\"Korea + Central\",\"regionalDisplayName\":\"(Asia Pacific) Korea Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"126.9780\",\"latitude\":\"37.5665\",\"physicalLocation\":\"Seoul\",\"pairedRegion\":[{\"name\":\"koreasouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/koreasouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canadacentral\",\"name\":\"canadacentral\",\"displayName\":\"Canada + Central\",\"regionalDisplayName\":\"(Canada) Canada Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Canada\",\"longitude\":\"-79.383\",\"latitude\":\"43.653\",\"physicalLocation\":\"Toronto\",\"pairedRegion\":[{\"name\":\"canadaeast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canadaeast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/francecentral\",\"name\":\"francecentral\",\"displayName\":\"France + Central\",\"regionalDisplayName\":\"(Europe) France Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"2.3730\",\"latitude\":\"46.3772\",\"physicalLocation\":\"Paris\",\"pairedRegion\":[{\"name\":\"francesouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/francesouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germanywestcentral\",\"name\":\"germanywestcentral\",\"displayName\":\"Germany + West Central\",\"regionalDisplayName\":\"(Europe) Germany West Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"8.682127\",\"latitude\":\"50.110924\",\"physicalLocation\":\"Frankfurt\",\"pairedRegion\":[{\"name\":\"germanynorth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germanynorth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norwayeast\",\"name\":\"norwayeast\",\"displayName\":\"Norway + East\",\"regionalDisplayName\":\"(Europe) Norway East\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"10.752245\",\"latitude\":\"59.913868\",\"physicalLocation\":\"Norway\",\"pairedRegion\":[{\"name\":\"norwaywest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norwaywest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerlandnorth\",\"name\":\"switzerlandnorth\",\"displayName\":\"Switzerland + North\",\"regionalDisplayName\":\"(Europe) Switzerland North\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"Europe\",\"longitude\":\"8.564572\",\"latitude\":\"47.451542\",\"physicalLocation\":\"Zurich\",\"pairedRegion\":[{\"name\":\"switzerlandwest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerlandwest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/brazilsouth\",\"name\":\"brazilsouth\",\"displayName\":\"Brazil + South\",\"regionalDisplayName\":\"(South America) Brazil South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"South + America\",\"longitude\":\"-46.633\",\"latitude\":\"-23.55\",\"physicalLocation\":\"Sao + Paulo State\",\"pairedRegion\":[{\"name\":\"southcentralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southcentralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2euap\",\"name\":\"eastus2euap\",\"displayName\":\"East + US 2 EUAP\",\"regionalDisplayName\":\"(US) East US 2 EUAP\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Recommended\",\"geographyGroup\":\"US\",\"longitude\":\"-78.3889\",\"latitude\":\"36.6681\",\"pairedRegion\":[{\"name\":\"centraluseuap\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centraluseuap\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralusstage\",\"name\":\"centralusstage\",\"displayName\":\"Central + US (Stage)\",\"regionalDisplayName\":\"(US) Central US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastusstage\",\"name\":\"eastusstage\",\"displayName\":\"East + US (Stage)\",\"regionalDisplayName\":\"(US) East US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2stage\",\"name\":\"eastus2stage\",\"displayName\":\"East + US 2 (Stage)\",\"regionalDisplayName\":\"(US) East US 2 (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northcentralusstage\",\"name\":\"northcentralusstage\",\"displayName\":\"North + Central US (Stage)\",\"regionalDisplayName\":\"(US) North Central US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southcentralusstage\",\"name\":\"southcentralusstage\",\"displayName\":\"South + Central US (Stage)\",\"regionalDisplayName\":\"(US) South Central US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westusstage\",\"name\":\"westusstage\",\"displayName\":\"West + US (Stage)\",\"regionalDisplayName\":\"(US) West US (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus2stage\",\"name\":\"westus2stage\",\"displayName\":\"West + US 2 (Stage)\",\"regionalDisplayName\":\"(US) West US 2 (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/asia\",\"name\":\"asia\",\"displayName\":\"Asia\",\"regionalDisplayName\":\"Asia\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/asiapacific\",\"name\":\"asiapacific\",\"displayName\":\"Asia + Pacific\",\"regionalDisplayName\":\"Asia Pacific\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australia\",\"name\":\"australia\",\"displayName\":\"Australia\",\"regionalDisplayName\":\"Australia\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/brazil\",\"name\":\"brazil\",\"displayName\":\"Brazil\",\"regionalDisplayName\":\"Brazil\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canada\",\"name\":\"canada\",\"displayName\":\"Canada\",\"regionalDisplayName\":\"Canada\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/europe\",\"name\":\"europe\",\"displayName\":\"Europe\",\"regionalDisplayName\":\"Europe\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/france\",\"name\":\"france\",\"displayName\":\"France\",\"regionalDisplayName\":\"France\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germany\",\"name\":\"germany\",\"displayName\":\"Germany\",\"regionalDisplayName\":\"Germany\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/global\",\"name\":\"global\",\"displayName\":\"Global\",\"regionalDisplayName\":\"Global\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/india\",\"name\":\"india\",\"displayName\":\"India\",\"regionalDisplayName\":\"India\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japan\",\"name\":\"japan\",\"displayName\":\"Japan\",\"regionalDisplayName\":\"Japan\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/korea\",\"name\":\"korea\",\"displayName\":\"Korea\",\"regionalDisplayName\":\"Korea\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norway\",\"name\":\"norway\",\"displayName\":\"Norway\",\"regionalDisplayName\":\"Norway\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/singapore\",\"name\":\"singapore\",\"displayName\":\"Singapore\",\"regionalDisplayName\":\"Singapore\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafrica\",\"name\":\"southafrica\",\"displayName\":\"South + Africa\",\"regionalDisplayName\":\"South Africa\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerland\",\"name\":\"switzerland\",\"displayName\":\"Switzerland\",\"regionalDisplayName\":\"Switzerland\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uae\",\"name\":\"uae\",\"displayName\":\"United + Arab Emirates\",\"regionalDisplayName\":\"United Arab Emirates\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uk\",\"name\":\"uk\",\"displayName\":\"United + Kingdom\",\"regionalDisplayName\":\"United Kingdom\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/unitedstates\",\"name\":\"unitedstates\",\"displayName\":\"United + States\",\"regionalDisplayName\":\"United States\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/unitedstateseuap\",\"name\":\"unitedstateseuap\",\"displayName\":\"United + States EUAP\",\"regionalDisplayName\":\"United States EUAP\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastasiastage\",\"name\":\"eastasiastage\",\"displayName\":\"East + Asia (Stage)\",\"regionalDisplayName\":\"(Asia Pacific) East Asia (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southeastasiastage\",\"name\":\"southeastasiastage\",\"displayName\":\"Southeast + Asia (Stage)\",\"regionalDisplayName\":\"(Asia Pacific) Southeast Asia (Stage)\",\"metadata\":{\"regionType\":\"Logical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\"}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/northcentralus\",\"name\":\"northcentralus\",\"displayName\":\"North + Central US\",\"regionalDisplayName\":\"(US) North Central US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\",\"longitude\":\"-87.6278\",\"latitude\":\"41.8819\",\"physicalLocation\":\"Illinois\",\"pairedRegion\":[{\"name\":\"southcentralus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southcentralus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus\",\"name\":\"westus\",\"displayName\":\"West + US\",\"regionalDisplayName\":\"(US) West US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\",\"longitude\":\"-122.417\",\"latitude\":\"37.783\",\"physicalLocation\":\"California\",\"pairedRegion\":[{\"name\":\"eastus\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/jioindiawest\",\"name\":\"jioindiawest\",\"displayName\":\"Jio + India West\",\"regionalDisplayName\":\"(Asia Pacific) Jio India West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"70.05773\",\"latitude\":\"22.470701\",\"physicalLocation\":\"Jamnagar\",\"pairedRegion\":[{\"name\":\"jioindiacentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/jioindiacentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uaenorth\",\"name\":\"uaenorth\",\"displayName\":\"UAE + North\",\"regionalDisplayName\":\"(Middle East) UAE North\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Middle + East\",\"longitude\":\"55.316666\",\"latitude\":\"25.266666\",\"physicalLocation\":\"Dubai\",\"pairedRegion\":[{\"name\":\"uaecentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uaecentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centraluseuap\",\"name\":\"centraluseuap\",\"displayName\":\"Central + US EUAP\",\"regionalDisplayName\":\"(US) Central US EUAP\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\",\"longitude\":\"-93.6208\",\"latitude\":\"41.5908\",\"pairedRegion\":[{\"name\":\"eastus2euap\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/eastus2euap\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westcentralus\",\"name\":\"westcentralus\",\"displayName\":\"West + Central US\",\"regionalDisplayName\":\"(US) West Central US\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"US\",\"longitude\":\"-110.234\",\"latitude\":\"40.890\",\"physicalLocation\":\"Wyoming\",\"pairedRegion\":[{\"name\":\"westus2\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westus2\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafricawest\",\"name\":\"southafricawest\",\"displayName\":\"South + Africa West\",\"regionalDisplayName\":\"(Africa) South Africa West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Africa\",\"longitude\":\"18.843266\",\"latitude\":\"-34.075691\",\"physicalLocation\":\"Cape + Town\",\"pairedRegion\":[{\"name\":\"southafricanorth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southafricanorth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiacentral\",\"name\":\"australiacentral\",\"displayName\":\"Australia + Central\",\"regionalDisplayName\":\"(Asia Pacific) Australia Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"149.1244\",\"latitude\":\"-35.3075\",\"physicalLocation\":\"Canberra\",\"pairedRegion\":[{\"name\":\"australiacentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiacentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiacentral2\",\"name\":\"australiacentral2\",\"displayName\":\"Australia + Central 2\",\"regionalDisplayName\":\"(Asia Pacific) Australia Central 2\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"149.1244\",\"latitude\":\"-35.3075\",\"physicalLocation\":\"Canberra\",\"pairedRegion\":[{\"name\":\"australiacentral2\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiacentral2\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiasoutheast\",\"name\":\"australiasoutheast\",\"displayName\":\"Australia + Southeast\",\"regionalDisplayName\":\"(Asia Pacific) Australia Southeast\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"144.9631\",\"latitude\":\"-37.8136\",\"physicalLocation\":\"Victoria\",\"pairedRegion\":[{\"name\":\"australiaeast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/australiaeast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japanwest\",\"name\":\"japanwest\",\"displayName\":\"Japan + West\",\"regionalDisplayName\":\"(Asia Pacific) Japan West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"135.5022\",\"latitude\":\"34.6939\",\"physicalLocation\":\"Osaka\",\"pairedRegion\":[{\"name\":\"japaneast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/japaneast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/jioindiacentral\",\"name\":\"jioindiacentral\",\"displayName\":\"Jio + India Central\",\"regionalDisplayName\":\"(Asia Pacific) Jio India Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"79.08886\",\"latitude\":\"21.146633\",\"physicalLocation\":\"Nagpur\",\"pairedRegion\":[{\"name\":\"jioindiawest\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/jioindiawest\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/koreasouth\",\"name\":\"koreasouth\",\"displayName\":\"Korea + South\",\"regionalDisplayName\":\"(Asia Pacific) Korea South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"129.0756\",\"latitude\":\"35.1796\",\"physicalLocation\":\"Busan\",\"pairedRegion\":[{\"name\":\"koreacentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/koreacentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southindia\",\"name\":\"southindia\",\"displayName\":\"South + India\",\"regionalDisplayName\":\"(Asia Pacific) South India\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"80.1636\",\"latitude\":\"12.9822\",\"physicalLocation\":\"Chennai\",\"pairedRegion\":[{\"name\":\"centralindia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/centralindia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westindia\",\"name\":\"westindia\",\"displayName\":\"West + India\",\"regionalDisplayName\":\"(Asia Pacific) West India\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Asia + Pacific\",\"longitude\":\"72.868\",\"latitude\":\"19.088\",\"physicalLocation\":\"Mumbai\",\"pairedRegion\":[{\"name\":\"southindia\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/southindia\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canadaeast\",\"name\":\"canadaeast\",\"displayName\":\"Canada + East\",\"regionalDisplayName\":\"(Canada) Canada East\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Canada\",\"longitude\":\"-71.217\",\"latitude\":\"46.817\",\"physicalLocation\":\"Quebec\",\"pairedRegion\":[{\"name\":\"canadacentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/canadacentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/francesouth\",\"name\":\"francesouth\",\"displayName\":\"France + South\",\"regionalDisplayName\":\"(Europe) France South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"2.1972\",\"latitude\":\"43.8345\",\"physicalLocation\":\"Marseille\",\"pairedRegion\":[{\"name\":\"francecentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/francecentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germanynorth\",\"name\":\"germanynorth\",\"displayName\":\"Germany + North\",\"regionalDisplayName\":\"(Europe) Germany North\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"8.806422\",\"latitude\":\"53.073635\",\"physicalLocation\":\"Berlin\",\"pairedRegion\":[{\"name\":\"germanywestcentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/germanywestcentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norwaywest\",\"name\":\"norwaywest\",\"displayName\":\"Norway + West\",\"regionalDisplayName\":\"(Europe) Norway West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"5.733107\",\"latitude\":\"58.969975\",\"physicalLocation\":\"Norway\",\"pairedRegion\":[{\"name\":\"norwayeast\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/norwayeast\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedensouth\",\"name\":\"swedensouth\",\"displayName\":\"Sweden + South\",\"regionalDisplayName\":\"(Europe) Sweden South\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"13.0007\",\"latitude\":\"55.6059\",\"physicalLocation\":\"Malmo\",\"pairedRegion\":[{\"name\":\"swedencentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedencentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerlandwest\",\"name\":\"switzerlandwest\",\"displayName\":\"Switzerland + West\",\"regionalDisplayName\":\"(Europe) Switzerland West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"6.143158\",\"latitude\":\"46.204391\",\"physicalLocation\":\"Geneva\",\"pairedRegion\":[{\"name\":\"switzerlandnorth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/switzerlandnorth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/ukwest\",\"name\":\"ukwest\",\"displayName\":\"UK + West\",\"regionalDisplayName\":\"(Europe) UK West\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"-3.084\",\"latitude\":\"53.427\",\"physicalLocation\":\"Cardiff\",\"pairedRegion\":[{\"name\":\"uksouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uksouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uaecentral\",\"name\":\"uaecentral\",\"displayName\":\"UAE + Central\",\"regionalDisplayName\":\"(Middle East) UAE Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Middle + East\",\"longitude\":\"54.366669\",\"latitude\":\"24.466667\",\"physicalLocation\":\"Abu + Dhabi\",\"pairedRegion\":[{\"name\":\"uaenorth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/uaenorth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/brazilsoutheast\",\"name\":\"brazilsoutheast\",\"displayName\":\"Brazil + Southeast\",\"regionalDisplayName\":\"(South America) Brazil Southeast\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"South + America\",\"longitude\":\"-43.2075\",\"latitude\":\"-22.90278\",\"physicalLocation\":\"Rio\",\"pairedRegion\":[{\"name\":\"brazilsouth\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/brazilsouth\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/polandcentral\",\"name\":\"polandcentral\",\"displayName\":\"Poland + Central\",\"regionalDisplayName\":\"(Europe) Poland Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Europe\",\"longitude\":\"14.6512702\",\"latitude\":\"51.8685079\",\"physicalLocation\":\"Warsaw\",\"pairedRegion\":[{\"name\":\"swedencentral\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/swedencentral\"}]}},{\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/qatarcentral\",\"name\":\"qatarcentral\",\"displayName\":\"Qatar + Central\",\"regionalDisplayName\":\"(Middle East) Qatar Central\",\"metadata\":{\"regionType\":\"Physical\",\"regionCategory\":\"Other\",\"geographyGroup\":\"Middle + East\",\"longitude\":\"51.439327\",\"latitude\":\"25.551462\",\"physicalLocation\":\"Doha\",\"pairedRegion\":[{\"name\":\"westeurope\",\"id\":\"/subscriptions/00000000-0000-0000-0000-000000000000/locations/westeurope\"}]}}]}" + headers: + cache-control: + - no-cache + content-length: + - '30397' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 07 Jul 2022 06:43:35 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +version: 1 diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_subscription_id_arg_format.yaml b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_subscription_id_arg_format.yaml new file mode 100644 index 00000000000..92ad2459144 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/test_aaz_subscription_id_arg_format.yaml @@ -0,0 +1,42 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - group show + Connection: + - keep-alive + ParameterSetName: + - -n + User-Agent: + - AZURECLI/2.38.0 azsdk-python-azure-mgmt-resource/21.1.0b1 Python/3.8.10 (Windows-10-10.0.22622-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/test_subscription_id_arg_format000001?api-version=2021-04-01 + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test_subscription_id_arg_format000001","name":"test_subscription_id_arg_format000001","type":"Microsoft.Resources/resourceGroups","location":"westus","tags":{"product":"azurecli","cause":"automation","date":"2022-07-07T06:43:41Z"},"properties":{"provisioningState":"Succeeded"}}' + headers: + cache-control: + - no-cache + content-length: + - '352' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 07 Jul 2022 06:43:45 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +version: 1 diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_aaz_resource_arg_format.py b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_aaz_resource_arg_format.py new file mode 100644 index 00000000000..dbe0bc239e8 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_aaz_resource_arg_format.py @@ -0,0 +1,220 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from azure.cli.testsdk import ScenarioTest, ResourceGroupPreparer + + +class TestAAZResourceArgFmt(ScenarioTest): + """ Scenario tests can not be contained in azure-cli-core, so have to move them here.""" + + def format_arg(self, schema, data): + from azure.cli.core.aaz._command_ctx import AAZCommandCtx + from azure.cli.core.aaz.exceptions import AAZInvalidArgValueError + ctx = AAZCommandCtx( + cli_ctx=self.cli_ctx, schema=schema, + command_args=data + ) + ctx.format_args() + return ctx.args + + @ResourceGroupPreparer(name_prefix='test_resource_location_arg_fmt', location="eastus2") + def test_aaz_resource_location_arg_fmt(self, resource_group): + from azure.cli.core.aaz._arg import AAZArgumentsSchema + from azure.cli.core.aaz.exceptions import AAZInvalidArgValueError + from azure.cli.core.aaz import AAZResourceGroupNameArg, AAZResourceLocationArg, AAZResourceLocationArgFormat, AAZStrArg + + schema = AAZArgumentsSchema() + schema.rg_name = AAZResourceGroupNameArg() + schema.location = AAZResourceLocationArg( + fmt=AAZResourceLocationArgFormat(resource_group_arg='rg_name'), + nullable=True + ) + schema.name = AAZStrArg() + + args = self.format_arg(schema, { + "rg_name": resource_group, + }) + self.assertEqual(args.to_serialized_data(), { + "rg_name": resource_group, + "location": "eastus2" + }) + + args = self.format_arg(schema, { + "rg_name": resource_group, + "location": "westus" + }) + self.assertEqual(args.to_serialized_data(), { + "rg_name": resource_group, + "location": "westus" + }) + + args = self.format_arg(schema, { + "rg_name": resource_group, + "location": "North Central US" + }) + self.assertEqual(args.to_serialized_data(), { + "rg_name": resource_group, + "location": "northcentralus" + }) + + args = self.format_arg(schema, { + "rg_name": resource_group, + "location": None + }) + self.assertEqual(args.to_serialized_data(), { + "rg_name": resource_group, + "location": None + }) + + args = self.format_arg(schema, { + "name": "test" + }) + self.assertEqual(args.to_serialized_data(), { + "name": "test" + }) + + schema = AAZArgumentsSchema() + schema.rg_name = AAZResourceGroupNameArg() + schema.location = AAZResourceLocationArg() + schema.name = AAZStrArg() + + args = self.format_arg(schema, { + "rg_name": resource_group, + "name": "test" + }) + self.assertEqual(args.to_serialized_data(), { + "rg_name": resource_group, + "name": "test" + }) + + args = self.format_arg(schema, { + "rg_name": resource_group, + "location": "eastus", + "name": "test" + }) + self.assertEqual(args.to_serialized_data(), { + "rg_name": resource_group, + "location": "eastus", + "name": "test" + }) + + args = self.format_arg(schema, { + "rg_name": resource_group, + "location": "South Africa North", + "name": "test" + }) + self.assertEqual(args.to_serialized_data(), { + "rg_name": resource_group, + "location": "southafricanorth", + "name": "test" + }) + + @ResourceGroupPreparer(name_prefix='test_resource_id_arg_fmt', location="eastus2") + def test_aaz_resource_id_arg_fmt(self, resource_group): + from azure.cli.core.aaz._arg import AAZArgumentsSchema + from azure.cli.core.aaz.exceptions import AAZInvalidArgValueError + from azure.cli.core.aaz import AAZResourceGroupNameArg, AAZResourceLocationArg, AAZResourceIdArg, AAZStrArg, \ + AAZResourceLocationArgFormat, AAZResourceIdArgFormat, AAZObjectArg, AAZListArg, AAZSubscriptionIdArg, \ + AAZSubscriptionIdArgFormat + from azure.cli.core.commands.client_factory import get_subscription_id + from azure.cli.core._profile import Profile + + # Test AAZResourceIdArg + sub_id = get_subscription_id(cli_ctx=self.cli_ctx) + + schema = AAZArgumentsSchema() + schema.rg_name = AAZResourceGroupNameArg() + schema.location = AAZResourceLocationArg( + fmt=AAZResourceLocationArgFormat(resource_group_arg='rg_name'), + ) + schema.vnet_name = AAZStrArg() + schema.subnet = AAZObjectArg() + schema.subnet.id = AAZResourceIdArg(fmt=AAZResourceIdArgFormat( + template="/subscriptions/{subscription}/resourceGroups/{rg_name}/providers/Microsoft.Network/virtualNetworks/{vnet_name}/locations/{location}/subnets/{}" + )) + + schema.vnets = AAZListArg() + schema.vnets.Element = AAZResourceIdArg(fmt=AAZResourceIdArgFormat( + template="/subscriptions/{subscription}/resourceGroups/{rg_name}/providers/Microsoft.Network/virtualNetworks/{}" + ), nullable=True) + + schema.vm = AAZResourceIdArg(fmt=AAZResourceIdArgFormat( + template="/subscriptions/{}/resourceGroups/{rg_name}/providers/Microsoft.Compute/virtualMachines/{}" + )) + + args = self.format_arg(schema, { + "rg_name": resource_group, + "name": "test", + "vnet_name": "test-vnet", + "subnet": { + "id": "test-subnet" + }, + "vnets": [ + "vnet1", + f"/subscriptions/{sub_id}/resourceGroups/{resource_group}/providers/Microsoft.Network/virtualNetworks/vnet2", + f"/subscRiptions/{sub_id}/reSourcegroupS/{resource_group}/providers/microsoFT.network/Virtualnetworks/vnet3", + None, + ], + }) + self.assertEqual(args.to_serialized_data(), { + "rg_name": resource_group, + "location": "eastus2", + "vnet_name": "test-vnet", + "subnet": { + "id": f"/subscriptions/{sub_id}/resourceGroups/{resource_group}/providers/Microsoft.Network/virtualNetworks/test-vnet/locations/eastus2/subnets/test-subnet" + }, + "vnets": [ + f"/subscriptions/{sub_id}/resourceGroups/{resource_group}/providers/Microsoft.Network/virtualNetworks/vnet1", + f"/subscriptions/{sub_id}/resourceGroups/{resource_group}/providers/Microsoft.Network/virtualNetworks/vnet2", + f"/subscRiptions/{sub_id}/reSourcegroupS/{resource_group}/providers/microsoFT.network/Virtualnetworks/vnet3", + None, + ] + }) + + with self.assertRaises(AAZInvalidArgValueError): + self.format_arg(schema, { + "rg_name": resource_group, + "name": "test", + "vnet_name": "test-vnet", + "subnet": { + "id": f"/subscriptions/{sub_id}/resourceGroups/{resource_group}/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-subnet" + } + }) + + with self.assertRaises(AAZInvalidArgValueError): + self.format_arg(schema, { + "rg_name": resource_group, + "name": "test", + "subnet": { + "id": "test-subnet" + } + }) + + with self.assertRaises(AAZInvalidArgValueError): + self.format_arg(schema, { + "rg_name": resource_group, + "name": "test", + "vm": "test-vm" + }) + + @ResourceGroupPreparer(name_prefix="test_subscription_id_arg_format") + def test_aaz_subscription_id_arg_format(self, resource_group): + from azure.cli.core.aaz._arg import AAZArgumentsSchema + from azure.cli.core.aaz import AAZSubscriptionIdArg, AAZSubscriptionIdArgFormat + from azure.cli.core._profile import Profile + + self.cmd(f"az group show -n {resource_group}") + + sub = Profile(cli_ctx=self.cli_ctx).get_subscription() + + schema = AAZArgumentsSchema() + schema.sub = AAZSubscriptionIdArg() + + args = self.format_arg(schema, { + "sub": sub['name'], + }) + self.assertEqual(args.to_serialized_data(), { + "sub": sub['id'], + })