1# --------------------------------------------------------------------------------------------
2# Copyright (c) Microsoft Corporation. All rights reserved.
3# Licensed under the MIT License. See License.txt in the project root for license information.
4# --------------------------------------------------------------------------------------------
5
6# pylint: disable=too-many-lines
7# pylint: disable=line-too-long
8
9from collections import OrderedDict
10import codecs
11import json
12import os
13import platform
14import re
15import ssl
16import sys
17import uuid
18import base64
19
20from six.moves.urllib.request import urlopen  # pylint: disable=import-error
21from six.moves.urllib.parse import urlparse  # pylint: disable=import-error
22
23from msrestazure.tools import is_valid_resource_id, parse_resource_id
24
25from azure.mgmt.resource.resources.models import GenericResource, DeploymentMode
26
27from azure.cli.core.azclierror import ArgumentUsageError, InvalidArgumentValueError, RequiredArgumentMissingError
28from azure.cli.core.parser import IncorrectUsageError
29from azure.cli.core.util import get_file_json, read_file_content, shell_safe_json_parse, sdk_no_wait
30from azure.cli.core.commands import LongRunningOperation
31from azure.cli.core.commands.arm import raise_subdivision_deployment_error
32from azure.cli.core.commands.client_factory import get_mgmt_service_client, get_subscription_id
33from azure.cli.core.profiles import ResourceType, get_sdk, get_api_version, AZURE_API_PROFILES
34
35from azure.cli.command_modules.resource._client_factory import (
36    _resource_client_factory, _resource_policy_client_factory, _resource_lock_client_factory,
37    _resource_links_client_factory, _resource_deploymentscripts_client_factory, _authorization_management_client, _resource_managedapps_client_factory, _resource_templatespecs_client_factory)
38from azure.cli.command_modules.resource._validators import _parse_lock_id
39
40from azure.core.pipeline.policies import SansIOHTTPPolicy
41
42from knack.log import get_logger
43from knack.prompting import prompt, prompt_pass, prompt_t_f, prompt_choice_list, prompt_int, NoTTYException
44from knack.util import CLIError
45
46from msrest.serialization import Serializer
47
48from ._validators import MSI_LOCAL_ID
49from ._formatters import format_what_if_operation_result
50from ._bicep import (
51    run_bicep_command,
52    is_bicep_file,
53    ensure_bicep_installation,
54    remove_bicep_installation,
55    get_bicep_latest_release_tag,
56    get_bicep_available_release_tags,
57    validate_bicep_target_scope
58)
59
60logger = get_logger(__name__)
61
62RPAAS_APIS = {'microsoft.datadog': '/subscriptions/{subscriptionId}/providers/Microsoft.Datadog/agreements/default?api-version=2020-02-01-preview',
63              'microsoft.confluent': '/subscriptions/{subscriptionId}/providers/Microsoft.Confluent/agreements/default?api-version=2020-03-01-preview'}
64
65
66def _build_resource_id(**kwargs):
67    from msrestazure.tools import resource_id as resource_id_from_dict
68    try:
69        return resource_id_from_dict(**kwargs)
70    except KeyError:
71        return None
72
73
74def _process_parameters(template_param_defs, parameter_lists):  # pylint: disable=too-many-statements
75
76    def _try_parse_json_object(value):
77        try:
78            parsed = _remove_comments_from_json(value, False)
79            return parsed.get('parameters', parsed)
80        except Exception:  # pylint: disable=broad-except
81            return None
82
83    def _try_load_file_object(file_path):
84        try:
85            is_file = os.path.isfile(file_path)
86        except ValueError:
87            return None
88        if is_file is True:
89            try:
90                content = read_file_content(file_path)
91                if not content:
92                    return None
93                parsed = _remove_comments_from_json(content, False, file_path)
94                return parsed.get('parameters', parsed)
95            except Exception as ex:
96                raise CLIError("Failed to parse {} with exception:\n    {}".format(file_path, ex))
97        return None
98
99    def _try_load_uri(uri):
100        if "://" in uri:
101            try:
102                value = _urlretrieve(uri).decode('utf-8')
103                parsed = _remove_comments_from_json(value, False)
104                return parsed.get('parameters', parsed)
105            except Exception:  # pylint: disable=broad-except
106                pass
107        return None
108
109    def _try_parse_key_value_object(template_param_defs, parameters, value):
110        # support situation where empty JSON "{}" is provided
111        if value == '{}' and not parameters:
112            return True
113
114        try:
115            key, value = value.split('=', 1)
116        except ValueError:
117            return False
118
119        param = template_param_defs.get(key, None)
120        if param is None:
121            raise CLIError("unrecognized template parameter '{}'. Allowed parameters: {}"
122                           .format(key, ', '.join(sorted(template_param_defs.keys()))))
123
124        param_type = param.get('type', None)
125        if param_type:
126            param_type = param_type.lower()
127        if param_type in ['object', 'array', 'secureobject']:
128            parameters[key] = {'value': shell_safe_json_parse(value)}
129        elif param_type in ['string', 'securestring']:
130            parameters[key] = {'value': value}
131        elif param_type == 'bool':
132            parameters[key] = {'value': value.lower() == 'true'}
133        elif param_type == 'int':
134            parameters[key] = {'value': int(value)}
135        else:
136            logger.warning("Unrecognized type '%s' for parameter '%s'. Interpretting as string.", param_type, key)
137            parameters[key] = {'value': value}
138
139        return True
140
141    parameters = {}
142    for params in parameter_lists or []:
143        for item in params:
144            param_obj = _try_load_file_object(item)
145            if param_obj is None:
146                param_obj = _try_parse_json_object(item)
147            if param_obj is None:
148                param_obj = _try_load_uri(item)
149            if param_obj is not None:
150                parameters.update(param_obj)
151            elif not _try_parse_key_value_object(template_param_defs, parameters, item):
152                raise CLIError('Unable to parse parameter: {}'.format(item))
153
154    return parameters
155
156
157# pylint: disable=redefined-outer-name
158def _find_missing_parameters(parameters, template):
159    if template is None:
160        return {}
161    template_parameters = template.get('parameters', None)
162    if template_parameters is None:
163        return {}
164
165    missing = OrderedDict()
166    for parameter_name in template_parameters:
167        parameter = template_parameters[parameter_name]
168        if 'defaultValue' in parameter:
169            continue
170        if parameters is not None and parameters.get(parameter_name, None) is not None:
171            continue
172        missing[parameter_name] = parameter
173    return missing
174
175
176def _prompt_for_parameters(missing_parameters, fail_on_no_tty=True):  # pylint: disable=too-many-statements
177
178    prompt_list = missing_parameters.keys() if isinstance(missing_parameters, OrderedDict) \
179        else sorted(missing_parameters)
180    result = OrderedDict()
181    no_tty = False
182    for param_name in prompt_list:
183        param = missing_parameters[param_name]
184        param_type = param.get('type', 'string').lower()
185        description = 'Missing description'
186        metadata = param.get('metadata', None)
187        if metadata is not None:
188            description = metadata.get('description', description)
189        allowed_values = param.get('allowedValues', None)
190
191        prompt_str = "Please provide {} value for '{}' (? for help): ".format(param_type, param_name)
192        while True:
193            if allowed_values is not None:
194                try:
195                    ix = prompt_choice_list(prompt_str, allowed_values, help_string=description)
196                    result[param_name] = allowed_values[ix]
197                except NoTTYException:
198                    result[param_name] = None
199                    no_tty = True
200                break
201            if param_type == 'securestring':
202                try:
203                    value = prompt_pass(prompt_str, help_string=description)
204                except NoTTYException:
205                    value = None
206                    no_tty = True
207                result[param_name] = value
208                break
209            if param_type == 'int':
210                try:
211                    int_value = prompt_int(prompt_str, help_string=description)
212                    result[param_name] = int_value
213                except NoTTYException:
214                    result[param_name] = 0
215                    no_tty = True
216                break
217            if param_type == 'bool':
218                try:
219                    value = prompt_t_f(prompt_str, help_string=description)
220                    result[param_name] = value
221                except NoTTYException:
222                    result[param_name] = False
223                    no_tty = True
224                break
225            if param_type in ['object', 'array']:
226                try:
227                    value = prompt(prompt_str, help_string=description)
228                except NoTTYException:
229                    value = ''
230                    no_tty = True
231
232                if value == '':
233                    value = {} if param_type == 'object' else []
234                else:
235                    try:
236                        value = shell_safe_json_parse(value)
237                    except Exception as ex:  # pylint: disable=broad-except
238                        logger.error(ex)
239                        continue
240                result[param_name] = value
241                break
242
243            try:
244                result[param_name] = prompt(prompt_str, help_string=description)
245            except NoTTYException:
246                result[param_name] = None
247                no_tty = True
248            break
249    if no_tty and fail_on_no_tty:
250        raise NoTTYException
251    return result
252
253
254# pylint: disable=redefined-outer-name
255def _get_missing_parameters(parameters, template, prompt_fn, no_prompt=False):
256    missing = _find_missing_parameters(parameters, template)
257    if missing:
258        if no_prompt is True:
259            logger.warning("Missing input parameters: %s ", ', '.join(sorted(missing.keys())))
260        else:
261            try:
262                prompt_parameters = prompt_fn(missing)
263                for param_name in prompt_parameters:
264                    parameters[param_name] = {
265                        "value": prompt_parameters[param_name]
266                    }
267            except NoTTYException:
268                raise CLIError("Missing input parameters: {}".format(', '.join(sorted(missing.keys()))))
269    return parameters
270
271
272def _ssl_context():
273    if sys.version_info < (3, 4):
274        return ssl.SSLContext(ssl.PROTOCOL_TLSv1)
275
276    return ssl.create_default_context()
277
278
279def _urlretrieve(url):
280    try:
281        req = urlopen(url, context=_ssl_context())
282        return req.read()
283    except Exception:  # pylint: disable=broad-except
284        raise CLIError('Unable to retrieve url {}'.format(url))
285
286
287# pylint: disable=redefined-outer-name
288def _remove_comments_from_json(template, preserve_order=True, file_path=None):
289    from ._json_handler import json_min
290
291    # When commenting at the bottom of all elements in a JSON object, jsmin has a bug that will wrap lines.
292    # It will affect the subsequent multi-line processing logic, so remove those comments in advance here.
293    # Related issue: https://github.com/Azure/azure-cli/issues/11995, the sample is in the additional context of it.
294    template = re.sub(r'(^[\t ]*//[\s\S]*?\n)|(^[\t ]*/\*{1,2}[\s\S]*?\*/)', '', template, flags=re.M)
295
296    # In order to solve the package conflict introduced by jsmin, the jsmin code is referenced into json_min
297    minified = json_min(template)
298    try:
299        return shell_safe_json_parse(minified, preserve_order, strict=False)  # use strict=False to allow multiline strings
300    except CLIError:
301        # Because the processing of removing comments and compression will lead to misplacement of error location,
302        # so the error message should be wrapped.
303        if file_path:
304            raise CLIError("Failed to parse '{}', please check whether it is a valid JSON format".format(file_path))
305        raise CLIError("Failed to parse the JSON data, please check whether it is a valid JSON format")
306
307
308# pylint: disable=too-many-locals, too-many-statements, too-few-public-methods
309def _deploy_arm_template_core_unmodified(cmd, resource_group_name, template_file=None,
310                                         template_uri=None, deployment_name=None, parameters=None,
311                                         mode=None, rollback_on_error=None, validate_only=False, no_wait=False,
312                                         aux_subscriptions=None, aux_tenants=None, no_prompt=False):
313    DeploymentProperties, TemplateLink, OnErrorDeployment = cmd.get_models('DeploymentProperties', 'TemplateLink',
314                                                                           'OnErrorDeployment')
315    template_link = None
316    template_obj = None
317    on_error_deployment = None
318    template_content = None
319    if template_uri:
320        template_link = TemplateLink(uri=template_uri)
321        template_obj = _remove_comments_from_json(_urlretrieve(template_uri).decode('utf-8'), file_path=template_uri)
322    else:
323        template_content = (
324            run_bicep_command(["build", "--stdout", template_file])
325            if is_bicep_file(template_file)
326            else read_file_content(template_file)
327        )
328        template_obj = _remove_comments_from_json(template_content, file_path=template_file)
329
330    if rollback_on_error == '':
331        on_error_deployment = OnErrorDeployment(type='LastSuccessful')
332    elif rollback_on_error:
333        on_error_deployment = OnErrorDeployment(type='SpecificDeployment', deployment_name=rollback_on_error)
334
335    template_param_defs = template_obj.get('parameters', {})
336    template_obj['resources'] = template_obj.get('resources', [])
337    parameters = _process_parameters(template_param_defs, parameters) or {}
338    parameters = _get_missing_parameters(parameters, template_obj, _prompt_for_parameters, no_prompt)
339
340    parameters = json.loads(json.dumps(parameters))
341
342    properties = DeploymentProperties(template=template_content, template_link=template_link,
343                                      parameters=parameters, mode=mode, on_error_deployment=on_error_deployment)
344
345    smc = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES,
346                                  aux_subscriptions=aux_subscriptions, aux_tenants=aux_tenants)
347
348    deployment_client = smc.deployments  # This solves the multi-api for you
349
350    if not template_uri:
351        # pylint: disable=protected-access
352        deployment_client._serialize = JSONSerializer(
353            deployment_client._serialize.dependencies
354        )
355
356        # Plug this as default HTTP pipeline
357        from azure.core.pipeline import Pipeline
358        smc._client._pipeline._impl_policies.append(JsonCTemplatePolicy())
359        # Because JsonCTemplatePolicy needs to be wrapped as _SansIOHTTPPolicyRunner, so a new Pipeline is built
360        smc._client._pipeline = Pipeline(
361            policies=smc._client._pipeline._impl_policies,
362            transport=smc._client._pipeline._transport
363        )
364
365    from azure.core.exceptions import HttpResponseError
366    Deployment = cmd.get_models('Deployment')
367    deployment = Deployment(properties=properties)
368    if cmd.supported_api_version(min_api='2019-10-01', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES):
369        try:
370            validation_poller = deployment_client.begin_validate(resource_group_name, deployment_name, deployment)
371        except HttpResponseError as cx:
372            raise_subdivision_deployment_error(cx.response.internal_response.text, cx.error.code if cx.error else None)
373        validation_result = LongRunningOperation(cmd.cli_ctx)(validation_poller)
374    else:
375        validation_result = deployment_client.validate(resource_group_name, deployment_name, deployment)
376
377    if validation_result and validation_result.error:
378        err_message = _build_preflight_error_message(validation_result.error)
379        raise_subdivision_deployment_error(err_message)
380    if validate_only:
381        return validation_result
382
383    return sdk_no_wait(no_wait, deployment_client.begin_create_or_update, resource_group_name, deployment_name,
384                       deployment)
385
386
387class JsonCTemplate:
388    def __init__(self, template_as_bytes):
389        self.template_as_bytes = template_as_bytes
390
391
392class JSONSerializer(Serializer):
393    def body(self, data, data_type, **kwargs):
394        if data_type in ('Deployment', 'ScopedDeployment', 'DeploymentWhatIf', 'ScopedDeploymentWhatIf'):
395            # Be sure to pass a DeploymentProperties
396            template = data.properties.template
397            if template:
398                data_as_dict = data.serialize()
399                data_as_dict["properties"]["template"] = JsonCTemplate(template)
400
401                return data_as_dict
402        return super(JSONSerializer, self).body(data, data_type, **kwargs)
403
404
405class JsonCTemplatePolicy(SansIOHTTPPolicy):
406
407    def on_request(self, request):
408        http_request = request.http_request
409        if (getattr(http_request, 'data', {}) or {}).get('properties', {}).get('template'):
410            template = http_request.data["properties"]["template"]
411            if not isinstance(template, JsonCTemplate):
412                raise ValueError()
413
414            del http_request.data["properties"]["template"]
415            # templateLink nad template cannot exist at the same time in deployment_dry_run mode
416            if "templateLink" in http_request.data["properties"].keys():
417                del http_request.data["properties"]["templateLink"]
418            partial_request = json.dumps(http_request.data)
419
420            http_request.data = partial_request[:-2] + ", template:" + template.template_as_bytes + r"}}"
421            http_request.data = http_request.data.encode('utf-8')
422
423
424# pylint: disable=unused-argument
425def deploy_arm_template_at_subscription_scope(cmd,
426                                              template_file=None, template_uri=None, parameters=None,
427                                              deployment_name=None, deployment_location=None,
428                                              no_wait=False, handle_extended_json_format=None, no_prompt=False,
429                                              confirm_with_what_if=None, what_if_result_format=None,
430                                              what_if_exclude_change_types=None, template_spec=None, query_string=None,
431                                              what_if=None, proceed_if_no_change=None):
432    if confirm_with_what_if or what_if:
433        what_if_result = _what_if_deploy_arm_template_at_subscription_scope_core(cmd,
434                                                                                 template_file=template_file, template_uri=template_uri,
435                                                                                 parameters=parameters, deployment_name=deployment_name,
436                                                                                 deployment_location=deployment_location,
437                                                                                 result_format=what_if_result_format,
438                                                                                 exclude_change_types=what_if_exclude_change_types,
439                                                                                 no_prompt=no_prompt, template_spec=template_spec, query_string=query_string,
440                                                                                 return_result=True)
441        if what_if:
442            return None
443
444        ChangeType = cmd.get_models('ChangeType')
445        has_change = any(change.change_type not in [ChangeType.no_change, ChangeType.ignore] for change in what_if_result.changes)
446
447        if not proceed_if_no_change or has_change:
448            from knack.prompting import prompt_y_n
449
450            if not prompt_y_n("\nAre you sure you want to execute the deployment?"):
451                return None
452
453    return _deploy_arm_template_at_subscription_scope(cmd=cmd,
454                                                      template_file=template_file, template_uri=template_uri, parameters=parameters,
455                                                      deployment_name=deployment_name, deployment_location=deployment_location,
456                                                      validate_only=False, no_wait=no_wait,
457                                                      no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
458
459
460# pylint: disable=unused-argument
461def validate_arm_template_at_subscription_scope(cmd,
462                                                template_file=None, template_uri=None, parameters=None,
463                                                deployment_name=None, deployment_location=None,
464                                                no_wait=False, handle_extended_json_format=None,
465                                                no_prompt=False, template_spec=None, query_string=None):
466    return _deploy_arm_template_at_subscription_scope(cmd=cmd,
467                                                      template_file=template_file, template_uri=template_uri, parameters=parameters,
468                                                      deployment_name=deployment_name, deployment_location=deployment_location,
469                                                      validate_only=True, no_wait=no_wait,
470                                                      no_prompt=no_prompt, template_spec=template_spec, query_string=query_string,)
471
472
473def _deploy_arm_template_at_subscription_scope(cmd,
474                                               template_file=None, template_uri=None, parameters=None,
475                                               deployment_name=None, deployment_location=None, validate_only=False,
476                                               no_wait=False, no_prompt=False, template_spec=None, query_string=None):
477    deployment_properties = _prepare_deployment_properties_unmodified(cmd, 'subscription', template_file=template_file,
478                                                                      template_uri=template_uri, parameters=parameters,
479                                                                      mode='Incremental',
480                                                                      no_prompt=no_prompt,
481                                                                      template_spec=template_spec, query_string=query_string)
482
483    mgmt_client = _get_deployment_management_client(cmd.cli_ctx, plug_pipeline=(template_uri is None and template_spec is None))
484
485    from azure.core.exceptions import HttpResponseError
486    Deployment = cmd.get_models('Deployment')
487    deployment = Deployment(properties=deployment_properties, location=deployment_location)
488    if cmd.supported_api_version(min_api='2019-10-01', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES):
489        try:
490            validation_poller = mgmt_client.begin_validate_at_subscription_scope(deployment_name, deployment)
491        except HttpResponseError as cx:
492            raise_subdivision_deployment_error(cx.response.internal_response.text, cx.error.code if cx.error else None)
493        validation_result = LongRunningOperation(cmd.cli_ctx)(validation_poller)
494    else:
495        validation_result = mgmt_client.validate_at_subscription_scope(deployment_name, deployment)
496
497    if validation_result and validation_result.error:
498        err_message = _build_preflight_error_message(validation_result.error)
499        raise_subdivision_deployment_error(err_message)
500    if validate_only:
501        return validation_result
502
503    return sdk_no_wait(no_wait, mgmt_client.begin_create_or_update_at_subscription_scope, deployment_name, deployment)
504
505
506# pylint: disable=unused-argument
507def deploy_arm_template_at_resource_group(cmd,
508                                          resource_group_name=None,
509                                          template_file=None, template_uri=None, parameters=None,
510                                          deployment_name=None, mode=None, rollback_on_error=None,
511                                          no_wait=False, handle_extended_json_format=None,
512                                          aux_subscriptions=None, aux_tenants=None, no_prompt=False,
513                                          confirm_with_what_if=None, what_if_result_format=None,
514                                          what_if_exclude_change_types=None, template_spec=None, query_string=None,
515                                          what_if=None, proceed_if_no_change=None):
516    if confirm_with_what_if or what_if:
517        what_if_result = _what_if_deploy_arm_template_at_resource_group_core(cmd,
518                                                                             resource_group_name=resource_group_name,
519                                                                             template_file=template_file, template_uri=template_uri,
520                                                                             parameters=parameters, deployment_name=deployment_name, mode=mode,
521                                                                             aux_tenants=aux_tenants, result_format=what_if_result_format,
522                                                                             exclude_change_types=what_if_exclude_change_types,
523                                                                             no_prompt=no_prompt, template_spec=template_spec, query_string=query_string,
524                                                                             return_result=True)
525        if what_if:
526            return None
527
528        ChangeType = cmd.get_models('ChangeType')
529        has_change = any(change.change_type not in [ChangeType.no_change, ChangeType.ignore] for change in what_if_result.changes)
530
531        if not proceed_if_no_change or has_change:
532            from knack.prompting import prompt_y_n
533
534            if not prompt_y_n("\nAre you sure you want to execute the deployment?"):
535                return None
536
537    return _deploy_arm_template_at_resource_group(cmd=cmd,
538                                                  resource_group_name=resource_group_name,
539                                                  template_file=template_file, template_uri=template_uri, parameters=parameters,
540                                                  deployment_name=deployment_name, mode=mode, rollback_on_error=rollback_on_error,
541                                                  validate_only=False, no_wait=no_wait,
542                                                  aux_subscriptions=aux_subscriptions, aux_tenants=aux_tenants,
543                                                  no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
544
545
546# pylint: disable=unused-argument
547def validate_arm_template_at_resource_group(cmd,
548                                            resource_group_name=None,
549                                            template_file=None, template_uri=None, parameters=None,
550                                            deployment_name=None, mode=None, rollback_on_error=None,
551                                            no_wait=False, handle_extended_json_format=None, no_prompt=False, template_spec=None, query_string=None):
552    return _deploy_arm_template_at_resource_group(cmd,
553                                                  resource_group_name=resource_group_name,
554                                                  template_file=template_file, template_uri=template_uri, parameters=parameters,
555                                                  deployment_name=deployment_name, mode=mode, rollback_on_error=rollback_on_error,
556                                                  validate_only=True, no_wait=no_wait,
557                                                  no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
558
559
560def _deploy_arm_template_at_resource_group(cmd,
561                                           resource_group_name=None,
562                                           template_file=None, template_uri=None, parameters=None,
563                                           deployment_name=None, mode=None, rollback_on_error=None,
564                                           validate_only=False, no_wait=False,
565                                           aux_subscriptions=None, aux_tenants=None, no_prompt=False, template_spec=None, query_string=None):
566    deployment_properties = _prepare_deployment_properties_unmodified(cmd, 'resourceGroup', template_file=template_file,
567                                                                      template_uri=template_uri,
568                                                                      parameters=parameters, mode=mode,
569                                                                      rollback_on_error=rollback_on_error,
570                                                                      no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
571
572    mgmt_client = _get_deployment_management_client(cmd.cli_ctx, aux_subscriptions=aux_subscriptions,
573                                                    aux_tenants=aux_tenants, plug_pipeline=(template_uri is None and template_spec is None))
574
575    from azure.core.exceptions import HttpResponseError
576    Deployment = cmd.get_models('Deployment')
577    deployment = Deployment(properties=deployment_properties)
578    if cmd.supported_api_version(min_api='2019-10-01', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES):
579        try:
580            validation_poller = mgmt_client.begin_validate(resource_group_name, deployment_name, deployment)
581        except HttpResponseError as cx:
582            raise_subdivision_deployment_error(cx.response.internal_response.text, cx.error.code if cx.error else None)
583        validation_result = LongRunningOperation(cmd.cli_ctx)(validation_poller)
584    else:
585        validation_result = mgmt_client.validate(resource_group_name, deployment_name, deployment)
586
587    if validation_result and validation_result.error:
588        err_message = _build_preflight_error_message(validation_result.error)
589        raise_subdivision_deployment_error(err_message)
590    if validate_only:
591        return validation_result
592
593    return sdk_no_wait(no_wait, mgmt_client.begin_create_or_update, resource_group_name, deployment_name, deployment)
594
595
596# pylint: disable=unused-argument
597def deploy_arm_template_at_management_group(cmd,
598                                            management_group_id=None,
599                                            template_file=None, template_uri=None, parameters=None,
600                                            deployment_name=None, deployment_location=None,
601                                            no_wait=False, handle_extended_json_format=None, no_prompt=False,
602                                            confirm_with_what_if=None, what_if_result_format=None,
603                                            what_if_exclude_change_types=None, template_spec=None, query_string=None,
604                                            what_if=None, proceed_if_no_change=None):
605    if confirm_with_what_if or what_if:
606        what_if_result = _what_if_deploy_arm_template_at_management_group_core(cmd,
607                                                                               management_group_id=management_group_id,
608                                                                               template_file=template_file, template_uri=template_uri,
609                                                                               parameters=parameters, deployment_name=deployment_name,
610                                                                               deployment_location=deployment_location,
611                                                                               result_format=what_if_result_format,
612                                                                               exclude_change_types=what_if_exclude_change_types,
613                                                                               no_prompt=no_prompt, template_spec=template_spec, query_string=query_string,
614                                                                               return_result=True)
615        if what_if:
616            return None
617
618        ChangeType = cmd.get_models('ChangeType')
619        has_change = any(change.change_type not in [ChangeType.no_change, ChangeType.ignore] for change in what_if_result.changes)
620
621        if not proceed_if_no_change or has_change:
622            from knack.prompting import prompt_y_n
623
624            if not prompt_y_n("\nAre you sure you want to execute the deployment?"):
625                return None
626
627    return _deploy_arm_template_at_management_group(cmd=cmd,
628                                                    management_group_id=management_group_id,
629                                                    template_file=template_file, template_uri=template_uri, parameters=parameters,
630                                                    deployment_name=deployment_name, deployment_location=deployment_location,
631                                                    validate_only=False, no_wait=no_wait,
632                                                    no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
633
634
635# pylint: disable=unused-argument
636def validate_arm_template_at_management_group(cmd,
637                                              management_group_id=None,
638                                              template_file=None, template_uri=None, parameters=None,
639                                              deployment_name=None, deployment_location=None,
640                                              no_wait=False, handle_extended_json_format=None,
641                                              no_prompt=False, template_spec=None, query_string=None):
642    return _deploy_arm_template_at_management_group(cmd=cmd,
643                                                    management_group_id=management_group_id,
644                                                    template_file=template_file, template_uri=template_uri, parameters=parameters,
645                                                    deployment_name=deployment_name, deployment_location=deployment_location,
646                                                    validate_only=True, no_wait=no_wait,
647                                                    no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
648
649
650def _deploy_arm_template_at_management_group(cmd,
651                                             management_group_id=None,
652                                             template_file=None, template_uri=None, parameters=None,
653                                             deployment_name=None, deployment_location=None, validate_only=False,
654                                             no_wait=False, no_prompt=False, template_spec=None, query_string=None):
655    deployment_properties = _prepare_deployment_properties_unmodified(cmd, 'managementGroup', template_file=template_file,
656                                                                      template_uri=template_uri,
657                                                                      parameters=parameters, mode='Incremental',
658                                                                      no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
659
660    mgmt_client = _get_deployment_management_client(cmd.cli_ctx, plug_pipeline=(template_uri is None and template_spec is None))
661
662    from azure.core.exceptions import HttpResponseError
663    ScopedDeployment = cmd.get_models('ScopedDeployment')
664    deployment = ScopedDeployment(properties=deployment_properties, location=deployment_location)
665    if cmd.supported_api_version(min_api='2019-10-01', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES):
666        try:
667            validation_poller = mgmt_client.begin_validate_at_management_group_scope(management_group_id,
668                                                                                     deployment_name, deployment)
669        except HttpResponseError as cx:
670            raise_subdivision_deployment_error(cx.response.internal_response.text, cx.error.code if cx.error else None)
671        validation_result = LongRunningOperation(cmd.cli_ctx)(validation_poller)
672    else:
673        validation_result = mgmt_client.validate_at_management_group_scope(management_group_id, deployment_name,
674                                                                           deployment)
675
676    if validation_result and validation_result.error:
677        err_message = _build_preflight_error_message(validation_result.error)
678        raise_subdivision_deployment_error(err_message)
679    if validate_only:
680        return validation_result
681
682    return sdk_no_wait(no_wait, mgmt_client.begin_create_or_update_at_management_group_scope, management_group_id,
683                       deployment_name, deployment)
684
685
686# pylint: disable=unused-argument
687def deploy_arm_template_at_tenant_scope(cmd,
688                                        template_file=None, template_uri=None, parameters=None,
689                                        deployment_name=None, deployment_location=None,
690                                        no_wait=False, handle_extended_json_format=None, no_prompt=False,
691                                        confirm_with_what_if=None, what_if_result_format=None,
692                                        what_if_exclude_change_types=None, template_spec=None, query_string=None,
693                                        what_if=None, proceed_if_no_change=None):
694    if confirm_with_what_if or what_if:
695        what_if_result = _what_if_deploy_arm_template_at_tenant_scope_core(cmd,
696                                                                           template_file=template_file, template_uri=template_uri,
697                                                                           parameters=parameters, deployment_name=deployment_name,
698                                                                           deployment_location=deployment_location,
699                                                                           result_format=what_if_result_format,
700                                                                           exclude_change_types=what_if_exclude_change_types,
701                                                                           no_prompt=no_prompt, template_spec=template_spec, query_string=query_string,
702                                                                           return_result=True)
703        if what_if:
704            return None
705
706        ChangeType = cmd.get_models('ChangeType')
707        has_change = any(change.change_type not in [ChangeType.no_change, ChangeType.ignore] for change in what_if_result.changes)
708
709        if not proceed_if_no_change or has_change:
710            from knack.prompting import prompt_y_n
711
712            if not prompt_y_n("\nAre you sure you want to execute the deployment?"):
713                return None
714
715    return _deploy_arm_template_at_tenant_scope(cmd=cmd,
716                                                template_file=template_file, template_uri=template_uri, parameters=parameters,
717                                                deployment_name=deployment_name, deployment_location=deployment_location,
718                                                validate_only=False, no_wait=no_wait,
719                                                no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
720
721
722# pylint: disable=unused-argument
723def validate_arm_template_at_tenant_scope(cmd,
724                                          template_file=None, template_uri=None, parameters=None,
725                                          deployment_name=None, deployment_location=None,
726                                          no_wait=False, handle_extended_json_format=None, no_prompt=False, template_spec=None, query_string=None):
727    return _deploy_arm_template_at_tenant_scope(cmd=cmd,
728                                                template_file=template_file, template_uri=template_uri, parameters=parameters,
729                                                deployment_name=deployment_name, deployment_location=deployment_location,
730                                                validate_only=True, no_wait=no_wait,
731                                                no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
732
733
734def _deploy_arm_template_at_tenant_scope(cmd,
735                                         template_file=None, template_uri=None, parameters=None,
736                                         deployment_name=None, deployment_location=None, validate_only=False,
737                                         no_wait=False, no_prompt=False, template_spec=None, query_string=None):
738    deployment_properties = _prepare_deployment_properties_unmodified(cmd, 'tenant', template_file=template_file,
739                                                                      template_uri=template_uri,
740                                                                      parameters=parameters, mode='Incremental',
741                                                                      no_prompt=no_prompt, template_spec=template_spec, query_string=query_string,)
742
743    mgmt_client = _get_deployment_management_client(cmd.cli_ctx, plug_pipeline=(template_uri is None and template_spec is None))
744
745    from azure.core.exceptions import HttpResponseError
746    ScopedDeployment = cmd.get_models('ScopedDeployment')
747    deployment = ScopedDeployment(properties=deployment_properties, location=deployment_location)
748    if cmd.supported_api_version(min_api='2019-10-01', resource_type=ResourceType.MGMT_RESOURCE_RESOURCES):
749        try:
750            validation_poller = mgmt_client.begin_validate_at_tenant_scope(deployment_name=deployment_name,
751                                                                           parameters=deployment)
752        except HttpResponseError as cx:
753            raise_subdivision_deployment_error(cx.response.internal_response.text, cx.error.code if cx.error else None)
754        validation_result = LongRunningOperation(cmd.cli_ctx)(validation_poller)
755    else:
756        validation_result = mgmt_client.validate_at_tenant_scope(deployment_name=deployment_name,
757                                                                 parameters=deployment)
758
759    if validation_result and validation_result.error:
760        err_message = _build_preflight_error_message(validation_result.error)
761        raise_subdivision_deployment_error(err_message)
762    if validate_only:
763        return validation_result
764
765    return sdk_no_wait(no_wait, mgmt_client.begin_create_or_update_at_tenant_scope, deployment_name, deployment)
766
767
768def what_if_deploy_arm_template_at_resource_group(cmd, resource_group_name,
769                                                  template_file=None, template_uri=None, parameters=None,
770                                                  deployment_name=None, mode=DeploymentMode.incremental,
771                                                  aux_tenants=None, result_format=None,
772                                                  no_pretty_print=None, no_prompt=False,
773                                                  exclude_change_types=None, template_spec=None, query_string=None):
774    return _what_if_deploy_arm_template_at_resource_group_core(cmd, resource_group_name,
775                                                               template_file, template_uri, parameters,
776                                                               deployment_name, DeploymentMode.incremental,
777                                                               aux_tenants, result_format,
778                                                               no_pretty_print, no_prompt,
779                                                               exclude_change_types, template_spec, query_string)
780
781
782def _what_if_deploy_arm_template_at_resource_group_core(cmd, resource_group_name,
783                                                        template_file=None, template_uri=None, parameters=None,
784                                                        deployment_name=None, mode=DeploymentMode.incremental,
785                                                        aux_tenants=None, result_format=None,
786                                                        no_pretty_print=None, no_prompt=False,
787                                                        exclude_change_types=None, template_spec=None, query_string=None,
788                                                        return_result=None):
789    what_if_properties = _prepare_deployment_what_if_properties(cmd, 'resourceGroup', template_file, template_uri,
790                                                                parameters, mode, result_format, no_prompt, template_spec, query_string)
791    mgmt_client = _get_deployment_management_client(cmd.cli_ctx, aux_tenants=aux_tenants,
792                                                    plug_pipeline=(template_uri is None and template_spec is None))
793    DeploymentWhatIf = cmd.get_models('DeploymentWhatIf')
794    deployment_what_if = DeploymentWhatIf(properties=what_if_properties)
795    what_if_poller = mgmt_client.begin_what_if(resource_group_name, deployment_name,
796                                               parameters=deployment_what_if)
797    what_if_result = _what_if_deploy_arm_template_core(cmd.cli_ctx, what_if_poller, no_pretty_print, exclude_change_types)
798
799    return what_if_result if no_pretty_print or return_result else None
800
801
802def what_if_deploy_arm_template_at_subscription_scope(cmd,
803                                                      template_file=None, template_uri=None, parameters=None,
804                                                      deployment_name=None, deployment_location=None,
805                                                      result_format=None, no_pretty_print=None, no_prompt=False,
806                                                      exclude_change_types=None, template_spec=None, query_string=None):
807    return _what_if_deploy_arm_template_at_subscription_scope_core(cmd,
808                                                                   template_file, template_uri, parameters,
809                                                                   deployment_name, deployment_location,
810                                                                   result_format, no_pretty_print, no_prompt,
811                                                                   exclude_change_types, template_spec, query_string)
812
813
814def _what_if_deploy_arm_template_at_subscription_scope_core(cmd,
815                                                            template_file=None, template_uri=None, parameters=None,
816                                                            deployment_name=None, deployment_location=None,
817                                                            result_format=None, no_pretty_print=None, no_prompt=False,
818                                                            exclude_change_types=None, template_spec=None, query_string=None,
819                                                            return_result=None):
820    what_if_properties = _prepare_deployment_what_if_properties(cmd, 'subscription', template_file, template_uri, parameters,
821                                                                DeploymentMode.incremental, result_format, no_prompt, template_spec, query_string)
822    mgmt_client = _get_deployment_management_client(cmd.cli_ctx, plug_pipeline=(template_uri is None and template_spec is None))
823    ScopedDeploymentWhatIf = cmd.get_models('ScopedDeploymentWhatIf')
824    scoped_deployment_what_if = ScopedDeploymentWhatIf(location=deployment_location, properties=what_if_properties)
825    what_if_poller = mgmt_client.begin_what_if_at_subscription_scope(deployment_name,
826                                                                     parameters=scoped_deployment_what_if)
827    what_if_result = _what_if_deploy_arm_template_core(cmd.cli_ctx, what_if_poller, no_pretty_print, exclude_change_types)
828
829    return what_if_result if no_pretty_print or return_result else None
830
831
832def what_if_deploy_arm_template_at_management_group(cmd, management_group_id=None,
833                                                    template_file=None, template_uri=None, parameters=None,
834                                                    deployment_name=None, deployment_location=None,
835                                                    result_format=None, no_pretty_print=None, no_prompt=False,
836                                                    exclude_change_types=None, template_spec=None, query_string=None):
837    return _what_if_deploy_arm_template_at_management_group_core(cmd, management_group_id,
838                                                                 template_file, template_uri, parameters,
839                                                                 deployment_name, deployment_location,
840                                                                 result_format, no_pretty_print, no_prompt,
841                                                                 exclude_change_types, template_spec, query_string)
842
843
844def _what_if_deploy_arm_template_at_management_group_core(cmd, management_group_id=None,
845                                                          template_file=None, template_uri=None, parameters=None,
846                                                          deployment_name=None, deployment_location=None,
847                                                          result_format=None, no_pretty_print=None, no_prompt=False,
848                                                          exclude_change_types=None, template_spec=None, query_string=None,
849                                                          return_result=None):
850    what_if_properties = _prepare_deployment_what_if_properties(cmd, 'managementGroup', template_file, template_uri, parameters,
851                                                                DeploymentMode.incremental, result_format, no_prompt, template_spec=template_spec, query_string=query_string)
852    mgmt_client = _get_deployment_management_client(cmd.cli_ctx, plug_pipeline=(template_uri is None and template_spec is None))
853    ScopedDeploymentWhatIf = cmd.get_models('ScopedDeploymentWhatIf')
854    scoped_deployment_what_if = ScopedDeploymentWhatIf(location=deployment_location, properties=what_if_properties)
855    what_if_poller = mgmt_client.begin_what_if_at_management_group_scope(management_group_id, deployment_name,
856                                                                         parameters=scoped_deployment_what_if)
857    what_if_result = _what_if_deploy_arm_template_core(cmd.cli_ctx, what_if_poller, no_pretty_print, exclude_change_types)
858
859    return what_if_result if no_pretty_print or return_result else None
860
861
862def what_if_deploy_arm_template_at_tenant_scope(cmd,
863                                                template_file=None, template_uri=None, parameters=None,
864                                                deployment_name=None, deployment_location=None,
865                                                result_format=None, no_pretty_print=None, no_prompt=False,
866                                                exclude_change_types=None, template_spec=None, query_string=None):
867    return _what_if_deploy_arm_template_at_tenant_scope_core(cmd,
868                                                             template_file, template_uri, parameters,
869                                                             deployment_name, deployment_location,
870                                                             result_format, no_pretty_print, no_prompt,
871                                                             exclude_change_types, template_spec, query_string)
872
873
874def _what_if_deploy_arm_template_at_tenant_scope_core(cmd,
875                                                      template_file=None, template_uri=None, parameters=None,
876                                                      deployment_name=None, deployment_location=None,
877                                                      result_format=None, no_pretty_print=None, no_prompt=False,
878                                                      exclude_change_types=None, template_spec=None, query_string=None,
879                                                      return_result=None):
880    what_if_properties = _prepare_deployment_what_if_properties(cmd, 'tenant', template_file, template_uri, parameters,
881                                                                DeploymentMode.incremental, result_format, no_prompt, template_spec, query_string)
882    mgmt_client = _get_deployment_management_client(cmd.cli_ctx, plug_pipeline=(template_uri is None and template_spec is None))
883    ScopedDeploymentWhatIf = cmd.get_models('ScopedDeploymentWhatIf')
884    scoped_deployment_what_if = ScopedDeploymentWhatIf(location=deployment_location, properties=what_if_properties)
885    what_if_poller = mgmt_client.begin_what_if_at_tenant_scope(deployment_name, parameters=scoped_deployment_what_if)
886    what_if_result = _what_if_deploy_arm_template_core(cmd.cli_ctx, what_if_poller, no_pretty_print, exclude_change_types)
887
888    return what_if_result if no_pretty_print or return_result else None
889
890
891def _what_if_deploy_arm_template_core(cli_ctx, what_if_poller, no_pretty_print, exclude_change_types):
892    what_if_result = LongRunningOperation(cli_ctx)(what_if_poller)
893
894    if what_if_result.error:
895        # The status code is 200 even when there's an error, because
896        # it is technically a successful What-If operation. The error
897        # is on the ARM template but not the operation.
898        err_message = _build_preflight_error_message(what_if_result.error)
899        raise CLIError(err_message)
900
901    if exclude_change_types:
902        exclude_change_types = set(map(lambda x: x.lower(), exclude_change_types))
903        what_if_result.changes = list(
904            filter(lambda x: x.change_type.lower() not in exclude_change_types, what_if_result.changes)
905        )
906
907    if no_pretty_print:
908        return what_if_result
909
910    try:
911        if cli_ctx.enable_color:
912            # Disabling colorama since it will silently strip out the Xterm 256 color codes the What-If formatter
913            # is using. Unfortunately, the colors that colorama supports are very limited, which doesn't meet our needs.
914            from colorama import deinit
915            deinit()
916
917            # Enable virtual terminal mode for Windows console so it processes color codes.
918            if platform.system() == "Windows":
919                from ._win_vt import enable_vt_mode
920                enable_vt_mode()
921
922        print(format_what_if_operation_result(what_if_result, cli_ctx.enable_color))
923    finally:
924        if cli_ctx.enable_color:
925            from colorama import init
926            init()
927
928    return what_if_result
929
930
931def _build_preflight_error_message(preflight_error):
932    err_messages = [f'{preflight_error.code} - {preflight_error.message}']
933    for detail in preflight_error.details or []:
934        err_messages.append(_build_preflight_error_message(detail))
935    return '\n'.join(err_messages)
936
937
938def _prepare_template_uri_with_query_string(template_uri, input_query_string):
939    from six.moves.urllib.parse import urlencode, parse_qs, urlsplit, urlunsplit  # pylint: disable=import-error
940
941    try:
942        scheme, netloc, path, query_string, fragment = urlsplit(template_uri)  # pylint: disable=unused-variable
943        query_params = parse_qs(input_query_string)
944        new_query_string = urlencode(query_params, doseq=True)
945
946        return urlunsplit((scheme, netloc, path, new_query_string, fragment))
947    except Exception:  # pylint: disable=broad-except
948        raise InvalidArgumentValueError('Unable to parse parameter: {} .Make sure the value is formed correctly.'.format(input_query_string))
949
950
951def _prepare_deployment_properties_unmodified(cmd, deployment_scope, template_file=None, template_uri=None, parameters=None,
952                                              mode=None, rollback_on_error=None, no_prompt=False, template_spec=None, query_string=None):
953    cli_ctx = cmd.cli_ctx
954    DeploymentProperties, TemplateLink, OnErrorDeployment = get_sdk(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES,
955                                                                    'DeploymentProperties', 'TemplateLink',
956                                                                    'OnErrorDeployment', mod='models')
957    template_link = None
958    template_obj = None
959    on_error_deployment = None
960    template_content = None
961
962    if query_string and not template_uri:
963        raise IncorrectUsageError('please provide --template-uri if --query-string is specified')
964
965    if template_uri:
966        if query_string:
967            template_link = TemplateLink(uri=template_uri, query_string=query_string)
968            template_uri = _prepare_template_uri_with_query_string(template_uri=template_uri, input_query_string=query_string)
969        else:
970            template_link = TemplateLink(uri=template_uri)
971        template_obj = _remove_comments_from_json(_urlretrieve(template_uri).decode('utf-8'), file_path=template_uri)
972    elif template_spec:
973        template_link = TemplateLink(id=template_spec, mode="Incremental")
974        # The api-version for ResourceType.MGMT_RESOURCE_RESOURCES may get updated and point to another (newer) version of the api version for
975        # ResourceType.MGMT_RESOURCE_TEMPLATESPECS than our designated version. This ensures the api-version of all the rest requests for
976        # template_spec are consistent in the same profile:
977        api_version = get_api_version(cli_ctx, ResourceType.MGMT_RESOURCE_TEMPLATESPECS)
978        template_obj = show_resource(cmd=cmd, resource_ids=[template_spec], api_version=api_version).properties['mainTemplate']
979    else:
980        template_content = (
981            run_bicep_command(["build", "--stdout", template_file])
982            if is_bicep_file(template_file)
983            else read_file_content(template_file)
984        )
985        template_obj = _remove_comments_from_json(template_content, file_path=template_file)
986
987        if is_bicep_file(template_file):
988            template_schema = template_obj.get('$schema', '')
989            validate_bicep_target_scope(template_schema, deployment_scope)
990
991    if rollback_on_error == '':
992        on_error_deployment = OnErrorDeployment(type='LastSuccessful')
993    elif rollback_on_error:
994        on_error_deployment = OnErrorDeployment(type='SpecificDeployment', deployment_name=rollback_on_error)
995
996    template_param_defs = template_obj.get('parameters', {})
997    template_obj['resources'] = template_obj.get('resources', [])
998    parameters = _process_parameters(template_param_defs, parameters) or {}
999    parameters = _get_missing_parameters(parameters, template_obj, _prompt_for_parameters, no_prompt)
1000    parameters = json.loads(json.dumps(parameters))
1001
1002    properties = DeploymentProperties(template=template_content, template_link=template_link,
1003                                      parameters=parameters, mode=mode, on_error_deployment=on_error_deployment)
1004    return properties
1005
1006
1007def _prepare_deployment_what_if_properties(cmd, deployment_scope, template_file, template_uri, parameters,
1008                                           mode, result_format, no_prompt, template_spec, query_string):
1009    DeploymentWhatIfProperties, DeploymentWhatIfSettings = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES,
1010                                                                   'DeploymentWhatIfProperties', 'DeploymentWhatIfSettings',
1011                                                                   mod='models')
1012
1013    deployment_properties = _prepare_deployment_properties_unmodified(cmd, deployment_scope, template_file=template_file, template_uri=template_uri,
1014                                                                      parameters=parameters, mode=mode, no_prompt=no_prompt, template_spec=template_spec, query_string=query_string)
1015    deployment_what_if_properties = DeploymentWhatIfProperties(template=deployment_properties.template, template_link=deployment_properties.template_link,
1016                                                               parameters=deployment_properties.parameters, mode=deployment_properties.mode,
1017                                                               what_if_settings=DeploymentWhatIfSettings(result_format=result_format))
1018
1019    return deployment_what_if_properties
1020
1021
1022# pylint: disable=protected-access
1023def _get_deployment_management_client(cli_ctx, aux_subscriptions=None, aux_tenants=None, plug_pipeline=True):
1024
1025    smc = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES,
1026                                  aux_subscriptions=aux_subscriptions, aux_tenants=aux_tenants)
1027
1028    deployment_client = smc.deployments  # This solves the multi-api for you
1029
1030    if not plug_pipeline:
1031        return deployment_client
1032
1033    deployment_client._serialize = JSONSerializer(
1034        deployment_client._serialize.dependencies
1035    )
1036
1037    # Plug this as default HTTP pipeline
1038    from azure.core.pipeline import Pipeline
1039    smc._client._pipeline._impl_policies.append(JsonCTemplatePolicy())
1040    # Because JsonCTemplatePolicy needs to be wrapped as _SansIOHTTPPolicyRunner, so a new Pipeline is built
1041    smc._client._pipeline = Pipeline(
1042        policies=smc._client._pipeline._impl_policies,
1043        transport=smc._client._pipeline._transport
1044    )
1045
1046    return deployment_client
1047
1048
1049def _list_resources_odata_filter_builder(resource_group_name=None, resource_provider_namespace=None,
1050                                         resource_type=None, name=None, tag=None, location=None):
1051    """Build up OData filter string from parameters """
1052    if tag is not None:
1053        if resource_group_name:
1054            raise IncorrectUsageError('you cannot use \'--tag\' with \'--resource-group\''
1055                                      '(If the default value for resource group is set, please use \'az configure --defaults group=""\' command to clear it first)')
1056        if resource_provider_namespace:
1057            raise IncorrectUsageError('you cannot use \'--tag\' with \'--namespace\'')
1058        if resource_type:
1059            raise IncorrectUsageError('you cannot use \'--tag\' with \'--resource-type\'')
1060        if name:
1061            raise IncorrectUsageError('you cannot use \'--tag\' with \'--name\'')
1062        if location:
1063            raise IncorrectUsageError('you cannot use \'--tag\' with \'--location\''
1064                                      '(If the default value for location is set, please use \'az configure --defaults location=""\' command to clear it first)')
1065
1066    filters = []
1067
1068    if resource_group_name:
1069        filters.append("resourceGroup eq '{}'".format(resource_group_name))
1070
1071    if name:
1072        filters.append("name eq '{}'".format(name))
1073
1074    if location:
1075        filters.append("location eq '{}'".format(location))
1076
1077    if resource_type:
1078        if resource_provider_namespace:
1079            f = "'{}/{}'".format(resource_provider_namespace, resource_type)
1080        else:
1081            if not re.match('[^/]+/[^/]+', resource_type):
1082                raise CLIError(
1083                    'Malformed resource-type: '
1084                    '--resource-type=<namespace>/<resource-type> expected.')
1085            # assume resource_type is <namespace>/<type>. The worst is to get a server error
1086            f = "'{}'".format(resource_type)
1087        filters.append("resourceType eq " + f)
1088    else:
1089        if resource_provider_namespace:
1090            raise CLIError('--namespace also requires --resource-type')
1091
1092    if tag:
1093        tag_name = list(tag.keys())[0] if isinstance(tag, dict) else tag
1094        tag_value = tag[tag_name] if isinstance(tag, dict) else ''
1095        if tag_name:
1096            if tag_name[-1] == '*':
1097                filters.append("startswith(tagname, '%s')" % tag_name[0:-1])
1098            else:
1099                filters.append("tagname eq '%s'" % tag_name)
1100                if tag_value != '':
1101                    filters.append("tagvalue eq '%s'" % tag_value)
1102    return ' and '.join(filters)
1103
1104
1105def _get_auth_provider_latest_api_version(cli_ctx):
1106    rcf = _resource_client_factory(cli_ctx)
1107    api_version = _ResourceUtils.resolve_api_version(rcf, 'Microsoft.Authorization', None, 'providerOperations')
1108    return api_version
1109
1110
1111def _update_provider(cmd, namespace, registering, wait, properties=None, mg_id=None, accept_terms=None):
1112    import time
1113    target_state = 'Registered' if registering else 'Unregistered'
1114    rcf = _resource_client_factory(cmd.cli_ctx)
1115    is_rpaas = namespace.lower() in RPAAS_APIS
1116    if mg_id is None and registering:
1117        if is_rpaas and accept_terms:
1118            wait = True
1119        if cmd.supported_api_version(min_api='2021-04-01'):
1120            r = rcf.providers.register(namespace, properties=properties)
1121        else:
1122            r = rcf.providers.register(namespace)
1123    elif mg_id and registering:
1124        r = rcf.providers.register_at_management_group_scope(namespace, mg_id)
1125        if r is None:
1126            return
1127    else:
1128        r = rcf.providers.unregister(namespace)
1129
1130    if r.registration_state == target_state:
1131        return
1132
1133    if wait:
1134        while True:
1135            time.sleep(10)
1136            rp_info = rcf.providers.get(namespace)
1137            if rp_info.registration_state == target_state:
1138                break
1139        if is_rpaas and accept_terms and registering and mg_id is None:
1140            # call accept term API
1141            from azure.cli.core.util import send_raw_request
1142            send_raw_request(cmd.cli_ctx, 'put', RPAAS_APIS[namespace.lower()], body=json.dumps({"properties": {"accepted": True}}))
1143    else:
1144        action = 'Registering' if registering else 'Unregistering'
1145        msg_template = '%s is still on-going. You can monitor using \'az provider show -n %s\''
1146        logger.warning(msg_template, action, namespace)
1147
1148
1149def _build_policy_scope(subscription_id, resource_group_name, scope):
1150    subscription_scope = '/subscriptions/' + subscription_id
1151    if scope:
1152        if resource_group_name:
1153            err = "Resource group '{}' is redundant because 'scope' is supplied"
1154            raise CLIError(err.format(resource_group_name))
1155    elif resource_group_name:
1156        scope = subscription_scope + '/resourceGroups/' + resource_group_name
1157    else:
1158        scope = subscription_scope
1159    return scope
1160
1161
1162def _resolve_policy_id(cmd, policy, policy_set_definition, client):
1163    policy_id = policy or policy_set_definition
1164    if not is_valid_resource_id(policy_id):
1165        if policy:
1166            policy_def = _get_custom_or_builtin_policy(cmd, client, policy)
1167            policy_id = policy_def.id
1168        else:
1169            policy_set_def = _get_custom_or_builtin_policy(cmd, client, policy_set_definition, None, None, True)
1170            policy_id = policy_set_def.id
1171    return policy_id
1172
1173
1174def _parse_management_group_reference(name):
1175    if _is_management_group_scope(name):
1176        parts = name.split('/')
1177        if len(parts) >= 9:
1178            return parts[4], parts[8]
1179    return None, name
1180
1181
1182def _parse_management_group_id(scope):
1183    if _is_management_group_scope(scope):
1184        parts = scope.split('/')
1185        if len(parts) >= 5:
1186            return parts[4]
1187    return None
1188
1189
1190def _get_custom_or_builtin_policy(cmd, client, name, subscription=None, management_group=None, for_policy_set=False):
1191    from azure.core.exceptions import HttpResponseError
1192    policy_operations = client.policy_set_definitions if for_policy_set else client.policy_definitions
1193
1194    if cmd.supported_api_version(min_api='2018-03-01'):
1195        enforce_mutually_exclusive(subscription, management_group)
1196        if subscription:
1197            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
1198            client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
1199                                             subscription_id=subscription_id)
1200            policy_operations = client.policy_set_definitions if for_policy_set else client.policy_definitions
1201    try:
1202        if cmd.supported_api_version(min_api='2018-03-01'):
1203            if not management_group:
1204                management_group, name = _parse_management_group_reference(name)
1205            if management_group:
1206                return policy_operations.get_at_management_group(name, management_group)
1207        return policy_operations.get(name)
1208    except (HttpResponseError) as ex:
1209        status_code = ex.status_code if isinstance(ex, HttpResponseError) else ex.response.status_code
1210        if status_code == 404:
1211            try:
1212                return policy_operations.get_built_in(name)
1213            except HttpResponseError as ex2:
1214                # When the `--policy` parameter is neither a valid policy definition name nor conforms to the policy definition id format,
1215                # an exception of "AuthorizationFailed" will be reported to mislead customers.
1216                # So we need to modify the exception information thrown here.
1217                if ex2.status_code == 403 and ex2.error and ex2.error.code == 'AuthorizationFailed':
1218                    raise IncorrectUsageError('\'--policy\' should be a valid name or id of the policy definition')
1219                raise ex2
1220        raise
1221
1222
1223def _load_file_string_or_uri(file_or_string_or_uri, name, required=True):
1224    if file_or_string_or_uri is None:
1225        if required:
1226            raise CLIError('--{} is required'.format(name))
1227        return None
1228    url = urlparse(file_or_string_or_uri)
1229    if url.scheme == 'http' or url.scheme == 'https' or url.scheme == 'file':
1230        response = urlopen(file_or_string_or_uri)
1231        reader = codecs.getreader('utf-8')
1232        result = json.load(reader(response))
1233        response.close()
1234        return result
1235    if os.path.exists(file_or_string_or_uri):
1236        return get_file_json(file_or_string_or_uri)
1237    return shell_safe_json_parse(file_or_string_or_uri)
1238
1239
1240def _call_subscription_get(cmd, lock_client, *args):
1241    if cmd.supported_api_version(max_api='2015-01-01'):
1242        return lock_client.management_locks.get(*args)
1243    return lock_client.management_locks.get_at_subscription_level(*args)
1244
1245
1246def _extract_lock_params(resource_group_name, resource_provider_namespace,
1247                         resource_type, resource_name):
1248    if resource_group_name is None:
1249        return (None, None, None, None)
1250
1251    if resource_name is None:
1252        return (resource_group_name, None, None, None)
1253
1254    parts = resource_type.split('/', 2)
1255    if resource_provider_namespace is None and len(parts) == 2:
1256        resource_provider_namespace = parts[0]
1257        resource_type = parts[1]
1258    return (resource_group_name, resource_name, resource_provider_namespace, resource_type)
1259
1260
1261def _update_lock_parameters(parameters, level, notes):
1262    if level is not None:
1263        parameters.level = level
1264    if notes is not None:
1265        parameters.notes = notes
1266
1267
1268def _validate_resource_inputs(resource_group_name, resource_provider_namespace,
1269                              resource_type, resource_name):
1270    if resource_group_name is None:
1271        raise CLIError('--resource-group/-g is required.')
1272    if resource_type is None:
1273        raise CLIError('--resource-type is required')
1274    if resource_name is None:
1275        raise CLIError('--name/-n is required')
1276    if resource_provider_namespace is None:
1277        raise CLIError('--namespace is required')
1278
1279
1280# region Custom Commands
1281
1282def list_resource_groups(cmd, tag=None):  # pylint: disable=no-self-use
1283    """ List resource groups, optionally filtered by a tag.
1284    :param str tag:tag to filter by in 'key[=value]' format
1285    """
1286    rcf = _resource_client_factory(cmd.cli_ctx)
1287
1288    filters = []
1289    if tag:
1290        key = list(tag.keys())[0]
1291        filters.append("tagname eq '{}'".format(key))
1292        filters.append("tagvalue eq '{}'".format(tag[key]))
1293
1294    filter_text = ' and '.join(filters) if filters else None
1295
1296    groups = rcf.resource_groups.list(filter=filter_text)
1297    return list(groups)
1298
1299
1300def create_resource_group(cmd, rg_name, location, tags=None, managed_by=None):
1301    """ Create a new resource group.
1302    :param str resource_group_name:the desired resource group name
1303    :param str location:the resource group location
1304    :param str tags:tags in 'a=b c' format
1305    """
1306    rcf = _resource_client_factory(cmd.cli_ctx)
1307
1308    ResourceGroup = cmd.get_models('ResourceGroup')
1309    parameters = ResourceGroup(
1310        location=location,
1311        tags=tags
1312    )
1313
1314    if cmd.supported_api_version(min_api='2016-09-01'):
1315        parameters.managed_by = managed_by
1316
1317    return rcf.resource_groups.create_or_update(rg_name, parameters)
1318
1319
1320def update_resource_group(instance, tags=None):
1321
1322    if tags is not None:
1323        instance.tags = tags
1324
1325    return instance
1326
1327
1328def export_group_as_template(
1329        cmd, resource_group_name, include_comments=False, include_parameter_default_value=False, resource_ids=None, skip_resource_name_params=False, skip_all_params=False):
1330    """Captures a resource group as a template.
1331    :param str resource_group_name: the name of the resource group.
1332    :param resource_ids: space-separated resource ids to filter the export by. To export all resources, do not specify this argument or supply "*".
1333    :param bool include_comments: export template with comments.
1334    :param bool include_parameter_default_value: export template parameter with default value.
1335    :param bool skip_resource_name_params: export template and skip resource name parameterization.
1336    :param bool skip_all_params: export template parameter and skip all parameterization.
1337    """
1338    rcf = _resource_client_factory(cmd.cli_ctx)
1339
1340    export_options = []
1341    if include_comments:
1342        export_options.append('IncludeComments')
1343    if include_parameter_default_value:
1344        export_options.append('IncludeParameterDefaultValue')
1345    if skip_resource_name_params:
1346        export_options.append('SkipResourceNameParameterization')
1347    if skip_all_params:
1348        export_options.append('SkipAllParameterization')
1349
1350    resources = []
1351    if resource_ids is None or resource_ids[0] == "*":
1352        resources = ["*"]
1353    else:
1354        for i in resource_ids:
1355            if is_valid_resource_id(i):
1356                resources.append(i)
1357            else:
1358                raise CLIError('az resource: error: argument --resource-ids: invalid ResourceId value: \'%s\'' % i)
1359
1360    options = ','.join(export_options) if export_options else None
1361
1362    ExportTemplateRequest = cmd.get_models('ExportTemplateRequest')
1363    export_template_request = ExportTemplateRequest(resources=resources, options=options)
1364
1365    # Exporting a resource group as a template is async since API version 2019-08-01.
1366    if cmd.supported_api_version(min_api='2019-08-01'):
1367        result_poller = rcf.resource_groups.begin_export_template(resource_group_name,
1368                                                                  parameters=export_template_request)
1369        result = LongRunningOperation(cmd.cli_ctx)(result_poller)
1370    else:
1371        result = rcf.resource_groups.begin_export_template(resource_group_name,
1372                                                           parameters=export_template_request)
1373
1374    # pylint: disable=no-member
1375    # On error, server still returns 200, with details in the error attribute
1376    if result.error:
1377        error = result.error
1378        try:
1379            logger.warning(error.message)
1380        except AttributeError:
1381            logger.warning(str(error))
1382        for detail in getattr(error, 'details', None) or []:
1383            logger.error(detail.message)
1384
1385    return result.template
1386
1387
1388def create_application(cmd, resource_group_name,
1389                       application_name, managedby_resource_group_id,
1390                       kind, managedapp_definition_id=None, location=None,
1391                       plan_name=None, plan_publisher=None, plan_product=None,
1392                       plan_version=None, tags=None, parameters=None):
1393    """ Create a new managed application.
1394    :param str resource_group_name:the desired resource group name
1395    :param str application_name:the managed application name
1396    :param str kind:the managed application kind. can be marketplace or servicecatalog
1397    :param str plan_name:the managed application package plan name
1398    :param str plan_publisher:the managed application package plan publisher
1399    :param str plan_product:the managed application package plan product
1400    :param str plan_version:the managed application package plan version
1401    :param str tags:tags in 'a=b c' format
1402    """
1403    from azure.mgmt.resource.managedapplications.models import Application, Plan
1404    racf = _resource_managedapps_client_factory(cmd.cli_ctx)
1405    rcf = _resource_client_factory(cmd.cli_ctx)
1406    if not location:
1407        location = rcf.resource_groups.get(resource_group_name).location
1408    application = Application(
1409        location=location,
1410        managed_resource_group_id=managedby_resource_group_id,
1411        kind=kind,
1412        tags=tags
1413    )
1414
1415    if kind.lower() == 'servicecatalog':
1416        if managedapp_definition_id:
1417            application.application_definition_id = managedapp_definition_id
1418        else:
1419            raise CLIError('--managedapp-definition-id is required if kind is ServiceCatalog')
1420    elif kind.lower() == 'marketplace':
1421        if (plan_name is None and plan_product is None and
1422                plan_publisher is None and plan_version is None):
1423            raise CLIError('--plan-name, --plan-product, --plan-publisher and \
1424            --plan-version are all required if kind is MarketPlace')
1425        application.plan = Plan(name=plan_name, publisher=plan_publisher, product=plan_product, version=plan_version)
1426
1427    applicationParameters = None
1428
1429    if parameters:
1430        if os.path.exists(parameters):
1431            applicationParameters = get_file_json(parameters)
1432        else:
1433            applicationParameters = shell_safe_json_parse(parameters)
1434
1435    application.parameters = applicationParameters
1436
1437    return racf.applications.begin_create_or_update(resource_group_name, application_name, application)
1438
1439
1440def show_application(cmd, resource_group_name=None, application_name=None):
1441    """ Gets a managed application.
1442    :param str resource_group_name:the resource group name
1443    :param str application_name:the managed application name
1444    """
1445    racf = _resource_managedapps_client_factory(cmd.cli_ctx)
1446    return racf.applications.get(resource_group_name, application_name)
1447
1448
1449def show_applicationdefinition(cmd, resource_group_name=None, application_definition_name=None):
1450    """ Gets a managed application definition.
1451    :param str resource_group_name:the resource group name
1452    :param str application_definition_name:the managed application definition name
1453    """
1454    racf = _resource_managedapps_client_factory(cmd.cli_ctx)
1455    return racf.application_definitions.get(resource_group_name, application_definition_name)
1456
1457
1458def create_or_update_applicationdefinition(cmd, resource_group_name,
1459                                           application_definition_name,
1460                                           lock_level, authorizations,
1461                                           description, display_name,
1462                                           package_file_uri=None, create_ui_definition=None,
1463                                           main_template=None, location=None, tags=None):
1464    """ Create or update a new managed application definition.
1465    :param str resource_group_name:the desired resource group name
1466    :param str application_definition_name:the managed application definition name
1467    :param str description:the managed application definition description
1468    :param str display_name:the managed application definition display name
1469    :param str package_file_uri:the managed application definition package file uri
1470    :param str create_ui_definition:the managed application definition create ui definition
1471    :param str main_template:the managed application definition main template
1472    :param str tags:tags in 'a=b c' format
1473    """
1474    from azure.mgmt.resource.managedapplications.models import ApplicationDefinition, ApplicationProviderAuthorization
1475    if not package_file_uri and not create_ui_definition and not main_template:
1476        raise CLIError('usage error: --package-file-uri <url> | --create-ui-definition --main-template')
1477    if package_file_uri:
1478        if create_ui_definition or main_template:
1479            raise CLIError('usage error: must not specify --create-ui-definition --main-template')
1480    if not package_file_uri:
1481        if not create_ui_definition or not main_template:
1482            raise CLIError('usage error: must specify --create-ui-definition --main-template')
1483    racf = _resource_managedapps_client_factory(cmd.cli_ctx)
1484    rcf = _resource_client_factory(cmd.cli_ctx)
1485    if not location:
1486        location = rcf.resource_groups.get(resource_group_name).location
1487    authorizations = authorizations or []
1488    applicationAuthList = []
1489
1490    for name_value in authorizations:
1491        # split at the first ':', neither principalId nor roldeDefinitionId should have a ':'
1492        principalId, roleDefinitionId = name_value.split(':', 1)
1493        applicationAuth = ApplicationProviderAuthorization(
1494            principal_id=principalId,
1495            role_definition_id=roleDefinitionId)
1496        applicationAuthList.append(applicationAuth)
1497
1498    applicationDef = ApplicationDefinition(lock_level=lock_level,
1499                                           authorizations=applicationAuthList,
1500                                           package_file_uri=package_file_uri)
1501    applicationDef.display_name = display_name
1502    applicationDef.description = description
1503    applicationDef.location = location
1504    applicationDef.package_file_uri = package_file_uri
1505    applicationDef.create_ui_definition = create_ui_definition
1506    applicationDef.main_template = main_template
1507    applicationDef.tags = tags
1508
1509    return racf.application_definitions.begin_create_or_update(resource_group_name,
1510                                                               application_definition_name, applicationDef)
1511
1512
1513def list_applications(cmd, resource_group_name=None):
1514    racf = _resource_managedapps_client_factory(cmd.cli_ctx)
1515
1516    if resource_group_name:
1517        applications = racf.applications.list_by_resource_group(resource_group_name)
1518    else:
1519        applications = racf.applications.list_by_subscription()
1520    return list(applications)
1521
1522
1523def list_deployments_at_subscription_scope(cmd, filter_string=None):
1524    rcf = _resource_client_factory(cmd.cli_ctx)
1525    return rcf.deployments.list_at_subscription_scope(filter=filter_string)
1526
1527
1528def list_deployments_at_resource_group(cmd, resource_group_name, filter_string=None):
1529    rcf = _resource_client_factory(cmd.cli_ctx)
1530    return rcf.deployments.list_by_resource_group(resource_group_name, filter=filter_string)
1531
1532
1533def list_deployments_at_management_group(cmd, management_group_id, filter_string=None):
1534    rcf = _resource_client_factory(cmd.cli_ctx)
1535    return rcf.deployments.list_at_management_group_scope(management_group_id, filter=filter_string)
1536
1537
1538def list_deployments_at_tenant_scope(cmd, filter_string=None):
1539    rcf = _resource_client_factory(cmd.cli_ctx)
1540    return rcf.deployments.list_at_tenant_scope(filter=filter_string)
1541
1542
1543def get_deployment_at_subscription_scope(cmd, deployment_name):
1544    rcf = _resource_client_factory(cmd.cli_ctx)
1545    return rcf.deployments.get_at_subscription_scope(deployment_name)
1546
1547
1548def get_deployment_at_resource_group(cmd, resource_group_name, deployment_name):
1549    rcf = _resource_client_factory(cmd.cli_ctx)
1550    return rcf.deployments.get(resource_group_name, deployment_name)
1551
1552
1553def get_deployment_at_management_group(cmd, management_group_id, deployment_name):
1554    rcf = _resource_client_factory(cmd.cli_ctx)
1555    return rcf.deployments.get_at_management_group_scope(management_group_id, deployment_name)
1556
1557
1558def get_deployment_at_tenant_scope(cmd, deployment_name):
1559    rcf = _resource_client_factory(cmd.cli_ctx)
1560    return rcf.deployments.get_at_tenant_scope(deployment_name)
1561
1562
1563def delete_deployment_at_subscription_scope(cmd, deployment_name):
1564    rcf = _resource_client_factory(cmd.cli_ctx)
1565    return rcf.deployments.begin_delete_at_subscription_scope(deployment_name)
1566
1567
1568def delete_deployment_at_resource_group(cmd, resource_group_name, deployment_name):
1569    rcf = _resource_client_factory(cmd.cli_ctx)
1570    return rcf.deployments.begin_delete(resource_group_name, deployment_name)
1571
1572
1573def delete_deployment_at_management_group(cmd, management_group_id, deployment_name):
1574    rcf = _resource_client_factory(cmd.cli_ctx)
1575    return rcf.deployments.begin_delete_at_management_group_scope(management_group_id, deployment_name)
1576
1577
1578def delete_deployment_at_tenant_scope(cmd, deployment_name):
1579    rcf = _resource_client_factory(cmd.cli_ctx)
1580    return rcf.deployments.begin_delete_at_tenant_scope(deployment_name)
1581
1582
1583def cancel_deployment_at_subscription_scope(cmd, deployment_name):
1584    rcf = _resource_client_factory(cmd.cli_ctx)
1585    return rcf.deployments.cancel_at_subscription_scope(deployment_name)
1586
1587
1588def cancel_deployment_at_resource_group(cmd, resource_group_name, deployment_name):
1589    rcf = _resource_client_factory(cmd.cli_ctx)
1590    return rcf.deployments.cancel(resource_group_name, deployment_name)
1591
1592
1593def cancel_deployment_at_management_group(cmd, management_group_id, deployment_name):
1594    rcf = _resource_client_factory(cmd.cli_ctx)
1595    return rcf.deployments.cancel_at_management_group_scope(management_group_id, deployment_name)
1596
1597
1598def cancel_deployment_at_tenant_scope(cmd, deployment_name):
1599    rcf = _resource_client_factory(cmd.cli_ctx)
1600    return rcf.deployments.cancel_at_tenant_scope(deployment_name)
1601
1602
1603# pylint: disable=unused-argument
1604def deploy_arm_template(cmd, resource_group_name,
1605                        template_file=None, template_uri=None, deployment_name=None,
1606                        parameters=None, mode=None, rollback_on_error=None, no_wait=False,
1607                        handle_extended_json_format=None, aux_subscriptions=None, aux_tenants=None,
1608                        no_prompt=False):
1609    return _deploy_arm_template_core_unmodified(cmd, resource_group_name=resource_group_name,
1610                                                template_file=template_file, template_uri=template_uri,
1611                                                deployment_name=deployment_name, parameters=parameters, mode=mode,
1612                                                rollback_on_error=rollback_on_error, no_wait=no_wait,
1613                                                aux_subscriptions=aux_subscriptions, aux_tenants=aux_tenants,
1614                                                no_prompt=no_prompt)
1615
1616
1617# pylint: disable=unused-argument
1618def validate_arm_template(cmd, resource_group_name, template_file=None, template_uri=None,
1619                          parameters=None, mode=None, rollback_on_error=None, handle_extended_json_format=None,
1620                          no_prompt=False):
1621    return _deploy_arm_template_core_unmodified(cmd, resource_group_name, template_file, template_uri,
1622                                                'deployment_dry_run', parameters, mode, rollback_on_error,
1623                                                validate_only=True, no_prompt=no_prompt)
1624
1625
1626def export_template_at_subscription_scope(cmd, deployment_name):
1627    rcf = _resource_client_factory(cmd.cli_ctx)
1628    result = rcf.deployments.export_template_at_subscription_scope(deployment_name)
1629
1630    print(json.dumps(result.template, indent=2))  # pylint: disable=no-member
1631
1632
1633def export_template_at_resource_group(cmd, resource_group_name, deployment_name):
1634    rcf = _resource_client_factory(cmd.cli_ctx)
1635    result = rcf.deployments.export_template(resource_group_name, deployment_name)
1636
1637    print(json.dumps(result.template, indent=2))  # pylint: disable=no-member
1638
1639
1640def export_template_at_management_group(cmd, management_group_id, deployment_name):
1641    rcf = _resource_client_factory(cmd.cli_ctx)
1642    result = rcf.deployments.export_template_at_management_group_scope(management_group_id, deployment_name)
1643
1644    print(json.dumps(result.template, indent=2))  # pylint: disable=no-member
1645
1646
1647def export_template_at_tenant_scope(cmd, deployment_name):
1648    rcf = _resource_client_factory(cmd.cli_ctx)
1649    result = rcf.deployments.export_template_at_tenant_scope(deployment_name)
1650
1651    print(json.dumps(result.template, indent=2))  # pylint: disable=no-member
1652
1653
1654def export_deployment_as_template(cmd, resource_group_name, deployment_name):
1655    smc = _resource_client_factory(cmd.cli_ctx)
1656    result = smc.deployments.export_template(resource_group_name, deployment_name)
1657    print(json.dumps(result.template, indent=2))  # pylint: disable=no-member
1658
1659
1660def create_resource(cmd, properties,
1661                    resource_group_name=None, resource_provider_namespace=None,
1662                    parent_resource_path=None, resource_type=None, resource_name=None,
1663                    resource_id=None, api_version=None, location=None, is_full_object=False,
1664                    latest_include_preview=False):
1665    res = _ResourceUtils(cmd.cli_ctx, resource_group_name, resource_provider_namespace,
1666                         parent_resource_path, resource_type, resource_name,
1667                         resource_id, api_version, latest_include_preview=latest_include_preview)
1668    return res.create_resource(properties, location, is_full_object)
1669
1670
1671def _get_parsed_resource_ids(resource_ids):
1672    """
1673    Returns a generator of parsed resource ids. Raise when there is invalid resource id.
1674    """
1675    if not resource_ids:
1676        return None
1677
1678    for rid in resource_ids:
1679        if not is_valid_resource_id(rid):
1680            raise CLIError('az resource: error: argument --ids: invalid ResourceId value: \'%s\'' % rid)
1681
1682    return ({'resource_id': rid} for rid in resource_ids)
1683
1684
1685def _get_rsrc_util_from_parsed_id(cli_ctx, parsed_id, api_version, latest_include_preview=False):
1686    return _ResourceUtils(cli_ctx,
1687                          parsed_id.get('resource_group', None),
1688                          parsed_id.get('resource_namespace', None),
1689                          parsed_id.get('resource_parent', None),
1690                          parsed_id.get('resource_type', None),
1691                          parsed_id.get('resource_name', None),
1692                          parsed_id.get('resource_id', None),
1693                          api_version,
1694                          latest_include_preview=latest_include_preview)
1695
1696
1697def _create_parsed_id(cli_ctx, resource_group_name=None, resource_provider_namespace=None, parent_resource_path=None,
1698                      resource_type=None, resource_name=None):
1699    subscription = get_subscription_id(cli_ctx)
1700    return {
1701        'resource_group': resource_group_name,
1702        'resource_namespace': resource_provider_namespace,
1703        'resource_parent': parent_resource_path,
1704        'resource_type': resource_type,
1705        'resource_name': resource_name,
1706        'subscription': subscription
1707    }
1708
1709
1710def _single_or_collection(obj, default=None):
1711    if not obj:
1712        return default
1713
1714    if isinstance(obj, list) and len(obj) == 1:
1715        return obj[0]
1716
1717    return obj
1718
1719
1720def show_resource(cmd, resource_ids=None, resource_group_name=None,
1721                  resource_provider_namespace=None, parent_resource_path=None, resource_type=None,
1722                  resource_name=None, api_version=None, include_response_body=False, latest_include_preview=False):
1723    parsed_ids = _get_parsed_resource_ids(resource_ids) or [_create_parsed_id(cmd.cli_ctx,
1724                                                                              resource_group_name,
1725                                                                              resource_provider_namespace,
1726                                                                              parent_resource_path,
1727                                                                              resource_type,
1728                                                                              resource_name)]
1729
1730    return _single_or_collection(
1731        [_get_rsrc_util_from_parsed_id(cmd.cli_ctx, id_dict, api_version, latest_include_preview).get_resource(
1732            include_response_body) for id_dict in parsed_ids])
1733
1734
1735# pylint: disable=unused-argument
1736def delete_resource(cmd, resource_ids=None, resource_group_name=None,
1737                    resource_provider_namespace=None, parent_resource_path=None, resource_type=None,
1738                    resource_name=None, api_version=None, latest_include_preview=False):
1739    """
1740    Deletes the given resource(s).
1741    This function allows deletion of ids with dependencies on one another.
1742    This is done with multiple passes through the given ids.
1743    """
1744    parsed_ids = _get_parsed_resource_ids(resource_ids) or [_create_parsed_id(cmd.cli_ctx,
1745                                                                              resource_group_name,
1746                                                                              resource_provider_namespace,
1747                                                                              parent_resource_path,
1748                                                                              resource_type,
1749                                                                              resource_name)]
1750    to_be_deleted = [(_get_rsrc_util_from_parsed_id(cmd.cli_ctx, id_dict, api_version, latest_include_preview), id_dict)
1751                     for id_dict in parsed_ids]
1752
1753    results = []
1754    from azure.core.exceptions import HttpResponseError
1755    while to_be_deleted:
1756        logger.debug("Start new loop to delete resources.")
1757        operations = []
1758        failed_to_delete = []
1759        for rsrc_utils, id_dict in to_be_deleted:
1760            try:
1761                operations.append(rsrc_utils.delete())
1762                resource = _build_resource_id(**id_dict) or resource_name
1763                logger.debug("deleting %s", resource)
1764            except HttpResponseError as e:
1765                # request to delete failed, add parsed id dict back to queue
1766                id_dict['exception'] = str(e)
1767                failed_to_delete.append((rsrc_utils, id_dict))
1768        to_be_deleted = failed_to_delete
1769
1770        # stop deleting if none deletable
1771        if not operations:
1772            break
1773
1774        # all operations return result before next pass
1775        for operation in operations:
1776            results.append(operation.result())
1777
1778    if to_be_deleted:
1779        error_msg_builder = ['Some resources failed to be deleted (run with `--verbose` for more information):']
1780        for _, id_dict in to_be_deleted:
1781            logger.info(id_dict['exception'])
1782            resource_id = _build_resource_id(**id_dict) or id_dict['resource_id']
1783            error_msg_builder.append(resource_id)
1784        raise CLIError(os.linesep.join(error_msg_builder))
1785
1786    return _single_or_collection(results)
1787
1788
1789def update_resource(cmd, parameters, resource_ids=None,
1790                    resource_group_name=None, resource_provider_namespace=None,
1791                    parent_resource_path=None, resource_type=None, resource_name=None, api_version=None,
1792                    latest_include_preview=False):
1793    parsed_ids = _get_parsed_resource_ids(resource_ids) or [_create_parsed_id(cmd.cli_ctx,
1794                                                                              resource_group_name,
1795                                                                              resource_provider_namespace,
1796                                                                              parent_resource_path,
1797                                                                              resource_type,
1798                                                                              resource_name)]
1799
1800    return _single_or_collection(
1801        [_get_rsrc_util_from_parsed_id(cmd.cli_ctx, id_dict, api_version, latest_include_preview).update(parameters)
1802         for id_dict in parsed_ids])
1803
1804
1805def tag_resource(cmd, tags, resource_ids=None, resource_group_name=None, resource_provider_namespace=None,
1806                 parent_resource_path=None, resource_type=None, resource_name=None, api_version=None,
1807                 is_incremental=None, latest_include_preview=False):
1808    """ Updates the tags on an existing resource. To clear tags, specify the --tag option
1809    without anything else. """
1810    parsed_ids = _get_parsed_resource_ids(resource_ids) or [_create_parsed_id(cmd.cli_ctx,
1811                                                                              resource_group_name,
1812                                                                              resource_provider_namespace,
1813                                                                              parent_resource_path,
1814                                                                              resource_type,
1815                                                                              resource_name)]
1816
1817    return _single_or_collection([LongRunningOperation(cmd.cli_ctx)(
1818        _get_rsrc_util_from_parsed_id(cmd.cli_ctx, id_dict, api_version, latest_include_preview).tag(
1819            tags, is_incremental)) for id_dict in parsed_ids])
1820
1821
1822def invoke_resource_action(cmd, action, request_body=None, resource_ids=None,
1823                           resource_group_name=None, resource_provider_namespace=None,
1824                           parent_resource_path=None, resource_type=None, resource_name=None,
1825                           api_version=None, latest_include_preview=False):
1826    """ Invokes the provided action on an existing resource."""
1827    parsed_ids = _get_parsed_resource_ids(resource_ids) or [_create_parsed_id(cmd.cli_ctx,
1828                                                                              resource_group_name,
1829                                                                              resource_provider_namespace,
1830                                                                              parent_resource_path,
1831                                                                              resource_type,
1832                                                                              resource_name)]
1833
1834    return _single_or_collection(
1835        [_get_rsrc_util_from_parsed_id(cmd.cli_ctx, id_dict, api_version, latest_include_preview).invoke_action(
1836            action, request_body) for id_dict in parsed_ids])
1837
1838
1839def get_deployment_operations(client, resource_group_name, deployment_name, operation_ids):
1840    """get a deployment's operation."""
1841    result = []
1842    for op_id in operation_ids:
1843        dep = client.get(resource_group_name, deployment_name, op_id)
1844        result.append(dep)
1845    return result
1846
1847
1848def get_deployment_operations_at_subscription_scope(client, deployment_name, operation_ids):
1849    result = []
1850    for op_id in operation_ids:
1851        deployment = client.get_at_subscription_scope(deployment_name, op_id)
1852        result.append(deployment)
1853    return result
1854
1855
1856def get_deployment_operations_at_resource_group(client, resource_group_name, deployment_name, operation_ids):
1857    result = []
1858    for op_id in operation_ids:
1859        dep = client.get(resource_group_name, deployment_name, op_id)
1860        result.append(dep)
1861    return result
1862
1863
1864def get_deployment_operations_at_management_group(client, management_group_id, deployment_name, operation_ids):
1865    result = []
1866    for op_id in operation_ids:
1867        dep = client.get_at_management_group_scope(management_group_id, deployment_name, op_id)
1868        result.append(dep)
1869    return result
1870
1871
1872def get_deployment_operations_at_tenant_scope(client, deployment_name, operation_ids):
1873    result = []
1874    for op_id in operation_ids:
1875        dep = client.get_at_tenant_scope(deployment_name, op_id)
1876        result.append(dep)
1877    return result
1878
1879
1880def list_deployment_scripts(cmd, resource_group_name=None):
1881    rcf = _resource_deploymentscripts_client_factory(cmd.cli_ctx)
1882    if resource_group_name is not None:
1883        return rcf.deployment_scripts.list_by_resource_group(resource_group_name)
1884    return rcf.deployment_scripts.list_by_subscription()
1885
1886
1887def get_deployment_script(cmd, resource_group_name, name):
1888    rcf = _resource_deploymentscripts_client_factory(cmd.cli_ctx)
1889    return rcf.deployment_scripts.get(resource_group_name, name)
1890
1891
1892def get_deployment_script_logs(cmd, resource_group_name, name):
1893    rcf = _resource_deploymentscripts_client_factory(cmd.cli_ctx)
1894    return rcf.deployment_scripts.get_logs(resource_group_name, name)
1895
1896
1897def delete_deployment_script(cmd, resource_group_name, name):
1898    rcf = _resource_deploymentscripts_client_factory(cmd.cli_ctx)
1899    rcf.deployment_scripts.delete(resource_group_name, name)
1900
1901
1902def get_template_spec(cmd, resource_group_name=None, name=None, version=None, template_spec=None):
1903    if template_spec:
1904        id_parts = parse_resource_id(template_spec)
1905        resource_group_name = id_parts.get('resource_group')
1906        name = id_parts.get('name')
1907        version = id_parts.get('resource_name')
1908        if version == name:
1909            version = None
1910    rcf = _resource_templatespecs_client_factory(cmd.cli_ctx)
1911    if version:
1912        return rcf.template_spec_versions.get(resource_group_name, name, version)
1913    retrieved_template = rcf.template_specs.get(resource_group_name, name, expand="versions")
1914    version_names = list(retrieved_template.versions.keys())
1915    retrieved_template.versions = version_names
1916    return retrieved_template
1917
1918
1919def create_template_spec(cmd, resource_group_name, name, template_file=None, location=None, display_name=None,
1920                         description=None, version=None, version_description=None, tags=None, no_prompt=False, ui_form_definition_file=None):
1921    if location is None:
1922        rcf = _resource_client_factory(cmd.cli_ctx)
1923        location = rcf.resource_groups.get(resource_group_name).location
1924    rcf = _resource_templatespecs_client_factory(cmd.cli_ctx)
1925
1926    if template_file and not version:
1927        raise IncorrectUsageError('please provide --version if --template-file is specified')
1928
1929    if version:
1930        input_template, artifacts, input_ui_form_definition = None, None, None
1931        exists = False
1932        if no_prompt is False:
1933            try:  # Check if child template spec already exists.
1934                rcf.template_spec_versions.get(resource_group_name=resource_group_name, template_spec_name=name, template_spec_version=version)
1935                from knack.prompting import prompt_y_n
1936                confirmation = prompt_y_n("This will override template spec {} version {}. Proceed?".format(name, version))
1937                if not confirmation:
1938                    return None
1939                exists = True
1940            except Exception:  # pylint: disable=broad-except
1941                pass
1942
1943        if template_file:
1944            from azure.cli.command_modules.resource._packing_engine import (pack)
1945            if is_bicep_file(template_file):
1946                template_content = run_bicep_command(["build", "--stdout", template_file])
1947                input_content = _remove_comments_from_json(template_content, file_path=template_file)
1948                input_template = json.loads(json.dumps(input_content))
1949                artifacts = []
1950            else:
1951                packed_template = pack(cmd, template_file)
1952                input_template = getattr(packed_template, 'RootTemplate')
1953                artifacts = getattr(packed_template, 'Artifacts')
1954
1955        if ui_form_definition_file:
1956            ui_form_definition_content = _remove_comments_from_json(read_file_content(ui_form_definition_file))
1957            input_ui_form_definition = json.loads(json.dumps(ui_form_definition_content))
1958
1959        if not exists:
1960            try:  # Check if parent template spec already exists.
1961                existing_parent = rcf.template_specs.get(resource_group_name=resource_group_name, template_spec_name=name)
1962                if tags is None:  # New version should inherit tags from parent if none are provided.
1963                    tags = getattr(existing_parent, 'tags')
1964            except Exception:  # pylint: disable=broad-except
1965                tags = tags or {}
1966                TemplateSpec = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_TEMPLATESPECS, 'TemplateSpec', mod='models')
1967                template_spec_parent = TemplateSpec(location=location, description=description, display_name=display_name, tags=tags)
1968                rcf.template_specs.create_or_update(resource_group_name, name, template_spec_parent)
1969
1970        TemplateSpecVersion = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_TEMPLATESPECS, 'TemplateSpecVersion', mod='models')
1971        template_spec_version = TemplateSpecVersion(location=location, linked_templates=artifacts, description=version_description, main_template=input_template, tags=tags, ui_form_definition=input_ui_form_definition)
1972        return rcf.template_spec_versions.create_or_update(resource_group_name, name, version, template_spec_version)
1973
1974    tags = tags or {}
1975    TemplateSpec = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_TEMPLATESPECS, 'TemplateSpec', mod='models')
1976    template_spec_parent = TemplateSpec(location=location, description=description, display_name=display_name, tags=tags)
1977    return rcf.template_specs.create_or_update(resource_group_name, name, template_spec_parent)
1978
1979
1980def update_template_spec(cmd, resource_group_name=None, name=None, template_spec=None, template_file=None, display_name=None,
1981                         description=None, version=None, version_description=None, tags=None, ui_form_definition_file=None):
1982    rcf = _resource_templatespecs_client_factory(cmd.cli_ctx)
1983
1984    if template_spec:
1985        id_parts = parse_resource_id(template_spec)
1986        resource_group_name = id_parts.get('resource_group')
1987        name = id_parts.get('name')
1988        version = id_parts.get('resource_name')
1989        if version == name:
1990            version = None
1991
1992    existing_template, artifacts, input_ui_form_definition = None, None, None
1993    if template_file:
1994        from azure.cli.command_modules.resource._packing_engine import (pack)
1995        if is_bicep_file(template_file):
1996            template_content = run_bicep_command(["build", "--stdout", template_file])
1997            input_content = _remove_comments_from_json(template_content, file_path=template_file)
1998            input_template = json.loads(json.dumps(input_content))
1999            artifacts = []
2000        else:
2001            packed_template = pack(cmd, template_file)
2002            input_template = getattr(packed_template, 'RootTemplate')
2003            artifacts = getattr(packed_template, 'Artifacts')
2004
2005    if ui_form_definition_file:
2006        ui_form_definition_content = _remove_comments_from_json(read_file_content(ui_form_definition_file))
2007        input_ui_form_definition = json.loads(json.dumps(ui_form_definition_content))
2008
2009    if version:
2010        existing_template = rcf.template_spec_versions.get(resource_group_name=resource_group_name, template_spec_name=name, template_spec_version=version)
2011
2012        location = getattr(existing_template, 'location')
2013
2014        # Do not remove tags if not explicitly empty.
2015        if tags is None:
2016            tags = getattr(existing_template, 'tags')
2017        if version_description is None:
2018            version_description = getattr(existing_template, 'description')
2019        if template_file is None:
2020            input_template = getattr(existing_template, 'main_template')
2021        if ui_form_definition_file is None:
2022            input_ui_form_definition = getattr(existing_template, 'ui_form_definition')
2023        TemplateSpecVersion = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_TEMPLATESPECS, 'TemplateSpecVersion', mod='models')
2024
2025        updated_template_spec = TemplateSpecVersion(location=location, linked_templates=artifacts, description=version_description, main_template=input_template, tags=tags, ui_form_definition=input_ui_form_definition)
2026        return rcf.template_spec_versions.create_or_update(resource_group_name, name, version, updated_template_spec)
2027
2028    existing_template = rcf.template_specs.get(resource_group_name=resource_group_name, template_spec_name=name)
2029
2030    location = getattr(existing_template, 'location')
2031    # Do not remove tags if not explicitly empty.
2032    if tags is None:
2033        tags = getattr(existing_template, 'tags')
2034    if display_name is None:
2035        display_name = getattr(existing_template, 'display_name')
2036    if description is None:
2037        description = getattr(existing_template, 'description')
2038
2039    TemplateSpec = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_TEMPLATESPECS, 'TemplateSpec', mod='models')
2040
2041    root_template = TemplateSpec(location=location, description=description, display_name=display_name, tags=tags)
2042    return rcf.template_specs.create_or_update(resource_group_name, name, root_template)
2043
2044
2045def export_template_spec(cmd, output_folder, resource_group_name=None, name=None, version=None, template_spec=None):
2046    rcf = _resource_templatespecs_client_factory(cmd.cli_ctx)
2047    if template_spec:
2048        id_parts = parse_resource_id(template_spec)
2049        resource_group_name = id_parts.get('resource_group')
2050        name = id_parts.get('name')
2051        version = id_parts.get('resource_name')
2052        if version == name:
2053            version = None
2054    if not version:
2055        raise IncorrectUsageError('Please specify the template spec version for export')
2056    exported_template = rcf.template_spec_versions.get(resource_group_name, name, version)
2057    from azure.cli.command_modules.resource._packing_engine import (unpack)
2058    return unpack(cmd, exported_template, output_folder, (str(name) + '.JSON'))
2059
2060
2061def delete_template_spec(cmd, resource_group_name=None, name=None, version=None, template_spec=None):
2062    rcf = _resource_templatespecs_client_factory(cmd.cli_ctx)
2063    if template_spec:
2064        id_parts = parse_resource_id(template_spec)
2065        resource_group_name = id_parts.get('resource_group')
2066        name = id_parts.get('name')
2067        version = id_parts.get('resource_name')
2068        if version == name:
2069            version = None
2070    if version:
2071        return rcf.template_spec_versions.delete(resource_group_name=resource_group_name, template_spec_name=name, template_spec_version=version)
2072    return rcf.template_specs.delete(resource_group_name=resource_group_name, template_spec_name=name)
2073
2074
2075def list_template_specs(cmd, resource_group_name=None, name=None):
2076    rcf = _resource_templatespecs_client_factory(cmd.cli_ctx)
2077    if resource_group_name is not None:
2078        if name is not None:
2079            return rcf.template_spec_versions.list(resource_group_name=resource_group_name, template_spec_name=name)
2080        return rcf.template_specs.list_by_resource_group(resource_group_name)
2081    return rcf.template_specs.list_by_subscription()
2082
2083
2084def list_deployment_operations_at_subscription_scope(cmd, deployment_name):
2085    rcf = _resource_client_factory(cmd.cli_ctx)
2086    return rcf.deployment_operations.list_at_subscription_scope(deployment_name)
2087
2088
2089def list_deployment_operations_at_resource_group(cmd, resource_group_name, deployment_name):
2090    rcf = _resource_client_factory(cmd.cli_ctx)
2091    return rcf.deployment_operations.list(resource_group_name, deployment_name)
2092
2093
2094def list_deployment_operations_at_management_group(cmd, management_group_id, deployment_name):
2095    rcf = _resource_client_factory(cmd.cli_ctx)
2096    return rcf.deployment_operations.list_at_management_group_scope(management_group_id, deployment_name)
2097
2098
2099def list_deployment_operations_at_tenant_scope(cmd, deployment_name):
2100    rcf = _resource_client_factory(cmd.cli_ctx)
2101    return rcf.deployment_operations.list_at_tenant_scope(deployment_name)
2102
2103
2104def get_deployment_operation_at_subscription_scope(cmd, deployment_name, op_id):
2105    rcf = _resource_client_factory(cmd.cli_ctx)
2106    return rcf.deployment_operations.get_at_subscription_scope(deployment_name, op_id)
2107
2108
2109def get_deployment_operation_at_resource_group(cmd, resource_group_name, deployment_name, op_id):
2110    rcf = _resource_client_factory(cmd.cli_ctx)
2111    return rcf.deployment_operations.get(resource_group_name, deployment_name, op_id)
2112
2113
2114def get_deployment_operation_at_management_group(cmd, management_group_id, deployment_name, op_id):
2115    rcf = _resource_client_factory(cmd.cli_ctx)
2116    return rcf.deployment_operations.get_at_management_group_scope(management_group_id, deployment_name, op_id)
2117
2118
2119def get_deployment_operation_at_tenant_scope(cmd, deployment_name, op_id):
2120    rcf = _resource_client_factory(cmd.cli_ctx)
2121    return rcf.deployment_operations.get_at_tenant_scope(deployment_name, op_id)
2122
2123
2124def list_resources(cmd, resource_group_name=None,
2125                   resource_provider_namespace=None, resource_type=None, name=None, tag=None,
2126                   location=None):
2127    rcf = _resource_client_factory(cmd.cli_ctx)
2128
2129    if resource_group_name is not None:
2130        rcf.resource_groups.get(resource_group_name)
2131
2132    odata_filter = _list_resources_odata_filter_builder(resource_group_name,
2133                                                        resource_provider_namespace,
2134                                                        resource_type, name, tag, location)
2135
2136    expand = "createdTime,changedTime,provisioningState"
2137    resources = rcf.resources.list(filter=odata_filter, expand=expand)
2138    return list(resources)
2139
2140
2141def register_provider(cmd, resource_provider_namespace, consent_to_permissions=False, mg=None, wait=False, accept_terms=None):
2142    properties = None
2143    if cmd.supported_api_version(min_api='2021-04-01') and consent_to_permissions:
2144        ProviderRegistrationRequest, ProviderConsentDefinition = cmd.get_models('ProviderRegistrationRequest', 'ProviderConsentDefinition')
2145        properties = ProviderRegistrationRequest(third_party_provider_consent=ProviderConsentDefinition(consent_to_authorization=consent_to_permissions))
2146    _update_provider(cmd, resource_provider_namespace, registering=True, wait=wait, properties=properties, mg_id=mg, accept_terms=accept_terms)
2147
2148
2149def unregister_provider(cmd, resource_provider_namespace, wait=False):
2150    _update_provider(cmd, resource_provider_namespace, registering=False, wait=wait)
2151
2152
2153def list_provider_operations(cmd):
2154    auth_client = _authorization_management_client(cmd.cli_ctx)
2155    return auth_client.provider_operations_metadata.list()
2156
2157
2158def list_provider_permissions(cmd, resource_provider_namespace):
2159    rcf = _resource_client_factory(cmd.cli_ctx)
2160    return rcf.providers.provider_permissions(resource_provider_namespace)
2161
2162
2163def show_provider_operations(cmd, resource_provider_namespace):
2164    version = getattr(get_api_version(cmd.cli_ctx, ResourceType.MGMT_AUTHORIZATION), 'provider_operations_metadata')
2165    auth_client = _authorization_management_client(cmd.cli_ctx)
2166    if version == '2015-07-01':
2167        return auth_client.provider_operations_metadata.get(resource_provider_namespace, version)
2168    return auth_client.provider_operations_metadata.get(resource_provider_namespace)
2169
2170
2171def move_resource(cmd, ids, destination_group, destination_subscription_id=None):
2172    """Moves resources from one resource group to another(can be under different subscription)
2173
2174    :param ids: the space-separated resource ids to be moved
2175    :param destination_group: the destination resource group name
2176    :param destination_subscription_id: the destination subscription identifier
2177    """
2178    # verify all resource ids are valid and under the same group
2179    resources = []
2180    for i in ids:
2181        if is_valid_resource_id(i):
2182            resources.append(parse_resource_id(i))
2183        else:
2184            raise CLIError('Invalid id "{}", as it has no group or subscription field'.format(i))
2185
2186    if len({r['subscription'] for r in resources}) > 1:
2187        raise CLIError('All resources should be under the same subscription')
2188    if len({r['resource_group'] for r in resources}) > 1:
2189        raise CLIError('All resources should be under the same group')
2190
2191    rcf = _resource_client_factory(cmd.cli_ctx)
2192    default_subscription_id = get_subscription_id(cmd.cli_ctx)
2193    target = _build_resource_id(subscription=(destination_subscription_id or default_subscription_id),
2194                                resource_group=destination_group)
2195
2196    ResourcesMoveInfo = cmd.get_models('ResourcesMoveInfo')
2197    resources_move_info = ResourcesMoveInfo(resources=ids, target_resource_group=target)
2198    return rcf.resources.begin_move_resources(resources[0]['resource_group'], parameters=resources_move_info)
2199
2200
2201def list_features(client, resource_provider_namespace=None):
2202    if resource_provider_namespace:
2203        return client.list(resource_provider_namespace=resource_provider_namespace)
2204    return client.list_all()
2205
2206
2207def register_feature(client, resource_provider_namespace, feature_name):
2208    logger.warning("Once the feature '%s' is registered, invoking 'az provider register -n %s' is required "
2209                   "to get the change propagated", feature_name, resource_provider_namespace)
2210    return client.register(resource_provider_namespace, feature_name)
2211
2212
2213def unregister_feature(client, resource_provider_namespace, feature_name):
2214    logger.warning("Once the feature '%s' is unregistered, invoking 'az provider register -n %s' is required "
2215                   "to get the change propagated", feature_name, resource_provider_namespace)
2216    return client.unregister(resource_provider_namespace, feature_name)
2217
2218
2219def list_feature_registrations(client, resource_provider_namespace=None):
2220    if resource_provider_namespace:
2221        return client.list_by_subscription(provider_namespace=resource_provider_namespace)
2222    return client.list_all_by_subscription()
2223
2224
2225def create_feature_registration(client, resource_provider_namespace, feature_name):
2226    return client.create_or_update(resource_provider_namespace, feature_name, {})
2227
2228
2229def delete_feature_registration(client, resource_provider_namespace, feature_name):
2230    return client.delete(resource_provider_namespace, feature_name)
2231
2232
2233# pylint: disable=inconsistent-return-statements,too-many-locals
2234def create_policy_assignment(cmd, policy=None, policy_set_definition=None,
2235                             name=None, display_name=None, params=None,
2236                             resource_group_name=None, scope=None, sku=None,
2237                             not_scopes=None, location=None, assign_identity=None,
2238                             identity_scope=None, identity_role='Contributor', enforcement_mode='Default',
2239                             description=None):
2240    """Creates a policy assignment
2241    :param not_scopes: Space-separated scopes where the policy assignment does not apply.
2242    """
2243    if bool(policy) == bool(policy_set_definition):
2244        raise ArgumentUsageError('usage error: --policy NAME_OR_ID | '
2245                                 '--policy-set-definition NAME_OR_ID')
2246    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2247    subscription_id = get_subscription_id(cmd.cli_ctx)
2248    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2249    policy_id = _resolve_policy_id(cmd, policy, policy_set_definition, policy_client)
2250    params = _load_file_string_or_uri(params, 'params', False)
2251
2252    PolicyAssignment = cmd.get_models('PolicyAssignment')
2253    assignment = PolicyAssignment(display_name=display_name, policy_definition_id=policy_id, scope=scope, enforcement_mode=enforcement_mode, description=description)
2254    assignment.parameters = params if params else None
2255
2256    if cmd.supported_api_version(min_api='2017-06-01-preview'):
2257        if not_scopes:
2258            kwargs_list = []
2259            for id_arg in not_scopes.split(' '):
2260                id_parts = parse_resource_id(id_arg)
2261                if id_parts.get('subscription') or _is_management_group_scope(id_arg):
2262                    kwargs_list.append(id_arg)
2263                else:
2264                    raise InvalidArgumentValueError("Invalid resource ID value in --not-scopes: '%s'" % id_arg)
2265            assignment.not_scopes = kwargs_list
2266
2267    if cmd.supported_api_version(min_api='2018-05-01'):
2268        if location:
2269            assignment.location = location
2270        identity = None
2271        if assign_identity is not None:
2272            identity = _build_identities_info(cmd, assign_identity)
2273        assignment.identity = identity
2274
2275    if name is None:
2276        name = (base64.urlsafe_b64encode(uuid.uuid4().bytes).decode())[:-2]
2277
2278    createdAssignment = policy_client.policy_assignments.create(scope, name, assignment)
2279
2280    # Create the identity's role assignment if requested
2281    if assign_identity is not None and identity_scope:
2282        from azure.cli.core.commands.arm import assign_identity as _assign_identity_helper
2283        _assign_identity_helper(cmd.cli_ctx, lambda: createdAssignment, lambda resource: createdAssignment, identity_role, identity_scope)
2284
2285    return createdAssignment
2286
2287
2288def _build_identities_info(cmd, identities):
2289    identities = identities or []
2290    ResourceIdentityType = cmd.get_models('ResourceIdentityType')
2291    identity_type = ResourceIdentityType.none
2292    if not identities or MSI_LOCAL_ID in identities:
2293        identity_type = ResourceIdentityType.system_assigned
2294    ResourceIdentity = cmd.get_models('Identity')
2295    return ResourceIdentity(type=identity_type)
2296
2297
2298def update_policy_assignment(cmd, name=None, display_name=None, params=None,
2299                             resource_group_name=None, scope=None, sku=None,
2300                             not_scopes=None, enforcement_mode=None, description=None):
2301    """Updates a policy assignment
2302    :param not_scopes: Space-separated scopes where the policy assignment does not apply.
2303    """
2304    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2305    subscription_id = get_subscription_id(cmd.cli_ctx)
2306    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2307    params = _load_file_string_or_uri(params, 'params', False)
2308
2309    existing_assignment = policy_client.policy_assignments.get(scope, name)
2310    PolicyAssignment = cmd.get_models('PolicyAssignment')
2311    assignment = PolicyAssignment(
2312        display_name=display_name if display_name is not None else existing_assignment.display_name,
2313        policy_definition_id=existing_assignment.policy_definition_id,
2314        scope=existing_assignment.scope,
2315        enforcement_mode=enforcement_mode if enforcement_mode is not None else existing_assignment.enforcement_mode,
2316        metadata=existing_assignment.metadata,
2317        parameters=params if params is not None else existing_assignment.parameters,
2318        description=description if description is not None else existing_assignment.description)
2319
2320    if cmd.supported_api_version(min_api='2017-06-01-preview'):
2321        kwargs_list = existing_assignment.not_scopes
2322        if not_scopes:
2323            kwargs_list = []
2324            for id_arg in not_scopes.split(' '):
2325                id_parts = parse_resource_id(id_arg)
2326                if id_parts.get('subscription') or _is_management_group_scope(id_arg):
2327                    kwargs_list.append(id_arg)
2328                else:
2329                    raise InvalidArgumentValueError("Invalid resource ID value in --not-scopes: '%s'" % id_arg)
2330        assignment.not_scopes = kwargs_list
2331
2332    if cmd.supported_api_version(min_api='2018-05-01'):
2333        assignment.location = existing_assignment.location
2334        assignment.identity = existing_assignment.identity
2335
2336    if cmd.supported_api_version(min_api='2020-09-01'):
2337        assignment.non_compliance_messages = existing_assignment.non_compliance_messages
2338
2339    return policy_client.policy_assignments.create(scope, name, assignment)
2340
2341
2342def delete_policy_assignment(cmd, name, resource_group_name=None, scope=None):
2343    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2344    subscription_id = get_subscription_id(cmd.cli_ctx)
2345    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2346    policy_client.policy_assignments.delete(scope, name)
2347
2348
2349def show_policy_assignment(cmd, name, resource_group_name=None, scope=None):
2350    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2351    subscription_id = get_subscription_id(cmd.cli_ctx)
2352    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2353    return policy_client.policy_assignments.get(scope, name)
2354
2355
2356def list_policy_assignment(cmd, disable_scope_strict_match=None, resource_group_name=None, scope=None):
2357    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2358    _scope = _build_policy_scope(get_subscription_id(cmd.cli_ctx),
2359                                 resource_group_name, scope)
2360    id_parts = parse_resource_id(_scope)
2361    subscription = id_parts.get('subscription')
2362    resource_group = id_parts.get('resource_group')
2363    resource_type = id_parts.get('child_type_1') or id_parts.get('type')
2364    resource_name = id_parts.get('child_name_1') or id_parts.get('name')
2365    management_group = _parse_management_group_id(scope)
2366
2367    if management_group:
2368        result = policy_client.policy_assignments.list_for_management_group(management_group_id=management_group, filter='atScope()')
2369    elif all([resource_type, resource_group, subscription]):
2370        namespace = id_parts.get('namespace')
2371        parent_resource_path = '' if not id_parts.get('child_name_1') else (id_parts['type'] + '/' + id_parts['name'])
2372        result = policy_client.policy_assignments.list_for_resource(
2373            resource_group, namespace,
2374            parent_resource_path, resource_type, resource_name)
2375    elif resource_group:
2376        result = policy_client.policy_assignments.list_for_resource_group(resource_group)
2377    elif subscription:
2378        result = policy_client.policy_assignments.list()
2379    elif scope:
2380        raise InvalidArgumentValueError('usage error `--scope`: must be a fully qualified ARM ID.')
2381    else:
2382        raise ArgumentUsageError('usage error: --scope ARM_ID | --resource-group NAME')
2383
2384    if not disable_scope_strict_match:
2385        result = [i for i in result if _scope.lower().strip('/') == i.scope.lower().strip('/')]
2386
2387    return result
2388
2389
2390def list_policy_non_compliance_message(cmd, name, scope=None, resource_group_name=None):
2391    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2392    subscription_id = get_subscription_id(cmd.cli_ctx)
2393    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2394    return policy_client.policy_assignments.get(scope, name).non_compliance_messages
2395
2396
2397def create_policy_non_compliance_message(cmd, name, message, scope=None, resource_group_name=None,
2398                                         policy_definition_reference_id=None):
2399    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2400    subscription_id = get_subscription_id(cmd.cli_ctx)
2401    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2402
2403    assignment = policy_client.policy_assignments.get(scope, name)
2404
2405    NonComplianceMessage = cmd.get_models('NonComplianceMessage')
2406    created_message = NonComplianceMessage(message=message, policy_definition_reference_id=policy_definition_reference_id)
2407    if not assignment.non_compliance_messages:
2408        assignment.non_compliance_messages = []
2409    assignment.non_compliance_messages.append(created_message)
2410
2411    return policy_client.policy_assignments.create(scope, name, assignment).non_compliance_messages
2412
2413
2414def delete_policy_non_compliance_message(cmd, name, message, scope=None, resource_group_name=None,
2415                                         policy_definition_reference_id=None):
2416    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2417    subscription_id = get_subscription_id(cmd.cli_ctx)
2418    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2419
2420    assignment = policy_client.policy_assignments.get(scope, name)
2421
2422    NonComplianceMessage = cmd.get_models('NonComplianceMessage')
2423    message_to_remove = NonComplianceMessage(message=message, policy_definition_reference_id=policy_definition_reference_id)
2424    if assignment.non_compliance_messages:
2425        assignment.non_compliance_messages = [existingMessage for existingMessage in assignment.non_compliance_messages if not _is_non_compliance_message_equivalent(existingMessage, message_to_remove)]
2426
2427    return policy_client.policy_assignments.create(scope, name, assignment).non_compliance_messages
2428
2429
2430def _is_non_compliance_message_equivalent(first, second):
2431    first_message = '' if first.message is None else first.message
2432    seccond_message = '' if second.message is None else second.message
2433    first_reference_id = '' if first.policy_definition_reference_id is None else first.policy_definition_reference_id
2434    second_reference_id = '' if second.policy_definition_reference_id is None else second.policy_definition_reference_id
2435
2436    return first_message.lower() == seccond_message.lower() and first_reference_id.lower() == second_reference_id.lower()
2437
2438
2439def set_identity(cmd, name, scope=None, resource_group_name=None, identity_role='Contributor', identity_scope=None):
2440    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2441    subscription_id = get_subscription_id(cmd.cli_ctx)
2442    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2443
2444    def getter():
2445        return policy_client.policy_assignments.get(scope, name)
2446
2447    def setter(policyAssignment):
2448        policyAssignment.identity = _build_identities_info(cmd, [MSI_LOCAL_ID])
2449        return policy_client.policy_assignments.create(scope, name, policyAssignment)
2450
2451    from azure.cli.core.commands.arm import assign_identity as _assign_identity_helper
2452    updatedAssignment = _assign_identity_helper(cmd.cli_ctx, getter, setter, identity_role, identity_scope)
2453    return updatedAssignment.identity
2454
2455
2456def show_identity(cmd, name, scope=None, resource_group_name=None):
2457    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2458    subscription_id = get_subscription_id(cmd.cli_ctx)
2459    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2460    return policy_client.policy_assignments.get(scope, name).identity
2461
2462
2463def remove_identity(cmd, name, scope=None, resource_group_name=None):
2464    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2465    subscription_id = get_subscription_id(cmd.cli_ctx)
2466    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2467    policyAssignment = policy_client.policy_assignments.get(scope, name)
2468
2469    ResourceIdentityType = cmd.get_models('ResourceIdentityType')
2470    ResourceIdentity = cmd.get_models('Identity')
2471    policyAssignment.identity = ResourceIdentity(type=ResourceIdentityType.none)
2472    policyAssignment = policy_client.policy_assignments.create(scope, name, policyAssignment)
2473    return policyAssignment.identity
2474
2475
2476def enforce_mutually_exclusive(subscription, management_group):
2477    if subscription and management_group:
2478        raise IncorrectUsageError('cannot provide both --subscription and --management-group')
2479
2480
2481def create_policy_definition(cmd, name, rules=None, params=None, display_name=None, description=None, mode=None,
2482                             metadata=None, subscription=None, management_group=None):
2483    rules = _load_file_string_or_uri(rules, 'rules')
2484    params = _load_file_string_or_uri(params, 'params', False)
2485
2486    PolicyDefinition = cmd.get_models('PolicyDefinition')
2487    parameters = PolicyDefinition(policy_rule=rules, parameters=params, description=description,
2488                                  display_name=display_name)
2489    if cmd.supported_api_version(min_api='2016-12-01'):
2490        parameters.mode = mode
2491    if cmd.supported_api_version(min_api='2017-06-01-preview'):
2492        parameters.metadata = metadata
2493    if cmd.supported_api_version(min_api='2018-03-01'):
2494        enforce_mutually_exclusive(subscription, management_group)
2495        if management_group:
2496            policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2497            return policy_client.policy_definitions.create_or_update_at_management_group(name, management_group, parameters)
2498        if subscription:
2499            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
2500            policy_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
2501                                                    subscription_id=subscription_id)
2502            return policy_client.policy_definitions.create_or_update(name, parameters)
2503
2504    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2505    return policy_client.policy_definitions.create_or_update(name, parameters)
2506
2507
2508def create_policy_setdefinition(cmd, name, definitions, params=None, display_name=None, description=None,
2509                                subscription=None, management_group=None, definition_groups=None, metadata=None):
2510
2511    definitions = _load_file_string_or_uri(definitions, 'definitions')
2512    params = _load_file_string_or_uri(params, 'params', False)
2513    definition_groups = _load_file_string_or_uri(definition_groups, 'definition_groups', False)
2514
2515    PolicySetDefinition = cmd.get_models('PolicySetDefinition')
2516    parameters = PolicySetDefinition(policy_definitions=definitions, parameters=params, description=description,
2517                                     display_name=display_name, policy_definition_groups=definition_groups)
2518
2519    if cmd.supported_api_version(min_api='2017-06-01-preview'):
2520        parameters.metadata = metadata
2521    if cmd.supported_api_version(min_api='2018-03-01'):
2522        enforce_mutually_exclusive(subscription, management_group)
2523        if management_group:
2524            policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2525            return policy_client.policy_set_definitions.create_or_update_at_management_group(name, management_group, parameters)
2526        if subscription:
2527            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
2528            policy_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
2529                                                    subscription_id=subscription_id)
2530            return policy_client.policy_set_definitions.create_or_update(name, parameters)
2531
2532    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2533    return policy_client.policy_set_definitions.create_or_update(name, parameters)
2534
2535
2536def get_policy_definition(cmd, policy_definition_name, subscription=None, management_group=None):
2537    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2538    return _get_custom_or_builtin_policy(cmd, policy_client, policy_definition_name, subscription, management_group)
2539
2540
2541def get_policy_setdefinition(cmd, policy_set_definition_name, subscription=None, management_group=None):
2542    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2543    return _get_custom_or_builtin_policy(cmd, policy_client, policy_set_definition_name, subscription, management_group, True)
2544
2545
2546def list_policy_definition(cmd, subscription=None, management_group=None):
2547
2548    if cmd.supported_api_version(min_api='2018-03-01'):
2549        enforce_mutually_exclusive(subscription, management_group)
2550        if management_group:
2551            policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2552            return policy_client.policy_definitions.list_by_management_group(management_group)
2553        if subscription:
2554            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
2555            policy_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
2556                                                    subscription_id=subscription_id)
2557            return policy_client.policy_definitions.list()
2558
2559    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2560    return policy_client.policy_definitions.list()
2561
2562
2563def list_policy_setdefinition(cmd, subscription=None, management_group=None):
2564    if cmd.supported_api_version(min_api='2018-03-01'):
2565        enforce_mutually_exclusive(subscription, management_group)
2566        if management_group:
2567            policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2568            return policy_client.policy_set_definitions.list_by_management_group(management_group)
2569        if subscription:
2570            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
2571            policy_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
2572                                                    subscription_id=subscription_id)
2573            return policy_client.policy_set_definitions.list()
2574
2575    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2576    return policy_client.policy_set_definitions.list()
2577
2578
2579def delete_policy_definition(cmd, policy_definition_name, subscription=None, management_group=None):
2580    if cmd.supported_api_version(min_api='2018-03-01'):
2581        enforce_mutually_exclusive(subscription, management_group)
2582        if management_group:
2583            policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2584            return policy_client.policy_definitions.delete_at_management_group(policy_definition_name, management_group)
2585        if subscription:
2586            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
2587            policy_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
2588                                                    subscription_id=subscription_id)
2589            return policy_client.policy_definitions.delete(policy_definition_name)
2590
2591    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2592    return policy_client.policy_definitions.delete(policy_definition_name)
2593
2594
2595def delete_policy_setdefinition(cmd, policy_set_definition_name, subscription=None, management_group=None):
2596    if cmd.supported_api_version(min_api='2018-03-01'):
2597        enforce_mutually_exclusive(subscription, management_group)
2598        if management_group:
2599            policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2600            return policy_client.policy_set_definitions.delete_at_management_group(policy_set_definition_name,
2601                                                                                   management_group)
2602        if subscription:
2603            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
2604            policy_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
2605                                                    subscription_id=subscription_id)
2606            return policy_client.policy_set_definitions.delete(policy_set_definition_name)
2607
2608    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2609    return policy_client.policy_set_definitions.delete(policy_set_definition_name)
2610
2611
2612def update_policy_definition(cmd, policy_definition_name, rules=None, params=None,
2613                             display_name=None, description=None, metadata=None, mode=None,
2614                             subscription=None, management_group=None):
2615
2616    rules = _load_file_string_or_uri(rules, 'rules', False)
2617    params = _load_file_string_or_uri(params, 'params', False)
2618
2619    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2620    definition = _get_custom_or_builtin_policy(cmd, policy_client, policy_definition_name, subscription, management_group)
2621    # pylint: disable=line-too-long,no-member
2622
2623    PolicyDefinition = cmd.get_models('PolicyDefinition')
2624    parameters = PolicyDefinition(
2625        policy_rule=rules if rules is not None else definition.policy_rule,
2626        parameters=params if params is not None else definition.parameters,
2627        display_name=display_name if display_name is not None else definition.display_name,
2628        description=description if description is not None else definition.description,
2629        metadata=metadata if metadata is not None else definition.metadata)
2630
2631    if cmd.supported_api_version(min_api='2016-12-01'):
2632        parameters.mode = mode
2633    if cmd.supported_api_version(min_api='2018-03-01'):
2634        enforce_mutually_exclusive(subscription, management_group)
2635        if management_group:
2636            return policy_client.policy_definitions.create_or_update_at_management_group(policy_definition_name, management_group, parameters)
2637        if subscription:
2638            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
2639            policy_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
2640                                                    subscription_id=subscription_id)
2641            return policy_client.policy_definitions.create_or_update(policy_definition_name, parameters)
2642
2643    return policy_client.policy_definitions.create_or_update(policy_definition_name, parameters)
2644
2645
2646def update_policy_setdefinition(cmd, policy_set_definition_name, definitions=None, params=None,
2647                                display_name=None, description=None,
2648                                subscription=None, management_group=None, definition_groups=None, metadata=None):
2649
2650    definitions = _load_file_string_or_uri(definitions, 'definitions', False)
2651    params = _load_file_string_or_uri(params, 'params', False)
2652    definition_groups = _load_file_string_or_uri(definition_groups, 'definition_groups', False)
2653
2654    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2655    definition = _get_custom_or_builtin_policy(cmd, policy_client, policy_set_definition_name, subscription, management_group, True)
2656    # pylint: disable=line-too-long,no-member
2657    PolicySetDefinition = cmd.get_models('PolicySetDefinition')
2658    parameters = PolicySetDefinition(
2659        policy_definitions=definitions if definitions is not None else definition.policy_definitions,
2660        description=description if description is not None else definition.description,
2661        display_name=display_name if display_name is not None else definition.display_name,
2662        parameters=params if params is not None else definition.parameters,
2663        policy_definition_groups=definition_groups if definition_groups is not None else definition.policy_definition_groups,
2664        metadata=metadata if metadata is not None else definition.metadata)
2665
2666    if cmd.supported_api_version(min_api='2018-03-01'):
2667        enforce_mutually_exclusive(subscription, management_group)
2668        if management_group:
2669            return policy_client.policy_set_definitions.create_or_update_at_management_group(policy_set_definition_name, management_group, parameters)
2670        if subscription:
2671            subscription_id = _get_subscription_id_from_subscription(cmd.cli_ctx, subscription)
2672            policy_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_POLICY,
2673                                                    subscription_id=subscription_id)
2674            return policy_client.policy_set_definitions.create_or_update(policy_set_definition_name, parameters)
2675
2676    return policy_client.policy_set_definitions.create_or_update(policy_set_definition_name, parameters)
2677
2678
2679def create_policy_exemption(cmd, name, policy_assignment=None, exemption_category=None,
2680                            policy_definition_reference_ids=None, expires_on=None,
2681                            display_name=None, description=None, resource_group_name=None, scope=None,
2682                            metadata=None):
2683    if policy_assignment is None:
2684        raise RequiredArgumentMissingError('--policy_assignment is required')
2685    if exemption_category is None:
2686        raise RequiredArgumentMissingError('--exemption_category is required')
2687
2688    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2689    subscription_id = get_subscription_id(cmd.cli_ctx)
2690    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2691    PolicyExemption = cmd.get_models('PolicyExemption')
2692    exemption = PolicyExemption(policy_assignment_id=policy_assignment, policy_definition_reference_ids=policy_definition_reference_ids,
2693                                exemption_category=exemption_category, expires_on=expires_on,
2694                                display_name=display_name, description=description, metadata=metadata)
2695    createdExemption = policy_client.policy_exemptions.create_or_update(scope, name, exemption)
2696    return createdExemption
2697
2698
2699def update_policy_exemption(cmd, name, exemption_category=None,
2700                            policy_definition_reference_ids=None, expires_on=None,
2701                            display_name=None, description=None, resource_group_name=None, scope=None,
2702                            metadata=None):
2703    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2704    subscription_id = get_subscription_id(cmd.cli_ctx)
2705    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2706    PolicyExemption = cmd.get_models('PolicyExemption')
2707    exemption = policy_client.policy_exemptions.get(scope, name)
2708    parameters = PolicyExemption(
2709        policy_assignment_id=exemption.policy_assignment_id,
2710        policy_definition_reference_ids=policy_definition_reference_ids if policy_definition_reference_ids is not None else exemption.policy_definition_reference_ids,
2711        exemption_category=exemption_category if exemption_category is not None else exemption.exemption_category,
2712        expires_on=expires_on if expires_on is not None else exemption.expires_on,
2713        display_name=display_name if display_name is not None else exemption.display_name,
2714        description=description if description is not None else exemption.description,
2715        metadata=metadata if metadata is not None else exemption.metadata)
2716    updatedExemption = policy_client.policy_exemptions.create_or_update(scope, name, parameters)
2717    return updatedExemption
2718
2719
2720def delete_policy_exemption(cmd, name, resource_group_name=None, scope=None):
2721    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2722    subscription_id = get_subscription_id(cmd.cli_ctx)
2723    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2724    policy_client.policy_exemptions.delete(scope, name)
2725
2726
2727def get_policy_exemption(cmd, name, resource_group_name=None, scope=None):
2728    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2729    subscription_id = get_subscription_id(cmd.cli_ctx)
2730    scope = _build_policy_scope(subscription_id, resource_group_name, scope)
2731    return policy_client.policy_exemptions.get(scope, name)
2732
2733
2734def list_policy_exemption(cmd, disable_scope_strict_match=None, resource_group_name=None, scope=None):
2735    policy_client = _resource_policy_client_factory(cmd.cli_ctx)
2736    _scope = _build_policy_scope(get_subscription_id(cmd.cli_ctx),
2737                                 resource_group_name, scope)
2738    id_parts = parse_resource_id(_scope)
2739    subscription = id_parts.get('subscription')
2740    resource_group = id_parts.get('resource_group')
2741    resource_type = id_parts.get('child_type_1') or id_parts.get('type')
2742    resource_name = id_parts.get('child_name_1') or id_parts.get('name')
2743    management_group = _parse_management_group_id(scope)
2744
2745    if management_group:
2746        result = policy_client.policy_exemptions.list_for_management_group(management_group_id=management_group, filter='atScope()')
2747    elif all([resource_type, resource_group, subscription]):
2748        namespace = id_parts.get('namespace')
2749        parent_resource_path = '' if not id_parts.get('child_name_1') else (id_parts['type'] + '/' + id_parts['name'])
2750        result = policy_client.policy_exemptions.list_for_resource(
2751            resource_group, namespace,
2752            parent_resource_path, resource_type, resource_name)
2753    elif resource_group:
2754        result = policy_client.policy_exemptions.list_for_resource_group(resource_group)
2755    elif subscription:
2756        result = policy_client.policy_exemptions.list()
2757    elif scope:
2758        raise InvalidArgumentValueError('usage error `--scope`: must be a fully qualified ARM ID.')
2759    else:
2760        raise ArgumentUsageError('usage error: --scope ARM_ID | --resource-group NAME')
2761
2762    if not disable_scope_strict_match:
2763        result = [i for i in result if i.id.lower().strip('/').startswith(_scope.lower().strip('/') + "/providers/microsoft.authorization/policyexemptions")]
2764
2765    return result
2766
2767
2768def _register_rp(cli_ctx, subscription_id=None):
2769    rp = "Microsoft.Management"
2770    import time
2771    rcf = get_mgmt_service_client(
2772        cli_ctx,
2773        ResourceType.MGMT_RESOURCE_RESOURCES,
2774        subscription_id)
2775    rcf.providers.register(rp)
2776    while True:
2777        time.sleep(10)
2778        rp_info = rcf.providers.get(rp)
2779        if rp_info.registration_state == 'Registered':
2780            break
2781
2782
2783def _get_subscription_id_from_subscription(cli_ctx, subscription):  # pylint: disable=inconsistent-return-statements
2784    from azure.cli.core._profile import Profile
2785    profile = Profile(cli_ctx=cli_ctx)
2786    subscriptions_list = profile.load_cached_subscriptions()
2787    for sub in subscriptions_list:
2788        if subscription in (sub['id'], sub['name']):
2789            return sub['id']
2790    raise CLIError("Subscription not found in the current context.")
2791
2792
2793def _get_parent_id_from_parent(parent):
2794    if parent is None or _is_management_group_scope(parent):
2795        return parent
2796    return "/providers/Microsoft.Management/managementGroups/" + parent
2797
2798
2799def _is_management_group_scope(scope):
2800    return scope is not None and scope.lower().startswith("/providers/microsoft.management/managementgroups")
2801
2802
2803def cli_managementgroups_group_list(cmd, client):
2804    _register_rp(cmd.cli_ctx)
2805    return client.list()
2806
2807
2808def cli_managementgroups_group_show(
2809        cmd,
2810        client,
2811        group_name,
2812        expand=False,
2813        recurse=False):
2814    _register_rp(cmd.cli_ctx)
2815    if expand:
2816        return client.get(group_name, "children", recurse)
2817    return client.get(group_name)
2818
2819
2820def cli_managementgroups_group_create(
2821        cmd,
2822        client,
2823        group_name,
2824        display_name=None,
2825        parent=None):
2826    _register_rp(cmd.cli_ctx)
2827    parent_id = _get_parent_id_from_parent(parent)
2828    from azure.mgmt.managementgroups.models import (
2829        CreateManagementGroupRequest, CreateManagementGroupDetails, CreateParentGroupInfo)
2830    create_parent_grp_info = CreateParentGroupInfo(id=parent_id)
2831    create_mgmt_grp_details = CreateManagementGroupDetails(parent=create_parent_grp_info)
2832    create_mgmt_grp_request = CreateManagementGroupRequest(
2833        name=group_name,
2834        display_name=display_name,
2835        details=create_mgmt_grp_details)
2836    return client.create_or_update(group_name, create_mgmt_grp_request)
2837
2838
2839def cli_managementgroups_group_update_custom_func(
2840        instance,
2841        display_name=None,
2842        parent_id=None):
2843    parent_id = _get_parent_id_from_parent(parent_id)
2844    instance.display_name = display_name
2845    instance.parent_id = parent_id
2846    return instance
2847
2848
2849def cli_managementgroups_group_update_get():
2850    from azure.mgmt.managementgroups.models import PatchManagementGroupRequest
2851    update_parameters = PatchManagementGroupRequest(display_name=None, parent_id=None)
2852    return update_parameters
2853
2854
2855def cli_managementgroups_group_update_set(
2856        cmd, client, group_name, parameters=None):
2857    return client.update(group_name, parameters)
2858
2859
2860def cli_managementgroups_group_delete(cmd, client, group_name):
2861    _register_rp(cmd.cli_ctx)
2862    return client.delete(group_name)
2863
2864
2865def cli_managementgroups_subscription_add(
2866        cmd, client, group_name, subscription):
2867    subscription_id = _get_subscription_id_from_subscription(
2868        cmd.cli_ctx, subscription)
2869    return client.create(group_name, subscription_id)
2870
2871
2872def cli_managementgroups_subscription_remove(
2873        cmd, client, group_name, subscription):
2874    subscription_id = _get_subscription_id_from_subscription(
2875        cmd.cli_ctx, subscription)
2876    return client.delete(group_name, subscription_id)
2877
2878
2879# region Locks
2880
2881
2882def _validate_lock_params_match_lock(
2883        lock_client, name, resource_group, resource_provider_namespace, parent_resource_path,
2884        resource_type, resource_name):
2885    """
2886    Locks are scoped to subscription, resource group or resource.
2887    However, the az list command returns all locks for the current scopes
2888    and all lower scopes (e.g. resource group level also includes resource locks).
2889    This can lead to a confusing user experience where the user specifies a lock
2890    name and assumes that it will work, even if they haven't given the right
2891    scope. This function attempts to validate the parameters and help the
2892    user find the right scope, by first finding the lock, and then infering
2893    what it's parameters should be.
2894    """
2895    locks = lock_client.management_locks.list_at_subscription_level()
2896    found_count = 0  # locks at different levels can have the same name
2897    lock_resource_id = None
2898    for lock in locks:
2899        if lock.name == name:
2900            found_count = found_count + 1
2901            lock_resource_id = lock.id
2902    if found_count == 1:
2903        # If we only found one lock, let's validate that the parameters are correct,
2904        # if we found more than one, we'll assume the user knows what they're doing
2905        # TODO: Add validation for that case too?
2906        resource = parse_resource_id(lock_resource_id)
2907        _resource_group = resource.get('resource_group', None)
2908        _resource_namespace = resource.get('namespace', None)
2909        if _resource_group is None:
2910            return
2911        if resource_group != _resource_group:
2912            raise CLIError(
2913                'Unexpected --resource-group for lock {}, expected {}'.format(
2914                    name, _resource_group))
2915        if _resource_namespace is None or _resource_namespace == 'Microsoft.Authorization':
2916            return
2917        if resource_provider_namespace != _resource_namespace:
2918            raise CLIError(
2919                'Unexpected --namespace for lock {}, expected {}'.format(name, _resource_namespace))
2920        if resource.get('child_type_2', None) is None:
2921            _resource_type = resource.get('type', None)
2922            _resource_name = resource.get('name', None)
2923        else:
2924            if resource.get('child_type_3', None) is None:
2925                _resource_type = resource.get('child_type_1', None)
2926                _resource_name = resource.get('child_name_1', None)
2927                parent = (resource['type'] + '/' + resource['name'])
2928            else:
2929                _resource_type = resource.get('child_type_2', None)
2930                _resource_name = resource.get('child_name_2', None)
2931                parent = (resource['type'] + '/' + resource['name'] + '/' +
2932                          resource['child_type_1'] + '/' + resource['child_name_1'])
2933            if parent != parent_resource_path:
2934                raise CLIError(
2935                    'Unexpected --parent for lock {}, expected {}'.format(
2936                        name, parent))
2937        if resource_type != _resource_type:
2938            raise CLIError('Unexpected --resource-type for lock {}, expected {}'.format(
2939                name, _resource_type))
2940        if resource_name != _resource_name:
2941            raise CLIError('Unexpected --resource-name for lock {}, expected {}'.format(
2942                name, _resource_name))
2943
2944
2945def list_locks(cmd, resource_group=None,
2946               resource_provider_namespace=None, parent_resource_path=None, resource_type=None,
2947               resource_name=None, filter_string=None):
2948    """
2949    :param resource_provider_namespace: Name of a resource provider.
2950    :type resource_provider_namespace: str
2951    :param parent_resource_path: Path to a parent resource
2952    :type parent_resource_path: str
2953    :param resource_type: The type for the resource with the lock.
2954    :type resource_type: str
2955    :param resource_name: Name of a resource that has a lock.
2956    :type resource_name: str
2957    :param filter_string: A query filter to use to restrict the results.
2958    :type filter_string: str
2959    """
2960    lock_client = _resource_lock_client_factory(cmd.cli_ctx)
2961    lock_resource = _extract_lock_params(resource_group, resource_provider_namespace,
2962                                         resource_type, resource_name)
2963    resource_group = lock_resource[0]
2964    resource_name = lock_resource[1]
2965    resource_provider_namespace = lock_resource[2]
2966    resource_type = lock_resource[3]
2967
2968    if resource_group is None:
2969        return lock_client.management_locks.list_at_subscription_level(filter=filter_string)
2970    if resource_name is None:
2971        return lock_client.management_locks.list_at_resource_group_level(
2972            resource_group, filter=filter_string)
2973    return lock_client.management_locks.list_at_resource_level(
2974        resource_group, resource_provider_namespace, parent_resource_path or '', resource_type,
2975        resource_name, filter=filter_string)
2976
2977
2978# pylint: disable=inconsistent-return-statements
2979def get_lock(cmd, lock_name=None, resource_group=None, resource_provider_namespace=None,
2980             parent_resource_path=None, resource_type=None, resource_name=None, ids=None):
2981    """
2982    :param name: The name of the lock.
2983    :type name: str
2984    """
2985    if ids:
2986        kwargs_list = []
2987        for id_arg in ids:
2988            try:
2989                kwargs_list.append(_parse_lock_id(id_arg))
2990            except AttributeError:
2991                logger.error('az lock show: error: argument --ids: invalid ResourceId value: \'%s\'', id_arg)
2992                return
2993        results = [get_lock(cmd, **kwargs) for kwargs in kwargs_list]
2994        return results[0] if len(results) == 1 else results
2995
2996    lock_client = _resource_lock_client_factory(cmd.cli_ctx)
2997
2998    lock_resource = _extract_lock_params(resource_group, resource_provider_namespace,
2999                                         resource_type, resource_name)
3000
3001    resource_group = lock_resource[0]
3002    resource_name = lock_resource[1]
3003    resource_provider_namespace = lock_resource[2]
3004    resource_type = lock_resource[3]
3005
3006    _validate_lock_params_match_lock(lock_client, lock_name, resource_group,
3007                                     resource_provider_namespace, parent_resource_path,
3008                                     resource_type, resource_name)
3009
3010    if resource_group is None:
3011        return _call_subscription_get(cmd, lock_client, lock_name)
3012    if resource_name is None:
3013        return lock_client.management_locks.get_at_resource_group_level(resource_group, lock_name)
3014    if cmd.supported_api_version(max_api='2015-01-01'):
3015        lock_list = list_locks(resource_group, resource_provider_namespace, parent_resource_path,
3016                               resource_type, resource_name)
3017        return next((lock for lock in lock_list if lock.name == lock_name), None)
3018    return lock_client.management_locks.get_at_resource_level(
3019        resource_group, resource_provider_namespace,
3020        parent_resource_path or '', resource_type, resource_name, lock_name)
3021
3022
3023# pylint: disable=inconsistent-return-statements
3024def delete_lock(cmd, lock_name=None, resource_group=None, resource_provider_namespace=None,
3025                parent_resource_path=None, resource_type=None, resource_name=None, ids=None):
3026    """
3027    :param name: The name of the lock.
3028    :type name: str
3029    :param resource_provider_namespace: Name of a resource provider.
3030    :type resource_provider_namespace: str
3031    :param parent_resource_path: Path to a parent resource
3032    :type parent_resource_path: str
3033    :param resource_type: The type for the resource with the lock.
3034    :type resource_type: str
3035    :param resource_name: Name of a resource that has a lock.
3036    :type resource_name: str
3037    """
3038    if ids:
3039        kwargs_list = []
3040        for id_arg in ids:
3041            try:
3042                kwargs_list.append(_parse_lock_id(id_arg))
3043            except AttributeError:
3044                logger.error('az lock delete: error: argument --ids: invalid ResourceId value: \'%s\'', id_arg)
3045                return
3046        results = [delete_lock(cmd, **kwargs) for kwargs in kwargs_list]
3047        return results[0] if len(results) == 1 else results
3048
3049    lock_client = _resource_lock_client_factory(cmd.cli_ctx)
3050    lock_resource = _extract_lock_params(resource_group, resource_provider_namespace,
3051                                         resource_type, resource_name)
3052    resource_group = lock_resource[0]
3053    resource_name = lock_resource[1]
3054    resource_provider_namespace = lock_resource[2]
3055    resource_type = lock_resource[3]
3056
3057    _validate_lock_params_match_lock(lock_client, lock_name, resource_group,
3058                                     resource_provider_namespace, parent_resource_path,
3059                                     resource_type, resource_name)
3060
3061    if resource_group is None:
3062        return lock_client.management_locks.delete_at_subscription_level(lock_name)
3063    if resource_name is None:
3064        return lock_client.management_locks.delete_at_resource_group_level(
3065            resource_group, lock_name)
3066    return lock_client.management_locks.delete_at_resource_level(
3067        resource_group, resource_provider_namespace, parent_resource_path or '', resource_type,
3068        resource_name, lock_name)
3069
3070
3071def create_lock(cmd, lock_name, level,
3072                resource_group=None, resource_provider_namespace=None, notes=None,
3073                parent_resource_path=None, resource_type=None, resource_name=None):
3074    """
3075    :param name: The name of the lock.
3076    :type name: str
3077    :param resource_provider_namespace: Name of a resource provider.
3078    :type resource_provider_namespace: str
3079    :param parent_resource_path: Path to a parent resource
3080    :type parent_resource_path: str
3081    :param resource_type: The type for the resource with the lock.
3082    :type resource_type: str
3083    :param resource_name: Name of a resource that has a lock.
3084    :type resource_name: str
3085    :param notes: Notes about this lock.
3086    :type notes: str
3087    """
3088    ManagementLockObject = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_LOCKS, 'ManagementLockObject', mod='models')
3089    parameters = ManagementLockObject(level=level, notes=notes, name=lock_name)
3090
3091    lock_client = _resource_lock_client_factory(cmd.cli_ctx)
3092    lock_resource = _extract_lock_params(resource_group, resource_provider_namespace,
3093                                         resource_type, resource_name)
3094    resource_group = lock_resource[0]
3095    resource_name = lock_resource[1]
3096    resource_provider_namespace = lock_resource[2]
3097    resource_type = lock_resource[3]
3098
3099    if resource_group is None:
3100        return lock_client.management_locks.create_or_update_at_subscription_level(lock_name, parameters)
3101
3102    if resource_name is None:
3103        return lock_client.management_locks.create_or_update_at_resource_group_level(
3104            resource_group, lock_name, parameters)
3105
3106    return lock_client.management_locks.create_or_update_at_resource_level(
3107        resource_group, resource_provider_namespace, parent_resource_path or '', resource_type,
3108        resource_name, lock_name, parameters)
3109
3110
3111# pylint: disable=inconsistent-return-statements
3112def update_lock(cmd, lock_name=None, resource_group=None, resource_provider_namespace=None, notes=None,
3113                parent_resource_path=None, resource_type=None, resource_name=None, level=None, ids=None):
3114    """
3115    Allows updates to the lock-type(level) and the notes of the lock
3116    """
3117    if ids:
3118        kwargs_list = []
3119        for id_arg in ids:
3120            try:
3121                kwargs_list.append(_parse_lock_id(id_arg))
3122            except AttributeError:
3123                logger.error('az lock update: error: argument --ids: invalid ResourceId value: \'%s\'', id_arg)
3124                return
3125        results = [update_lock(cmd, level=level, notes=notes, **kwargs) for kwargs in kwargs_list]
3126        return results[0] if len(results) == 1 else results
3127
3128    lock_client = _resource_lock_client_factory(cmd.cli_ctx)
3129
3130    lock_resource = _extract_lock_params(resource_group, resource_provider_namespace,
3131                                         resource_type, resource_name)
3132
3133    resource_group = lock_resource[0]
3134    resource_name = lock_resource[1]
3135    resource_provider_namespace = lock_resource[2]
3136    resource_type = lock_resource[3]
3137
3138    _validate_lock_params_match_lock(lock_client, lock_name, resource_group, resource_provider_namespace,
3139                                     parent_resource_path, resource_type, resource_name)
3140
3141    if resource_group is None:
3142        params = _call_subscription_get(cmd, lock_client, lock_name)
3143        _update_lock_parameters(params, level, notes)
3144        return lock_client.management_locks.create_or_update_at_subscription_level(lock_name, params)
3145    if resource_name is None:
3146        params = lock_client.management_locks.get_at_resource_group_level(resource_group, lock_name)
3147        _update_lock_parameters(params, level, notes)
3148        return lock_client.management_locks.create_or_update_at_resource_group_level(
3149            resource_group, lock_name, params)
3150    if cmd.supported_api_version(max_api='2015-01-01'):
3151        lock_list = list_locks(resource_group, resource_provider_namespace, parent_resource_path,
3152                               resource_type, resource_name)
3153        return next((lock for lock in lock_list if lock.name == lock_name), None)
3154    params = lock_client.management_locks.get_at_resource_level(
3155        resource_group, resource_provider_namespace, parent_resource_path or '', resource_type,
3156        resource_name, lock_name)
3157    _update_lock_parameters(params, level, notes)
3158    return lock_client.management_locks.create_or_update_at_resource_level(
3159        resource_group, resource_provider_namespace, parent_resource_path or '', resource_type,
3160        resource_name, lock_name, params)
3161# endregion
3162
3163
3164# region ResourceLinks
3165def create_resource_link(cmd, link_id, target_id, notes=None):
3166    links_client = _resource_links_client_factory(cmd.cli_ctx).resource_links
3167
3168    ResourceLink = cmd.get_models('ResourceLink')
3169    ResourceLinkProperties = cmd.get_models('ResourceLinkProperties')
3170    properties = ResourceLinkProperties(target_id=target_id, notes=notes)
3171    resource_link = ResourceLink(properties=properties)
3172    links_client.create_or_update(link_id, resource_link)
3173
3174
3175def update_resource_link(cmd, link_id, target_id=None, notes=None):
3176    links_client = _resource_links_client_factory(cmd.cli_ctx).resource_links
3177    params = links_client.get(link_id)
3178
3179    ResourceLink = cmd.get_models('ResourceLink')
3180    ResourceLinkProperties = cmd.get_models('ResourceLinkProperties')
3181    # pylint: disable=no-member
3182    properties = ResourceLinkProperties(
3183        target_id=target_id if target_id is not None else params.properties.target_id,
3184        notes=notes if notes is not None else params.properties.notes)
3185    resource_link = ResourceLink(properties=properties)
3186    links_client.create_or_update(link_id, resource_link)
3187
3188
3189def list_resource_links(cmd, scope=None, filter_string=None):
3190    links_client = _resource_links_client_factory(cmd.cli_ctx).resource_links
3191    if scope is not None:
3192        return links_client.list_at_source_scope(scope, filter=filter_string)
3193    return links_client.list_at_subscription(filter=filter_string)
3194# endregion
3195
3196
3197# region tags
3198def get_tag_at_scope(cmd, resource_id=None):
3199    rcf = _resource_client_factory(cmd.cli_ctx)
3200    if resource_id is not None:
3201        return rcf.tags.get_at_scope(scope=resource_id)
3202
3203    return rcf.tags.list()
3204
3205
3206def create_or_update_tag_at_scope(cmd, resource_id=None, tags=None, tag_name=None):
3207    rcf = _resource_client_factory(cmd.cli_ctx)
3208    if resource_id is not None:
3209        if not tags:
3210            raise IncorrectUsageError("Tags could not be empty.")
3211        Tags = cmd.get_models('Tags')
3212        tag_obj = Tags(tags=tags)
3213        TagsResource = cmd.get_models('TagsResource')
3214        tags_resource = TagsResource(properties=tag_obj)
3215        return rcf.tags.create_or_update_at_scope(scope=resource_id, parameters=tags_resource)
3216
3217    return rcf.tags.create_or_update(tag_name=tag_name)
3218
3219
3220def delete_tag_at_scope(cmd, resource_id=None, tag_name=None):
3221    rcf = _resource_client_factory(cmd.cli_ctx)
3222    if resource_id is not None:
3223        return rcf.tags.delete_at_scope(scope=resource_id)
3224
3225    return rcf.tags.delete(tag_name=tag_name)
3226
3227
3228def update_tag_at_scope(cmd, resource_id, tags, operation):
3229    rcf = _resource_client_factory(cmd.cli_ctx)
3230    if not tags:
3231        raise IncorrectUsageError("Tags could not be empty.")
3232    Tags = cmd.get_models('Tags')
3233    tag_obj = Tags(tags=tags)
3234    TagsPatchResource = cmd.get_models('TagsPatchResource')
3235    tags_resource = TagsPatchResource(properties=tag_obj, operation=operation)
3236    return rcf.tags.update_at_scope(scope=resource_id, parameters=tags_resource)
3237# endregion
3238
3239
3240class _ResourceUtils:  # pylint: disable=too-many-instance-attributes
3241    def __init__(self, cli_ctx,
3242                 resource_group_name=None, resource_provider_namespace=None,
3243                 parent_resource_path=None, resource_type=None, resource_name=None,
3244                 resource_id=None, api_version=None, rcf=None, latest_include_preview=False):
3245        # if the resouce_type is in format 'namespace/type' split it.
3246        # (we don't have to do this, but commands like 'vm show' returns such values)
3247        if resource_type and not resource_provider_namespace and not parent_resource_path:
3248            parts = resource_type.split('/')
3249            if len(parts) > 1:
3250                resource_provider_namespace = parts[0]
3251                resource_type = parts[1]
3252
3253        self.rcf = rcf or _resource_client_factory(cli_ctx)
3254        if api_version is None:
3255            if resource_id:
3256                api_version = _ResourceUtils._resolve_api_version_by_id(self.rcf, resource_id,
3257                                                                        latest_include_preview=latest_include_preview)
3258            else:
3259                _validate_resource_inputs(resource_group_name, resource_provider_namespace,
3260                                          resource_type, resource_name)
3261                api_version = _ResourceUtils.resolve_api_version(self.rcf,
3262                                                                 resource_provider_namespace,
3263                                                                 parent_resource_path,
3264                                                                 resource_type,
3265                                                                 latest_include_preview=latest_include_preview)
3266
3267        self.resource_group_name = resource_group_name
3268        self.resource_provider_namespace = resource_provider_namespace
3269        self.parent_resource_path = parent_resource_path if parent_resource_path else ''
3270        self.resource_type = resource_type
3271        self.resource_name = resource_name
3272        self.resource_id = resource_id
3273        self.api_version = api_version
3274
3275    def create_resource(self, properties, location, is_full_object):
3276        try:
3277            res = json.loads(properties)
3278        except json.decoder.JSONDecodeError as ex:
3279            raise CLIError('Error parsing JSON.\n{}\n{}'.format(properties, ex))
3280
3281        if not is_full_object:
3282            if not location:
3283                if self.resource_id:
3284                    rg_name = parse_resource_id(self.resource_id)['resource_group']
3285                else:
3286                    rg_name = self.resource_group_name
3287                location = self.rcf.resource_groups.get(rg_name).location
3288
3289            res = GenericResource(location=location, properties=res)
3290        elif res.get('location', None) is None:
3291            raise IncorrectUsageError("location of the resource is required")
3292
3293        if self.resource_id:
3294            resource = self.rcf.resources.begin_create_or_update_by_id(self.resource_id,
3295                                                                       self.api_version,
3296                                                                       res)
3297        else:
3298            resource = self.rcf.resources.begin_create_or_update(self.resource_group_name,
3299                                                                 self.resource_provider_namespace,
3300                                                                 self.parent_resource_path,
3301                                                                 self.resource_type,
3302                                                                 self.resource_name,
3303                                                                 self.api_version,
3304                                                                 res)
3305        return resource
3306
3307    def get_resource(self, include_response_body=False):
3308
3309        def add_response_body(pipeline_response, deserialized, *kwargs):
3310            resource = deserialized
3311            response_body = {}
3312            try:
3313                response_body = pipeline_response.http_response.internal_response.content.decode()
3314            except AttributeError:
3315                pass
3316            setattr(resource, 'response_body', json.loads(response_body))
3317            return resource
3318
3319        cls = None
3320        if include_response_body:
3321            cls = add_response_body
3322
3323        if self.resource_id:
3324            resource = self.rcf.resources.get_by_id(self.resource_id, self.api_version, cls=cls)
3325        else:
3326            resource = self.rcf.resources.get(self.resource_group_name,
3327                                              self.resource_provider_namespace,
3328                                              self.parent_resource_path,
3329                                              self.resource_type,
3330                                              self.resource_name,
3331                                              self.api_version,
3332                                              cls=cls)
3333
3334        return resource
3335
3336    def delete(self):
3337        if self.resource_id:
3338            return self.rcf.resources.begin_delete_by_id(self.resource_id, self.api_version)
3339        return self.rcf.resources.begin_delete(self.resource_group_name,
3340                                               self.resource_provider_namespace,
3341                                               self.parent_resource_path,
3342                                               self.resource_type,
3343                                               self.resource_name,
3344                                               self.api_version)
3345
3346    def update(self, parameters):
3347        if self.resource_id:
3348            return self.rcf.resources.begin_create_or_update_by_id(self.resource_id,
3349                                                                   self.api_version,
3350                                                                   parameters)
3351        return self.rcf.resources.begin_create_or_update(self.resource_group_name,
3352                                                         self.resource_provider_namespace,
3353                                                         self.parent_resource_path,
3354                                                         self.resource_type,
3355                                                         self.resource_name,
3356                                                         self.api_version,
3357                                                         parameters)
3358
3359    def tag(self, tags, is_incremental=False):
3360        resource = self.get_resource()
3361
3362        if is_incremental is True:
3363            if not tags:
3364                raise CLIError("When modifying tag incrementally, the parameters of tag must have specific values.")
3365            if resource.tags:
3366                resource.tags.update(tags)
3367                tags = resource.tags
3368
3369        # please add the service type that needs to be requested with PATCH type here
3370        # for example: the properties of RecoveryServices/vaults must be filled, and a PUT request that passes back
3371        # to properties will fail due to the lack of properties, so the PATCH type should be used
3372        need_patch_service = ['Microsoft.RecoveryServices/vaults', 'Microsoft.Resources/resourceGroups',
3373                              'Microsoft.ContainerRegistry/registries/webhooks',
3374                              'Microsoft.ContainerInstance/containerGroups',
3375                              'Microsoft.Network/publicIPAddresses']
3376
3377        if resource is not None and resource.type in need_patch_service:
3378            parameters = GenericResource(tags=tags)
3379            if self.resource_id:
3380                return self.rcf.resources.begin_update_by_id(self.resource_id, self.api_version, parameters)
3381            return self.rcf.resources.begin_update(self.resource_group_name,
3382                                                   self.resource_provider_namespace,
3383                                                   self.parent_resource_path,
3384                                                   self.resource_type,
3385                                                   self.resource_name,
3386                                                   self.api_version,
3387                                                   parameters)
3388
3389        # pylint: disable=no-member
3390        parameters = GenericResource(
3391            location=resource.location,
3392            tags=tags,
3393            plan=resource.plan,
3394            properties=resource.properties,
3395            kind=resource.kind,
3396            managed_by=resource.managed_by,
3397            sku=resource.sku,
3398            identity=resource.identity)
3399
3400        if self.resource_id:
3401            return self.rcf.resources.begin_create_or_update_by_id(self.resource_id, self.api_version,
3402                                                                   parameters)
3403        return self.rcf.resources.begin_create_or_update(self.resource_group_name,
3404                                                         self.resource_provider_namespace,
3405                                                         self.parent_resource_path,
3406                                                         self.resource_type,
3407                                                         self.resource_name,
3408                                                         self.api_version,
3409                                                         parameters)
3410
3411    def invoke_action(self, action, request_body):
3412        """
3413        Formats Url if none provided and sends the POST request with the url and request-body.
3414        """
3415        from msrestazure.azure_operation import AzureOperationPoller
3416
3417        query_parameters = {}
3418        serialize = self.rcf.resources._serialize  # pylint: disable=protected-access
3419        client = self.rcf.resources._client  # pylint: disable=protected-access
3420
3421        url = '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/' \
3422            '{resourceProviderNamespace}/{parentResourcePath}/{resourceType}/{resourceName}/{action}'
3423
3424        if self.resource_id:
3425            url = client.format_url(
3426                '{resource_id}/{action}',
3427                resource_id=self.resource_id,
3428                action=serialize.url("action", action, 'str'))
3429        else:
3430            url = client.format_url(
3431                url,
3432                resourceGroupName=serialize.url(
3433                    "resource_group_name", self.resource_group_name, 'str',
3434                    max_length=90, min_length=1, pattern=r'^[-\w\._\(\)]+$'),
3435                resourceProviderNamespace=serialize.url(
3436                    "resource_provider_namespace", self.resource_provider_namespace, 'str'),
3437                parentResourcePath=serialize.url(
3438                    "parent_resource_path", self.parent_resource_path, 'str', skip_quote=True),
3439                resourceType=serialize.url("resource_type", self.resource_type, 'str', skip_quote=True),
3440                resourceName=serialize.url("resource_name", self.resource_name, 'str'),
3441                subscriptionId=serialize.url(
3442                    "self._config.subscription_id", self.rcf.resources._config.subscription_id, 'str'),
3443                action=serialize.url("action", action, 'str'))
3444
3445        # Construct parameters
3446        query_parameters['api-version'] = serialize.query("api_version", self.api_version, 'str')
3447
3448        # Construct headers
3449        header_parameters = {}
3450        header_parameters['Content-Type'] = 'application/json; charset=utf-8'
3451        # This value of accept_language comes from the fixed configuration in the AzureConfiguration in track 1.
3452        header_parameters['accept-language'] = 'en-US'
3453
3454        body_content_kwargs = {}
3455        body_content_kwargs['content'] = json.loads(request_body) if request_body else None
3456
3457        # Construct and send request
3458        def long_running_send():
3459            request = client.post(url, query_parameters, header_parameters, **body_content_kwargs)
3460            pipeline_response = client._pipeline.run(request, stream=False)
3461            return pipeline_response.http_response.internal_response
3462
3463        def get_long_running_status(status_link, headers=None):
3464            request = client.get(status_link, query_parameters, header_parameters)
3465            if headers:
3466                request.headers.update(headers)
3467            pipeline_response = client._pipeline.run(request, stream=False)
3468            return pipeline_response.http_response.internal_response
3469
3470        def get_long_running_output(response):
3471            from azure.core.exceptions import HttpResponseError
3472            if response.status_code not in [200, 202, 204]:
3473                exp = HttpResponseError(response)
3474                exp.request_id = response.headers.get('x-ms-request-id')
3475                raise exp
3476            return response.text
3477
3478        return AzureOperationPoller(long_running_send, get_long_running_output, get_long_running_status)
3479
3480    @staticmethod
3481    def resolve_api_version(rcf, resource_provider_namespace, parent_resource_path, resource_type,
3482                            latest_include_preview=False):
3483        provider = rcf.providers.get(resource_provider_namespace)
3484
3485        # If available, we will use parent resource's api-version
3486        resource_type_str = (parent_resource_path.split('/')[0] if parent_resource_path else resource_type)
3487
3488        rt = [t for t in provider.resource_types
3489              if t.resource_type.lower() == resource_type_str.lower()]
3490        if not rt:
3491            raise IncorrectUsageError('Resource type {} not found.'.format(resource_type_str))
3492        if len(rt) == 1 and rt[0].api_versions:
3493            # If latest_include_preview is true,
3494            # the last api-version will be taken regardless of whether it is preview version or not
3495            if latest_include_preview:
3496                return rt[0].api_versions[0]
3497            # Take the latest stable version first.
3498            # if there is no stable version, the latest preview version will be taken.
3499            npv = [v for v in rt[0].api_versions if 'preview' not in v.lower()]
3500            return npv[0] if npv else rt[0].api_versions[0]
3501        raise IncorrectUsageError(
3502            'API version is required and could not be resolved for resource {}'
3503            .format(resource_type))
3504
3505    @staticmethod
3506    def _resolve_api_version_by_id(rcf, resource_id, latest_include_preview=False):
3507        parts = parse_resource_id(resource_id)
3508
3509        if len(parts) == 2 and parts['subscription'] is not None and parts['resource_group'] is not None:
3510            return AZURE_API_PROFILES['latest'][ResourceType.MGMT_RESOURCE_RESOURCES]
3511
3512        if 'namespace' not in parts:
3513            raise CLIError('The type of value entered by --ids parameter is not supported.')
3514
3515        namespace = parts.get('child_namespace_1', parts['namespace'])
3516        if parts.get('child_type_2'):
3517            parent = (parts['type'] + '/' + parts['name'] + '/' +
3518                      parts['child_type_1'] + '/' + parts['child_name_1'])
3519            resource_type = parts['child_type_2']
3520        elif parts.get('child_type_1'):
3521            # if the child resource has a provider namespace it is independent of the
3522            # parent, so set the parent to empty
3523            if parts.get('child_namespace_1') is not None:
3524                parent = ''
3525            else:
3526                parent = parts['type'] + '/' + parts['name']
3527            resource_type = parts['child_type_1']
3528        else:
3529            parent = None
3530            resource_type = parts['type']
3531
3532        return _ResourceUtils.resolve_api_version(rcf, namespace, parent, resource_type,
3533                                                  latest_include_preview=latest_include_preview)
3534
3535
3536def install_bicep_cli(cmd, version=None):
3537    # The parameter version is actually a git tag here.
3538    ensure_bicep_installation(release_tag=version)
3539
3540
3541def uninstall_bicep_cli(cmd):
3542    remove_bicep_installation()
3543
3544
3545def upgrade_bicep_cli(cmd):
3546    latest_release_tag = get_bicep_latest_release_tag()
3547    ensure_bicep_installation(release_tag=latest_release_tag)
3548
3549
3550def build_bicep_file(cmd, file, stdout=None, outdir=None, outfile=None):
3551    args = ["build", file]
3552    if outdir:
3553        args += ["--outdir", outdir]
3554    if outfile:
3555        args += ["--outfile", outfile]
3556    if stdout:
3557        args += ["--stdout"]
3558        print(run_bicep_command(args))
3559        return
3560    run_bicep_command(args)
3561
3562
3563def decompile_bicep_file(cmd, file):
3564    run_bicep_command(["decompile", file])
3565
3566
3567def show_bicep_cli_version(cmd):
3568    print(run_bicep_command(["--version"], auto_install=False))
3569
3570
3571def list_bicep_cli_versions(cmd):
3572    return get_bicep_available_release_tags()
3573