def _validate_name_or_id(cli_ctx, resource_group_name, property_value, property_type, parent_value, parent_type): from azure.cli.core.commands.client_factory import get_subscription_id from azure.mgmt.core.tools import parse_resource_id, is_valid_resource_id has_parent = parent_type is not None if is_valid_resource_id(property_value): resource_id_parts = parse_resource_id(property_value) value_supplied_was_id = True elif has_parent: resource_id_parts = dict(name=parent_value, resource_group=resource_group_name, namespace=parent_type.split('/')[0], type=parent_type.split('/')[1], subscription=get_subscription_id(cli_ctx), child_name_1=property_value, child_type_1=property_type) value_supplied_was_id = False else: resource_id_parts = dict(name=property_value, resource_group=resource_group_name, namespace=property_type.split('/')[0], type=property_type.split('/')[1], subscription=get_subscription_id(cli_ctx)) value_supplied_was_id = False return (resource_id_parts, value_supplied_was_id)
def internal_validate_lock_parameters(namespace, resource_group, resource_provider_namespace, parent_resource_path, resource_type, resource_name): if resource_group is None: if resource_name is not None: from msrestazure.tools import parse_resource_id, is_valid_resource_id if not is_valid_resource_id(resource_name): raise CLIError( '--resource is not a valid resource ID. ' '--resource as a resource name is ignored if --resource-group is not given.' ) # resource-name is an ID, populate namespace id_dict = parse_resource_id(resource_name) for id_part in [ 'resource_name', 'resource_type', 'resource_group' ]: setattr(namespace, id_part, id_dict.get(id_part)) setattr(namespace, 'resource_provider_namespace', id_dict.get('resource_namespace')) setattr(namespace, 'parent_resource_path', id_dict.get('resource_parent').strip('/')) if resource_type is not None: raise CLIError( '--resource-type is ignored if --resource-group is not given.') if resource_provider_namespace is not None: raise CLIError( '--namespace is ignored if --resource-group is not given.') if parent_resource_path is not None: raise CLIError( '--parent is ignored if --resource-group is not given.') return if resource_name is None: if resource_type is not None: raise CLIError( '--resource-type is ignored if --resource is not given.') if resource_provider_namespace is not None: raise CLIError( '--namespace is ignored if --resource is not given.') if parent_resource_path is not None: raise CLIError('--parent is ignored if --resource is not given.') return if not resource_type: raise CLIError( '--resource-type is required if the name, --resource, is present') parts = resource_type.split('/') if resource_provider_namespace is None: if len(parts) == 1: raise CLIError( 'A resource namespace is required if the name, --resource, is present.' 'Expected <namespace>/<type> or --namespace=<namespace>') elif len(parts) != 1: raise CLIError( 'Resource namespace specified in both --resource-type and --namespace' )
def _construct_subnet_id(vnet_id, subnet): if not is_valid_resource_id(subnet): if subnet.count('/'): raise InvalidArgumentValueError('subnet {0} is not a valid name or resource ID'.format(subnet)) # subnet name is given return vnet_id + '/subnets/' + subnet if not subnet.lower().startswith(vnet_id.lower()): raise InvalidArgumentValueError('subnet {0} is not under virtual network {1}'.format(subnet, vnet_id)) return subnet
def _validate_template_spec(namespace): if namespace.template_spec is None: if (namespace.name is None or namespace.resource_group_name is None): raise CLIError('incorrect usage: Please enter ' 'a resource group and resource name or a resource ID for --template-spec') else: from azure.mgmt.core.tools import is_valid_resource_id namespace.template_spec = namespace.template_spec.strip("\"") if not is_valid_resource_id(namespace.template_spec): raise CLIError('--template-spec is not a valid resource ID.')
def validate_vnet(cmd, namespace): if not namespace.vnet and not namespace.app_subnet and \ not namespace.service_runtime_subnet and not namespace.reserved_cidr_range: return validate_vnet_required_parameters(namespace) vnet_id = '' if namespace.vnet: vnet_id = namespace.vnet # format the app_subnet and service_runtime_subnet if not is_valid_resource_id(vnet_id): if vnet_id.count('/') > 0: raise InvalidArgumentValueError('--vnet {0} is not a valid name or resource ID'.format(vnet_id)) vnet_id = resource_id( subscription=get_subscription_id(cmd.cli_ctx), resource_group=namespace.resource_group, namespace='Microsoft.Network', type='virtualNetworks', name=vnet_id ) else: vnet = parse_resource_id(vnet_id) if vnet['namespace'].lower() != 'microsoft.network' or vnet['type'].lower() != 'virtualnetworks': raise InvalidArgumentValueError('--vnet {0} is not a valid VirtualNetwork resource ID'.format(vnet_id)) namespace.app_subnet = _construct_subnet_id(vnet_id, namespace.app_subnet) namespace.service_runtime_subnet = _construct_subnet_id(vnet_id, namespace.service_runtime_subnet) else: app_vnet_id = _parse_vnet_id_from_subnet(namespace.app_subnet) service_runtime_vnet_id = _parse_vnet_id_from_subnet(namespace.service_runtime_subnet) if app_vnet_id.lower() != service_runtime_vnet_id.lower(): raise InvalidArgumentValueError('--app-subnet and --service-runtime-subnet should be in the same Virtual Networks.') vnet_id = app_vnet_id if namespace.app_subnet.lower() == namespace.service_runtime_subnet.lower(): raise InvalidArgumentValueError('--app-subnet and --service-runtime-subnet should not be the same.') vnet_obj = _get_vnet(cmd, vnet_id) instance_location = namespace.location if instance_location is None: instance_location = _get_rg_location(cmd.cli_ctx, namespace.resource_group) else: instance_location_slice = instance_location.split(" ") instance_location = "".join([piece.lower() for piece in instance_location_slice]) if vnet_obj.location.lower() != instance_location.lower(): raise InvalidArgumentValueError('--vnet and Azure Spring Apps instance should be in the same location.') for subnet in vnet_obj.subnets: _validate_subnet(namespace, subnet) _validate_route_table(namespace, vnet_obj) if namespace.reserved_cidr_range: _validate_cidr_range(namespace) else: namespace.reserved_cidr_range = _set_default_cidr_range(vnet_obj.address_space.address_prefixes) if \ vnet_obj and vnet_obj.address_space and vnet_obj.address_space.address_prefixes \ else '10.234.0.0/16,10.244.0.0/16,172.17.0.1/16'
def validate_log_analytic_workspace_name_or_id(cmd, namespace): if namespace.workspace_resource_id: from msrestazure.tools import resource_id from azure.cli.core.commands.client_factory import get_subscription_id if not is_valid_resource_id(namespace.workspace_resource_id): namespace.workspace_resource_id = resource_id( subscription=get_subscription_id(cmd.cli_ctx), resource_group=namespace.resource_group_name, namespace='microsoft.OperationalInsights', type='workspaces', name=namespace.workspace_resource_id)
def validate_storage_account_name_or_id(cmd, namespace): if namespace.storage_account_id: from msrestazure.tools import resource_id from azure.cli.core.commands.client_factory import get_subscription_id if not is_valid_resource_id(namespace.storage_account_id): namespace.storage_account_id = resource_id( subscription=get_subscription_id(cmd.cli_ctx), resource_group=namespace.resource_group_name, namespace='Microsoft.Storage', type='storageAccounts', name=namespace.storage_account_id)
def validate_applications(namespace): if namespace.resource_group_name: if isinstance(namespace.application, list): if len(namespace.application) == 1: if is_valid_resource_id(namespace.application[0]): raise CLIError( "Specify either a full resource id or an application name and resource group." ) else: raise CLIError( "Resource group only allowed with a single application name." )
def get_resource_name_and_rg(resource_group_name, name_or_id): if is_valid_resource_id(name_or_id): id_parts = parse_resource_id(name_or_id) name = id_parts.get('name') resource_group = id_parts.get('resource_group') if name is None or resource_group is None: raise InvalidArgumentValueError( "Please provide a valid resource id.") else: name = name_or_id resource_group = resource_group_name return name, resource_group
def process_container_resource(cmd, namespace): """Processes the resource group parameter from the storage account and container name""" if not namespace.storage_account or not namespace.container_name: raise ValueError( 'usage error: Please specify --storage-account and --container-name for blob-storage-target' ) from azure.mgmt.core.tools import is_valid_resource_id if not is_valid_resource_id(namespace.storage_account): raise ValueError('usage error: {} is not a valid resource id'.format( namespace.storage_account)) namespace.clfs_target = '{}/blobServices/default/containers/{}'.format( namespace.storage_account, namespace.container_name) del namespace.storage_account del namespace.container_name
def _parse_vnet_id_from_subnet(subnet_id): if not is_valid_resource_id(subnet_id): raise InvalidArgumentValueError( '{0} is not a valid subnet resource ID'.format(subnet_id)) subnet = parse_resource_id(subnet_id) if subnet['namespace'].lower() != 'microsoft.network' or \ subnet['type'].lower() != 'virtualnetworks' or \ 'resource_type' not in subnet or subnet['resource_type'].lower() != 'subnets': raise InvalidArgumentValueError( '{0} is not a valid subnet resource ID'.format(subnet_id)) return resource_id(subscription=subnet['subscription'], resource_group=subnet['resource_group'], namespace=subnet['namespace'], type=subnet['type'], name=subnet['name'])
def _validate_deployment_name_with_template_specs(namespace): # If missing,try come out with a name associated with the template name if namespace.deployment_name is None: template_filename = None if namespace.template_file and os.path.isfile(namespace.template_file): template_filename = namespace.template_file if namespace.template_uri and urlparse(namespace.template_uri).scheme: template_filename = urlsplit(namespace.template_uri).path if namespace.template_spec: from azure.mgmt.core.tools import parse_resource_id, is_valid_resource_id namespace.template_spec = namespace.template_spec.strip("\"") if not is_valid_resource_id(namespace.template_spec): raise CLIError('--template-spec is not a valid resource ID.') if namespace.template_spec.__contains__("versions") is False: raise CLIError('Please enter a valid template spec version ID.') template_filename = parse_resource_id(namespace.template_spec).get('resource_name') if template_filename: template_filename = os.path.basename(template_filename) namespace.deployment_name = os.path.splitext(template_filename)[0] else: namespace.deployment_name = 'deployment1'
def validate_dest_account(namespace): from msrestazure.tools import parse_resource_id if is_valid_resource_id(namespace.dest_account): parsed_storage = parse_resource_id(namespace.dest_account) storage_name = parsed_storage['resource_name'] namespace.dest_account = storage_name
def validate_resource_id(namespace): if not is_valid_resource_id(namespace.resource_id): raise InvalidArgumentValueError("Invalid resource id {}".format( namespace.resource_id))
def validate_app_service(namespace): if namespace.app_service and is_valid_resource_id(namespace.app_service): namespace.app_service = parse_resource_id( namespace.app_service)['name']
def validate_dest_account(namespace): if is_valid_resource_id(namespace.dest_account): parsed_storage = parse_resource_id(namespace.dest_account) storage_name = parsed_storage['resource_name'] namespace.dest_account = storage_name
def test_resource_parse(self): """ Tests resource id parsing, reforming, and validation. """ tests = [{ 'resource_id': '/subscriptions/fakesub/resourcegroups/testgroup/providers' '/Microsoft.Storage/storageAccounts/foo/providers' '/Microsoft.Authorization/locks/bar', 'expected': { 'name': 'foo', 'type': 'storageAccounts', 'namespace': 'Microsoft.Storage', 'child_name_1': 'bar', 'child_namespace_1': 'Microsoft.Authorization', 'child_type_1': 'locks', 'child_parent_1': 'storageAccounts/foo/providers/Microsoft.Authorization/', 'resource_group': 'testgroup', 'subscription': 'fakesub', } }, { 'resource_id': '/subscriptions/fakesub/resourcegroups/testgroup/providers' '/Microsoft.Storage/storageAccounts/foo' '/locks/bar', 'expected': { 'name': 'foo', 'type': 'storageAccounts', 'namespace': 'Microsoft.Storage', 'child_name_1': 'bar', 'child_type_1': 'locks', 'child_parent_1': 'storageAccounts/foo/', 'resource_group': 'testgroup', 'subscription': 'fakesub', } }, { 'resource_id': '/subscriptions/fakesub/resourcegroups/testgroup/providers' '/Microsoft.Storage/storageAccounts/foo/providers' '/Microsoft.Authorization/locks/bar/providers/Microsoft.Network/' 'nets/gc', 'expected': { 'name': 'foo', 'type': 'storageAccounts', 'namespace': 'Microsoft.Storage', 'child_name_1': 'bar', 'child_namespace_1': 'Microsoft.Authorization', 'child_type_1': 'locks', 'child_parent_1': 'storageAccounts/foo/providers/Microsoft.Authorization/', 'child_name_2': 'gc', 'child_namespace_2': 'Microsoft.Network', 'child_type_2': 'nets', 'child_parent_2': 'storageAccounts/foo/providers/Microsoft.Authorization/' 'locks/bar/providers/Microsoft.Network/', 'resource_group': 'testgroup', 'subscription': 'fakesub', } }, { 'resource_id': '/subscriptions/fakesub/resourcegroups/testgroup/providers' '/Microsoft.Storage/storageAccounts/foo' '/locks/bar/nets/gc', 'expected': { 'name': 'foo', 'type': 'storageAccounts', 'namespace': 'Microsoft.Storage', 'child_name_1': 'bar', 'child_type_1': 'locks', 'child_parent_1': 'storageAccounts/foo/', 'child_name_2': 'gc', 'child_type_2': 'nets', 'child_parent_2': 'storageAccounts/foo/locks/bar/', 'resource_group': 'testgroup', 'subscription': 'fakesub', } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg/providers/' 'Microsoft.Provider1/resourceType1/name1', 'expected': { 'subscription': 'mySub', 'resource_group': 'myRg', 'namespace': 'Microsoft.Provider1', 'type': 'resourceType1', 'name': 'name1', 'resource_parent': '', 'resource_namespace': 'Microsoft.Provider1', 'resource_type': 'resourceType1', 'resource_name': 'name1' } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg/providers/' 'Microsoft.Provider1/resourceType1/name1/resourceType2/name2', 'expected': { 'subscription': 'mySub', 'resource_group': 'myRg', 'namespace': 'Microsoft.Provider1', 'type': 'resourceType1', 'name': 'name1', 'child_namespace_1': None, 'child_type_1': 'resourceType2', 'child_name_1': 'name2', 'child_parent_1': 'resourceType1/name1/', 'resource_parent': 'resourceType1/name1/', 'resource_namespace': 'Microsoft.Provider1', 'resource_type': 'resourceType2', 'resource_name': 'name2' } }, { 'resource_id': '/subscriptions/00000/resourceGroups/myRg/providers/' 'Microsoft.RecoveryServices/vaults/vault_name/backupFabrics/' 'fabric_name/protectionContainers/container_name/' 'protectedItems/item_name/recoveryPoint/recovery_point_guid', 'expected': { 'subscription': '00000', 'resource_group': 'myRg', 'namespace': 'Microsoft.RecoveryServices', 'type': 'vaults', 'name': 'vault_name', 'child_type_1': 'backupFabrics', 'child_name_1': 'fabric_name', 'child_parent_1': 'vaults/vault_name/', 'child_type_2': 'protectionContainers', 'child_name_2': 'container_name', 'child_parent_2': 'vaults/vault_name/backupFabrics/fabric_name/', 'child_type_3': 'protectedItems', 'child_name_3': 'item_name', 'child_parent_3': 'vaults/vault_name/backupFabrics/fabric_name/' 'protectionContainers/container_name/', 'child_type_4': 'recoveryPoint', 'child_name_4': 'recovery_point_guid', 'child_parent_4': 'vaults/vault_name/backupFabrics/fabric_name/' 'protectionContainers/container_name/protectedItems/' 'item_name/', 'resource_parent': 'vaults/vault_name/backupFabrics/fabric_name/' 'protectionContainers/container_name/protectedItems/' 'item_name/', 'resource_namespace': 'Microsoft.RecoveryServices', 'resource_type': 'recoveryPoint', 'resource_name': 'recovery_point_guid' } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg/providers/' 'Microsoft.Provider1/resourceType1/name1/resourceType2/name2/' 'providers/Microsoft.Provider3/resourceType3/name3', 'expected': { 'subscription': 'mySub', 'resource_group': 'myRg', 'namespace': 'Microsoft.Provider1', 'type': 'resourceType1', 'name': 'name1', 'child_namespace_1': None, 'child_type_1': 'resourceType2', 'child_name_1': 'name2', 'child_parent_1': 'resourceType1/name1/', 'child_namespace_2': 'Microsoft.Provider3', 'child_type_2': 'resourceType3', 'child_name_2': 'name3', 'child_parent_2': 'resourceType1/name1/resourceType2/name2/' 'providers/Microsoft.Provider3/', 'resource_parent': 'resourceType1/name1/resourceType2/name2/' 'providers/Microsoft.Provider3/', 'resource_namespace': 'Microsoft.Provider1', 'resource_type': 'resourceType3', 'resource_name': 'name3' } }, { 'resource_id': '/subscriptions/fakesub/providers/Microsoft.Authorization' '/locks/foo', 'expected': { 'name': 'foo', 'type': 'locks', 'namespace': 'Microsoft.Authorization', 'subscription': 'fakesub', } }, { 'resource_id': '/Subscriptions/fakesub/providers/Microsoft.Authorization' '/locks/foo', 'expected': { 'name': 'foo', 'type': 'locks', 'namespace': 'Microsoft.Authorization', 'subscription': 'fakesub', } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg', 'expected': { 'subscription': 'mySub', 'resource_group': 'myRg' } }] for test in tests: self.assertTrue(is_valid_resource_id(test['resource_id'])) kwargs = parse_resource_id(test['resource_id']) for key in test['expected']: try: self.assertEqual(kwargs[key], test['expected'][key]) except KeyError: self.assertTrue(key not in kwargs and test['expected'][key] is None) invalid_ids = [ '/subscriptions/fakesub/resourceGroups/myRg/type1/name1', '/subscriptions/fakesub/resourceGroups/myRg/providers/Microsoft.Provider/foo', '/subscriptions/fakesub/resourceGroups/myRg/providers/namespace/type/name/type1' ] for invalid_id in invalid_ids: self.assertFalse(is_valid_resource_id(invalid_id)) tests = [{ 'resource_id': '/subscriptions/fakesub/resourcegroups/testgroup/providers' '/Microsoft.Storage/storageAccounts/foo/providers' '/Microsoft.Authorization/locks/bar', 'id_args': { 'name': 'foo', 'type': 'storageAccounts', 'namespace': 'Microsoft.Storage', 'child_name_1': 'bar', 'child_namespace_1': 'Microsoft.Authorization', 'child_type_1': 'locks', 'resource_group': 'testgroup', 'subscription': 'fakesub', } }, { 'resource_id': '/subscriptions/fakesub/resourcegroups/testgroup/providers' '/Microsoft.Storage/storageAccounts/foo' '/locks/bar', 'id_args': { 'name': 'foo', 'type': 'storageAccounts', 'namespace': 'Microsoft.Storage', 'child_name_1': 'bar', 'child_type_1': 'locks', 'resource_group': 'testgroup', 'subscription': 'fakesub', } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg/providers/' 'Microsoft.Provider1/resourceType1/name1/resourceType2/name2/' 'providers/Microsoft.Provider3/resourceType3/name3', 'id_args': { 'subscription': 'mySub', 'resource_group': 'myRg', 'namespace': 'Microsoft.Provider1', 'type': 'resourceType1', 'name': 'name1', 'child_type_1': 'resourceType2', 'child_name_1': 'name2', 'child_namespace_2': 'Microsoft.Provider3', 'child_type_2': 'resourceType3', 'child_name_2': 'name3' } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg/' 'providers/Microsoft.Provider1', 'id_args': { 'subscription': 'mySub', 'resource_group': 'myRg', 'namespace': 'Microsoft.Provider1' } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg', 'id_args': { 'subscription': 'mySub', 'resource_group': 'myRg' } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg/' 'providers/Microsoft.Provider1/resourceType1/name1/resourceType2/' 'name2/providers/Microsoft.Provider3', 'id_args': { 'subscription': 'mySub', 'resource_group': 'myRg', 'namespace': 'Microsoft.Provider1', 'type': 'resourceType1', 'name': 'name1', 'child_type_1': 'resourceType2', 'child_name_1': 'name2', 'child_namespace_2': 'Microsoft.Provider3' } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg/' 'providers/Microsoft.Provider1/resourceType1/name1', 'id_args': { 'subscription': 'mySub', 'resource_group': 'myRg', 'namespace': 'Microsoft.Provider1', 'type': 'resourceType1', 'name': 'name1', 'child_type_1': None, 'child_name_1': 'name2', 'child_namespace_2': 'Microsoft.Provider3' } }, { 'resource_id': '/subscriptions/mySub/resourceGroups/myRg', 'id_args': { 'subscription': 'mySub', 'resource_group': 'myRg' } }] for test in tests: rsrc_id = resource_id(**test['id_args']) self.assertEqual(rsrc_id.lower(), test['resource_id'].lower())
def _is_valid_user_assigned_managed_identity_resource_id(resource_id): if not is_valid_resource_id(resource_id.lower()): return False if "/providers/Microsoft.ManagedIdentity/userAssignedIdentities/".lower() not in resource_id.lower(): return False return True
def login_with_managed_identity(self, identity_id=None, allow_no_subscriptions=None): import jwt from azure.mgmt.core.tools import is_valid_resource_id from azure.cli.core.auth.adal_authentication import MSIAuthenticationWrapper resource = self.cli_ctx.cloud.endpoints.active_directory_resource_id if identity_id: if is_valid_resource_id(identity_id): msi_creds = MSIAuthenticationWrapper(resource=resource, msi_res_id=identity_id) identity_type = MsiAccountTypes.user_assigned_resource_id else: authenticated = False from azure.cli.core.azclierror import AzureResponseError try: msi_creds = MSIAuthenticationWrapper(resource=resource, client_id=identity_id) identity_type = MsiAccountTypes.user_assigned_client_id authenticated = True except AzureResponseError as ex: if 'http error: 400, reason: Bad Request' in ex.error_msg: logger.info('Sniff: not an MSI client id') else: raise if not authenticated: try: identity_type = MsiAccountTypes.user_assigned_object_id msi_creds = MSIAuthenticationWrapper( resource=resource, object_id=identity_id) authenticated = True except AzureResponseError as ex: if 'http error: 400, reason: Bad Request' in ex.error_msg: logger.info('Sniff: not an MSI object id') else: raise if not authenticated: raise CLIError( 'Failed to connect to MSI, check your managed service identity id.' ) else: identity_type = MsiAccountTypes.system_assigned msi_creds = MSIAuthenticationWrapper(resource=resource) token_entry = msi_creds.token token = token_entry['access_token'] logger.info( 'MSI: token was retrieved. Now trying to initialize local accounts...' ) decode = jwt.decode(token, algorithms=['RS256'], options={"verify_signature": False}) tenant = decode['tid'] subscription_finder = SubscriptionFinder(self.cli_ctx) subscriptions = subscription_finder.find_using_specific_tenant( tenant, msi_creds) base_name = ('{}-{}'.format(identity_type, identity_id) if identity_id else identity_type) user = _USER_ASSIGNED_IDENTITY if identity_id else _SYSTEM_ASSIGNED_IDENTITY if not subscriptions: if allow_no_subscriptions: subscriptions = self._build_tenant_level_accounts([tenant]) else: raise CLIError( 'No access was configured for the VM, hence no subscriptions were found. ' "If this is expected, use '--allow-no-subscriptions' to have tenant level access." ) consolidated = self._normalize_properties( user, subscriptions, is_service_principal=True, user_assigned_identity_id=base_name) self._set_subscriptions(consolidated) return deepcopy(consolidated)