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