def load_subscriptions(all_clouds=False, refresh=False): profile = Profile() if refresh: subscriptions = profile.refresh_accounts() subscriptions = profile.load_cached_subscriptions(all_clouds) return subscriptions
def _get_token(server, resource, scope): # pylint: disable=unused-argument return Profile(cli_ctx=cli_ctx).get_login_credentials(resource)[0]._token_retriever() # pylint: disable=protected-access
def send_raw_request(cli_ctx, method, url, headers=None, uri_parameters=None, # pylint: disable=too-many-locals,too-many-branches,too-many-statements body=None, skip_authorization_header=False, resource=None, output_file=None, generated_client_request_id_name='x-ms-client-request-id'): import uuid from requests import Session, Request from requests.structures import CaseInsensitiveDict result = CaseInsensitiveDict() for s in headers or []: try: temp = shell_safe_json_parse(s) result.update(temp) except CLIError: key, value = s.split('=', 1) result[key] = value headers = result # If Authorization header is already provided, don't bother with the token if 'Authorization' in headers: skip_authorization_header = True # Handle User-Agent agents = [get_az_user_agent()] # Borrow AZURE_HTTP_USER_AGENT from msrest # https://github.com/Azure/msrest-for-python/blob/4cc8bc84e96036f03b34716466230fb257e27b36/msrest/pipeline/universal.py#L70 _ENV_ADDITIONAL_USER_AGENT = 'AZURE_HTTP_USER_AGENT' import os if _ENV_ADDITIONAL_USER_AGENT in os.environ: agents.append(os.environ[_ENV_ADDITIONAL_USER_AGENT]) # Custom User-Agent provided as command argument if 'User-Agent' in headers: agents.append(headers['User-Agent']) headers['User-Agent'] = ' '.join(agents) if generated_client_request_id_name: headers[generated_client_request_id_name] = str(uuid.uuid4()) # try to figure out the correct content type if body: try: _ = shell_safe_json_parse(body) if 'Content-Type' not in headers: headers['Content-Type'] = 'application/json' except Exception: # pylint: disable=broad-except pass # add telemetry headers['CommandName'] = cli_ctx.data['command'] if cli_ctx.data.get('safe_params'): headers['ParameterSetName'] = ' '.join(cli_ctx.data['safe_params']) result = {} for s in uri_parameters or []: try: temp = shell_safe_json_parse(s) result.update(temp) except CLIError: key, value = s.split('=', 1) result[key] = value uri_parameters = result or None # If url is an ARM resource ID, like /subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01, # default to Azure Resource Manager. # https://management.azure.com/ + subscriptions/xxx/resourcegroups/xxx?api-version=2019-07-01 if '://' not in url: url = cli_ctx.cloud.endpoints.resource_manager + url.lstrip('/') # Replace common tokens with real values. It is for smooth experience if users copy and paste the url from # Azure Rest API doc from azure.cli.core._profile import Profile profile = Profile() if '{subscriptionId}' in url: url = url.replace('{subscriptionId}', cli_ctx.data['subscription_id'] or profile.get_subscription_id()) token_subscription = None _subscription_regexes = [re.compile('https://management.azure.com/subscriptions/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})'), re.compile('https://graph.windows.net/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})')] for regex in _subscription_regexes: match = regex.match(url) if match: token_subscription = match.groups()[0] logger.debug('Retrieve token from subscription %s', token_subscription) if not skip_authorization_header and url.lower().startswith('https://'): if not resource: endpoints = cli_ctx.cloud.endpoints # If url starts with ARM endpoint, like https://management.azure.com/, # use active_directory_resource_id for resource. # This follows the same behavior as azure.cli.core.commands.client_factory._get_mgmt_service_client if url.lower().startswith(endpoints.resource_manager.rstrip('/')): resource = endpoints.active_directory_resource_id else: from azure.cli.core.cloud import CloudEndpointNotSetException for p in [x for x in dir(endpoints) if not x.startswith('_')]: try: value = getattr(endpoints, p) except CloudEndpointNotSetException: continue if isinstance(value, six.string_types) and url.lower().startswith(value.lower()): resource = value break if resource: token_info, _, _ = profile.get_raw_token(resource, subscription=token_subscription) logger.debug('Retrievd AAD token for resource: %s', resource or 'ARM') token_type, token, _ = token_info headers = headers or {} headers['Authorization'] = '{} {}'.format(token_type, token) else: logger.warning("Can't derive appropriate Azure AD resource from --url to acquire an access token. " "If access token is required, use --resource to specify the resource") try: # https://requests.readthedocs.io/en/latest/user/advanced/#prepared-requests s = Session() req = Request(method=method, url=url, headers=headers, params=uri_parameters, data=body) prepped = s.prepare_request(req) # Merge environment settings into session settings = s.merge_environment_settings(prepped.url, {}, None, not should_disable_connection_verify(), None) _log_request(prepped) r = s.send(prepped, **settings) _log_response(r) except Exception as ex: # pylint: disable=broad-except raise CLIError(ex) if not r.ok: reason = r.reason if r.text: reason += '({})'.format(r.text) raise CLIError(reason) if output_file: with open(output_file, 'wb') as fd: for chunk in r.iter_content(chunk_size=128): fd.write(chunk) return r
def _initialize_session(self): """ Creates a session using available authentication type. Auth priority: 1. Token Auth 2. Tenant Auth 3. Azure CLI Auth """ # Only run once if self.credentials is not None: return tenant_auth_variables = [ constants.ENV_TENANT_ID, constants.ENV_SUB_ID, constants.ENV_CLIENT_ID, constants.ENV_CLIENT_SECRET ] token_auth_variables = [ constants.ENV_ACCESS_TOKEN, constants.ENV_SUB_ID ] msi_auth_variables = [constants.ENV_USE_MSI, constants.ENV_SUB_ID] if self.authorization_file: self.credentials, self.subscription_id = self.load_auth_file( self.authorization_file) self.log.info("Creating session with authorization file") elif all(k in os.environ for k in token_auth_variables): # Token authentication self.credentials = BasicTokenAuthentication( token={'access_token': os.environ[constants.ENV_ACCESS_TOKEN]}) self.subscription_id = os.environ[constants.ENV_SUB_ID] self.log.info("Creating session with Token Authentication") self._is_token_auth = True elif all(k in os.environ for k in tenant_auth_variables): # Tenant (service principal) authentication self.credentials = ServicePrincipalCredentials( client_id=os.environ[constants.ENV_CLIENT_ID], secret=os.environ[constants.ENV_CLIENT_SECRET], tenant=os.environ[constants.ENV_TENANT_ID], resource=self.resource_namespace) self.subscription_id = os.environ[constants.ENV_SUB_ID] self.tenant_id = os.environ[constants.ENV_TENANT_ID] self.log.info( "Creating session with Service Principal Authentication") elif all(k in os.environ for k in msi_auth_variables): # MSI authentication if constants.ENV_CLIENT_ID in os.environ: self.credentials = MSIAuthentication( client_id=os.environ[constants.ENV_CLIENT_ID], resource=self.resource_namespace) else: self.credentials = MSIAuthentication( resource=self.resource_namespace) self.subscription_id = os.environ[constants.ENV_SUB_ID] self.log.info("Creating session with MSI Authentication") else: # Azure CLI authentication self._is_cli_auth = True (self.credentials, self.subscription_id, self.tenant_id) = Profile().get_login_credentials( resource=self.resource_namespace) self.log.info("Creating session with Azure CLI Authentication") # TODO: cleanup this workaround when issue resolved. # https://github.com/Azure/azure-sdk-for-python/issues/5096 if self.resource_namespace == constants.RESOURCE_VAULT: access_token = AccessToken(token=self.get_bearer_token()) self.credentials = KeyVaultAuthentication( lambda _1, _2, _3: access_token) # Let provided id parameter override everything else if self.subscription_id_override is not None: self.subscription_id = self.subscription_id_override self.log.info("Session using Subscription ID: %s" % self.subscription_id) if self.credentials is None: self.log.error('Unable to locate credentials for Azure session.')
from azure.cli.core.application import Configuration from azure.cli.core.commands import LongRunningOperation, get_op_handler from azure.cli.core.cloud import get_active_cloud_name from azure.cli.core._config import az_config, DEFAULTS_SECTION from azure.cli.core._environment import get_config_dir from azure.cli.core._profile import _SUBSCRIPTION_NAME, Profile from azure.cli.core._session import ACCOUNT, CONFIG, SESSION import azure.cli.core.telemetry as cli_telemetry from azure.cli.core.util import (show_version_info_exit, handle_exception) from azure.cli.core.util import CLIError SHELL_CONFIGURATION = azclishell.configuration.CONFIGURATION SHELL_CONFIG_DIR = azclishell.configuration.get_config_dir NOTIFICATIONS = "" PROFILE = Profile() SELECT_SYMBOL = azclishell.configuration.SELECT_SYMBOL PART_SCREEN_EXAMPLE = .3 START_TIME = datetime.datetime.utcnow() CLEAR_WORD = get_os_clear_screen_word() def space_examples(list_examples, rows): """ makes the example text """ examples_with_index = [] for i, _ in list(enumerate(list_examples)): examples_with_index.append("[" + str(i + 1) + "] " + list_examples[i][0] + list_examples[i][1]) example = "".join(exam for exam in examples_with_index)
def test_create_token_cache(self, mock_read_file): mock_read_file.return_value = [] profile = Profile() cache = profile._creds_cache.adal_token_cache self.assertFalse(cache.read_items()) self.assertTrue(mock_read_file.called)
def _get_profile(): from azure.cli.core._profile import Profile return Profile(cli_ctx=_session.application)
def get_subscription_id(): profile = Profile() _, subscription_id, _ = profile.get_login_credentials() return subscription_id
def createDeployment(cmd, name, location, templateId, description=None, parameters=None, parameterFile=None, solutionStorageConnectionString=None, subscription=None): """Creates a deployment with the specified parameters. name: The name of the deployment. (must be alphanumeric lowercase between 3 to 9 characters beginning with a letter) location: The location to deploy the solution to. templateId: The unique id of the template which the deployment will be bulit from. description[optional]: The optional description of the deployment. parameters[optional]: solutionStorageConnectionString[optional]: subscription[optional]: Provides an alternate subscription to use if desired. """ if subscription is None: subscription = get_subscription_id(cmd.cli_ctx) logger.info("Using default subscription: " + subscription) profile = Profile(cli_ctx=cmd.cli_ctx) auth_token = profile.get_raw_token(subscription=subscription) if parameters is not None and parameterFile is not None: raise CLIError( "May not have parameters and a parameters file at the same time.") elif parameterFile is not None: logger.info("Using parameter file: " + parameterFile) try: with open(parameterFile) as jsonfile: parameters = json.load(jsonfile) logger.info("Parameters loaded.") except IOError: raise CLIError("Could not open file.") elif parameters is not None: logger.info("Using parameters from commandline argument.") validators.validate_json_arg(parameters) parameters = json.loads(parameters) logger.info("Parameters loaded") creds = authentication.BasicTokenAuthentication( {'access_token': auth_token[0][1]}) ciqsapi = ciqs_api.CiqsApi(creds=creds, base_url=api.getEndpoint()) request = models.MicrosoftCiqsModelsDeploymentCreateDeploymentRequest( name, location, template_id=templateId, subscription=subscription, description=description, referrer='az ciqs', solution_storage_connection_string=solutionStorageConnectionString, parameters=parameters) try: logger.info("Sending request.") response = ciqsapi.post_api_deployments_by_subscription_id_by_template_id( subscription_id=subscription, template_id=templateId, body=request, ms_asm_refresh_token=auth_token[0][2]['refreshToken']) except msrest.exceptions.HttpOperationError as e: message = e.response.json() raise CLIError(message) return response
def _call_rp_configure_cicd( target_name, target_resource_group, vsts_account_name, vsts_project_name, registry_name, registry_resource_id, remote_url, remote_branch, remote_access_token, create_release=True): """ Calls the RP to build and deploy the service(s) in the cluster. :param target_name: Name of the target Azure container service instance to deploy containers to. :type target_name: String :param target_resource_group: Name of Azure container service's resource group. :type target_resource_group: String :param remote_url: Remote url of the GitHub or VSTS source repository that will be built and deployed. If omitted, a source repository will be searched for in the current working directory. :type remote_url: String :param remote_branch: Remote branch of the GitHub or VSTS source repository that will be built and deployed. If omitted refs/heads/master will be selected. :type remote_branch: String :param remote_access_token: GitHub personal access token (minimum permission is 'repo'). Required if the source repository is in GitHub. :type remote_access_token: String :param vsts_account_name: VSTS account name to create the build and release definitions. A new VSTS account is created if omitted or does not exist. :type vsts_account_name: String :param vsts_project_name: VSTS project name to create the build and release definitions. A new VSTS project is created if omitted or does not exist. :type vsts_project_name: String :param registry_resource_id: Azure container registry resource id. :type registry_resource_id: String :param registry_name: Azure container registry name. A new Azure container registry is created if omitted or does not exist. :type registry_name: String :param create_release: Whether to create a release definition and deploy the application. :type create_release: bool """ profile = Profile() cred, subscription_id, _ = profile.get_login_credentials() o_auth_token = cred.signed_session().headers['Authorization'] container_service_resource_id = CONTAINER_SERVICE_RESOURCE_URL.format(subscription_id=subscription_id, resource_group_name=target_resource_group, container_service_name=target_name) data = { 'acsResourceId': container_service_resource_id, 'vstsAccountName': vsts_account_name, 'vstsProjectName': vsts_project_name, 'registryName': registry_name, 'registryResourceId': registry_resource_id, 'remoteToken': remote_access_token, 'remoteUrl': remote_url, 'remoteBranch': remote_branch, 'createRelease' : create_release } configure_ci_cd_url = SERVICE_URL.format( subscription_id=subscription_id) + '/configureCI?api-version=' + API_VERSION headers = {} headers['Authorization'] = o_auth_token headers['Content-Type'] = 'application/json; charset=utf-8' headers['x-ms-client-request-id'] = str(uuid.uuid1()) req = requests.post(configure_ci_cd_url, data=json.dumps(data), headers=headers, timeout=600) while req.status_code == 202: # Long-running operation time.sleep(10) req = requests.get(BASE_URL + req.headers['Location'], headers=headers, timeout=600) if req.status_code != 200: raise CLIError( 'Server returned status code: ' + str(req.status_code) + '. Could not configure CI/CD: ' + req.text) json_request = req.json() return json_request
def create_bot_json( cmd, client, resource_group_name, resource_name, logger, app_password=None, # pylint:disable=too-many-locals raw_bot_properties=None, password_only=True): """ :param cmd: :param client: :param resource_group_name: :param resource_name: :param logger: :param app_password: :param raw_bot_properties: :return: Dictionary """ if not raw_bot_properties: raw_bot_properties = client.bots.get( resource_group_name=resource_group_name, resource_name=resource_name) # Initialize names bot_file and secret to capture botFilePath and botFileSecret values from the application's # settings. bot_file = None bot_file_secret = None profile = Profile(cli_ctx=cmd.cli_ctx) if not app_password: site_name = WebAppOperations.get_bot_site_name( raw_bot_properties.properties.endpoint) app_settings = WebAppOperations.get_app_settings( cmd=cmd, resource_group_name=resource_group_name, name=site_name) app_password_values = [ item['value'] for item in app_settings if item['name'] == 'MicrosoftAppPassword' ] app_password = app_password_values[ 0] if app_password_values else None if not app_password: bot_file_values = [ item['value'] for item in app_settings if item['name'] == 'botFilePath' ] bot_file = bot_file_values[0] if bot_file_values else None bot_file_secret_values = [ item['value'] for item in app_settings if item['name'] == 'botFileSecret' ] bot_file_secret = bot_file_secret_values[ 0] if bot_file_secret_values else None if not bot_file and not app_password: bot_site_name = WebAppOperations.get_bot_site_name( raw_bot_properties.properties.endpoint) scm_url = WebAppOperations.get_scm_url(cmd, resource_group_name, bot_site_name, None) # TODO: Reevaluate "Public-or-Gov" Azure logic. is_public_azure = ( 'azurewebsites.net' in raw_bot_properties.properties.endpoint or '.net' in raw_bot_properties.properties.endpoint or '.com' in raw_bot_properties.properties.endpoint) host = 'https://portal.azure.com/' if is_public_azure else 'https://portal.azure.us/' subscription_id = get_subscription_id(cmd.cli_ctx) tenant_id = profile.get_subscription( subscription=client.config.subscription_id)['tenantId'] settings_url = host + '#@{}/resource/subscriptions/{}/resourceGroups/{}/providers/Microsoft.BotService/botServices/{}/app_settings'.format(tenant_id, subscription_id, resource_group_name, resource_name) # pylint: disable=line-too-long logger.warning( '"MicrosoftAppPassword" and "botFilePath" not found in application settings' ) logger.warning( 'To see your bot\'s application settings, visit %s' % settings_url) logger.warning( 'To visit your deployed bot\'s code on Azure, visit Kudu for your bot at %s' % scm_url) elif not app_password and bot_file: # We have the information we need to obtain the MSA App app password via bot file data from Kudu. kudu_client = KuduClient(cmd, resource_group_name, resource_name, raw_bot_properties, logger) bot_file_data = kudu_client.get_bot_file(bot_file) app_password = BotJsonFormatter.__decrypt_bot_file( bot_file_data, bot_file_secret, logger, password_only) return { 'type': 'abs', 'id': raw_bot_properties.name, 'name': raw_bot_properties.properties.display_name, 'appId': raw_bot_properties.properties.msa_app_id, 'appPassword': app_password, 'endpoint': raw_bot_properties.properties.endpoint, 'resourceGroup': str(resource_group_name), 'tenantId': profile.get_subscription( subscription=client.config.subscription_id)['tenantId'], 'subscriptionId': client.config.subscription_id, 'serviceName': resource_name }
def create_service_principal_for_rbac( # pylint:disable=too-many-arguments,too-many-statements,too-many-locals, too-many-branches name=None, password=None, years=1, create_cert=False, cert=None, scopes=None, role='Contributor', expanded_view=None, skip_assignment=False): '''create a service principal and configure its access to Azure resources :param str name: a display name or an app id uri. Command will generate one if missing. :param str password: the password used to login. If missing, command will generate one. :param str cert: PEM formatted public certificate. Do not include private key info. :param str years: Years the password will be valid. :param str scopes: space separated scopes the service principal's role assignment applies to. Defaults to the root of the current subscription. :param str role: role the service principal has on the resources. ''' import time graph_client = _graph_client_factory() role_client = _auth_client_factory().role_assignments scopes = scopes or ['/subscriptions/' + role_client.config.subscription_id] sp_oid = None _RETRY_TIMES = 36 app_display_name = None if name and '://' not in name: app_display_name = name name = "http://" + name # normalize be a valid graph service principal name if name: query_exp = 'servicePrincipalNames/any(x:x eq \'{}\')'.format(name) aad_sps = list(graph_client.service_principals.list(filter=query_exp)) if aad_sps: raise CLIError("'{}' already exists.".format(name)) # pylint: disable=protected-access public_cert_string = None cert_file = None password = None if len([x for x in [cert, create_cert, password] if x]) > 1: raise CLIError('Usage error: --cert | --create-cert | --password') if create_cert: public_cert_string, cert_file = _create_self_signed_cert(years) elif cert: public_cert_string = cert else: password = password or str(uuid.uuid4()) start_date = datetime.datetime.utcnow() app_display_name = app_display_name or ( 'azure-cli-' + start_date.strftime('%Y-%m-%d-%H-%M-%S')) if name is None: name = 'http://' + app_display_name # just a valid uri, no need to exist end_date = start_date + relativedelta(years=years) aad_application = create_application( graph_client.applications, display_name=app_display_name, # pylint: disable=too-many-function-args homepage='http://' + app_display_name, identifier_uris=[name], available_to_other_tenants=False, password=password, key_value=public_cert_string, start_date=start_date, end_date=end_date) # pylint: disable=no-member app_id = aad_application.app_id # retry till server replication is done for l in range(0, _RETRY_TIMES): try: aad_sp = _create_service_principal(app_id, resolve_app=False) break except Exception as ex: # pylint: disable=broad-except # pylint: disable=line-too-long if l < _RETRY_TIMES and (' does not reference ' in str(ex) or ' does not exist ' in str(ex)): time.sleep(5) logger.warning('Retrying service principal creation: %s/%s', l + 1, _RETRY_TIMES) else: logger.warning( "Creating service principal failed for appid '%s'. Trace followed:\n%s", name, ex.response.headers if hasattr(ex, 'response') else ex) # pylint: disable=no-member raise sp_oid = aad_sp.object_id # retry while server replication is done if not skip_assignment: # pylint: disable=line-too-long for scope in scopes: for l in range(0, _RETRY_TIMES): try: _create_role_assignment(role, sp_oid, None, scope, resolve_assignee=False) break except Exception as ex: if l < _RETRY_TIMES and ' does not exist in the directory ' in str( ex): time.sleep(5) logger.warning( 'Retrying role assignment creation: %s/%s', l + 1, _RETRY_TIMES) continue else: # dump out history for diagnoses logger.warning('Role assignment creation failed.\n') if getattr(ex, 'response', None) is not None: logger.warning( 'role assignment response headers: %s\n', ex.response.headers) # pylint: disable=no-member raise if expanded_view: from azure.cli.core._profile import Profile profile = Profile() result = profile.get_expanded_subscription_info( scopes[0].split('/')[2] if scopes else None, app_id, password) else: result = { 'appId': app_id, 'password': password, 'name': name, 'displayName': app_display_name, 'tenant': graph_client.config.tenant_id } if cert_file: # pylint: disable=line-too-long logger.warning( "Please copy %s to a safe place. When run 'az login' provide the file path to the --password argument", cert_file) result['fileWithCertAndPrivateKey'] = cert_file return result
def login(cmd, username=None, password=None, service_principal=None, tenant=None, allow_no_subscriptions=False, identity=False, use_device_code=False, use_cert_sn_issuer=None): """Log in to access Azure subscriptions""" from adal.adal_error import AdalError import requests # quick argument usage check if any([password, service_principal, tenant]) and identity: raise CLIError( "usage error: '--identity' is not applicable with other arguments") if any([password, service_principal, username, identity ]) and use_device_code: raise CLIError( "usage error: '--use-device-code' is not applicable with other arguments" ) if use_cert_sn_issuer and not service_principal: raise CLIError( "usage error: '--use-sn-issuer' is only applicable with a service principal" ) if service_principal and not username: raise CLIError( 'usage error: --service-principal --username NAME --password SECRET --tenant TENANT' ) interactive = False profile = Profile(cli_ctx=cmd.cli_ctx, async_persist=False) if identity: if in_cloud_console(): return profile.find_subscriptions_in_cloud_console() return profile.find_subscriptions_in_vm_with_msi( username, allow_no_subscriptions) if in_cloud_console(): # tell users they might not need login logger.warning(_CLOUD_CONSOLE_LOGIN_WARNING) if username: if not password: try: password = prompt_pass('Password: '******'Please specify both username and password in non-interactive mode.' ) else: interactive = True try: subscriptions = profile.find_subscriptions_on_login( interactive, username, password, service_principal, tenant, use_device_code=use_device_code, allow_no_subscriptions=allow_no_subscriptions, use_cert_sn_issuer=use_cert_sn_issuer) except AdalError as err: # try polish unfriendly server errors if username: msg = str(err) suggestion = "For cross-check, try 'az login' to authenticate through browser." if ('ID3242:' in msg) or ('Server returned an unknown AccountType' in msg): raise CLIError("The user name might be invalid. " + suggestion) if 'Server returned error in RSTR - ErrorCode' in msg: raise CLIError( "Logging in through command line is not supported. " + suggestion) if 'wstrust' in msg: raise CLIError( "Authentication failed due to error of '" + msg + "' " "This typically happens when attempting a Microsoft account, which requires " "interactive login. Please invoke 'az login' to cross check. " # pylint: disable=line-too-long "More details are available at https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication" ) raise CLIError(err) except requests.exceptions.SSLError as err: from azure.cli.core.util import SSLERROR_TEMPLATE raise CLIError(SSLERROR_TEMPLATE.format(str(err))) except requests.exceptions.ConnectionError as err: raise CLIError( 'Please ensure you have network connection. Error detail: ' + str(err)) all_subscriptions = list(subscriptions) for sub in all_subscriptions: sub['cloudName'] = sub.pop('environmentName', None) return all_subscriptions
def login(username=None, password=None, service_principal=None, tenant=None, allow_no_subscriptions=False): """Log in to access Azure subscriptions""" import os import re from adal.adal_error import AdalError import requests interactive = False profile = Profile() if username: if not password: # in a VM with managed service identity? result = profile.init_if_in_msi_env(username) if result: return result try: password = prompt_pass('Password: '******'Please specify both username and password in non-interactive mode.' ) else: # in a cloud console? console_tokens = os.environ.get('AZURE_CONSOLE_TOKENS', None) if console_tokens: return profile.find_subscriptions_in_cloud_console( re.split(';|,', console_tokens)) interactive = True try: subscriptions = profile.find_subscriptions_on_login( interactive, username, password, service_principal, tenant, allow_no_subscriptions=allow_no_subscriptions) except AdalError as err: # try polish unfriendly server errors if username: msg = str(err) suggestion = "For cross-check, try 'az login' to authenticate through browser." if ('ID3242:' in msg) or ('Server returned an unknown AccountType' in msg): raise CLIError("The user name might be invalid. " + suggestion) if 'Server returned error in RSTR - ErrorCode' in msg: raise CLIError( "Logging in through command line is not supported. " + suggestion) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError( 'Please ensure you have network connection. Error detail: ' + str(err)) all_subscriptions = list(subscriptions) for sub in all_subscriptions: sub['cloudName'] = sub.pop('environmentName', None) return all_subscriptions
def _get_profile(): from azure.cli.core._profile import Profile return Profile()
def load_subscriptions(all_clouds=False): profile = Profile() subscriptions = profile.load_cached_subscriptions(all_clouds) return subscriptions
def get_subscription_id(cli_ctx): from azure.cli.core._profile import Profile if not cli_ctx.data.get('subscription_id'): cli_ctx.data['subscription_id'] = Profile( cli_ctx=cli_ctx).get_subscription_id() return cli_ctx.data['subscription_id']
def show_subscription(subscription=None, expanded_view=None): profile = Profile() if not expanded_view: return profile.get_subscription(subscription) else: return profile.get_expanded_subscription_info(subscription)
def create_keyvault( cmd, client, # pylint: disable=too-many-locals resource_group_name, vault_name, location=None, sku=None, enabled_for_deployment=None, enabled_for_disk_encryption=None, enabled_for_template_deployment=None, enable_soft_delete=None, enable_purge_protection=None, bypass=None, default_action=None, no_self_perms=None, tags=None): from azext_keyvault.mgmt.keyvault.models import ( VaultCreateOrUpdateParameters, Permissions, KeyPermissions, SecretPermissions, CertificatePermissions, StoragePermissions, AccessPolicyEntry, Sku, VaultProperties) from azure.cli.core._profile import Profile from azure.graphrbac.models import GraphErrorException from azure.graphrbac import GraphRbacManagementClient profile = Profile(cli_ctx=cmd.cli_ctx) cred, _, tenant_id = profile.get_login_credentials( resource=cmd.cli_ctx.cloud.endpoints.active_directory_graph_resource_id ) graph_client = GraphRbacManagementClient( cred, tenant_id, base_url=cmd.cli_ctx.cloud.endpoints.active_directory_graph_resource_id ) subscription = profile.get_subscription() # if bypass or default_action was specified create a NetworkRuleSet # if neither were specified we make network_acls None network_acls = _create_network_rule_set( bypass, default_action) if bypass or default_action else None if no_self_perms: access_policies = [] else: permissions = Permissions( keys=[ KeyPermissions.get, KeyPermissions.create, KeyPermissions.delete, KeyPermissions.list, KeyPermissions.update, KeyPermissions.import_enum, KeyPermissions.backup, KeyPermissions.restore, KeyPermissions.recover ], secrets=[ SecretPermissions.get, SecretPermissions.list, SecretPermissions.set, SecretPermissions.delete, SecretPermissions.backup, SecretPermissions.restore, SecretPermissions.recover ], certificates=[ CertificatePermissions.get, CertificatePermissions.list, CertificatePermissions.delete, CertificatePermissions.create, CertificatePermissions.import_enum, CertificatePermissions.update, CertificatePermissions.managecontacts, CertificatePermissions.getissuers, CertificatePermissions.listissuers, CertificatePermissions.setissuers, CertificatePermissions.deleteissuers, CertificatePermissions.manageissuers, CertificatePermissions.recover ], storage=[ StoragePermissions.get, StoragePermissions.list, StoragePermissions.delete, StoragePermissions.set, StoragePermissions.update, StoragePermissions.regeneratekey, StoragePermissions.setsas, StoragePermissions.listsas, StoragePermissions.getsas, StoragePermissions.deletesas ]) try: object_id = _get_current_user_object_id(graph_client) except GraphErrorException: object_id = _get_object_id(graph_client, subscription=subscription) if not object_id: raise CLIError( 'Cannot create vault.\nUnable to query active directory for information ' 'about the current user.\nYou may try the --no-self-perms flag to ' 'create a vault without permissions.') access_policies = [ AccessPolicyEntry(tenant_id=tenant_id, object_id=object_id, permissions=permissions) ] properties = VaultProperties( tenant_id=tenant_id, sku=Sku(name=sku), access_policies=access_policies, vault_uri=None, enabled_for_deployment=enabled_for_deployment, enabled_for_disk_encryption=enabled_for_disk_encryption, enabled_for_template_deployment=enabled_for_template_deployment, enable_soft_delete=enable_soft_delete, enable_purge_protection=enable_purge_protection, network_acls=network_acls) parameters = VaultCreateOrUpdateParameters(location=location, tags=tags, properties=properties) return client.create_or_update(resource_group_name=resource_group_name, vault_name=vault_name, parameters=parameters)
def account_clear(): """Clear all stored subscriptions. To clear individual, use 'logout'""" profile = Profile() profile.logout_all()
def _get_aad_token(cli_ctx, login_server, only_refresh_token, repository=None, permission='*'): """Obtains refresh and access tokens for an AAD-enabled registry. :param str login_server: The registry login server URL to log in to :param bool only_refresh_token: Whether to ask for only refresh token, or for both refresh and access tokens :param str repository: Repository for which the access token is requested :param str permission: The requested permission on the repository, '*' or 'pull' """ login_server = login_server.rstrip('/') challenge = requests.get('https://' + login_server + '/v2/', verify=(not should_disable_connection_verify())) if challenge.status_code not in [ 401 ] or 'WWW-Authenticate' not in challenge.headers: raise CLIError( "Registry '{}' did not issue a challenge.".format(login_server)) authenticate = challenge.headers['WWW-Authenticate'] tokens = authenticate.split(' ', 2) if len(tokens) < 2 or tokens[0].lower() != 'bearer': raise CLIError( "Registry '{}' does not support AAD login.".format(login_server)) params = { y[0]: y[1].strip('"') for y in (x.strip().split('=', 2) for x in tokens[1].split(',')) } if 'realm' not in params or 'service' not in params: raise CLIError( "Registry '{}' does not support AAD login.".format(login_server)) authurl = urlparse(params['realm']) authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/exchange', '', '', '')) from azure.cli.core._profile import Profile profile = Profile(cli_ctx=cli_ctx) creds, _, tenant = profile.get_raw_token() headers = {'Content-Type': 'application/x-www-form-urlencoded'} content = { 'grant_type': 'access_token', 'service': params['service'], 'tenant': tenant, 'access_token': creds[1] } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) if response.status_code not in [200]: raise CLIError( "Access to registry '{}' was denied. Response code: {}.".format( login_server, response.status_code)) refresh_token = loads(response.content.decode("utf-8"))["refresh_token"] if only_refresh_token: return refresh_token authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/token', '', '', '')) if repository is None: scope = 'registry:catalog:*' else: scope = 'repository:{}:{}'.format(repository, permission) content = { 'grant_type': 'refresh_token', 'service': login_server, 'scope': scope, 'refresh_token': refresh_token } response = requests.post(authhost, urlencode(content), headers=headers, verify=(not should_disable_connection_verify())) access_token = loads(response.content.decode("utf-8"))["access_token"] return access_token
def set_active_subscription(cmd, subscription): """Set the current subscription""" profile = Profile(cli_ctx=cmd.cli_ctx) if not id: raise CLIError('Please provide subscription id or unique name.') profile.set_active_subscription(subscription)
def _authenticate(self): try: keyvault_client_id = self._auth_params.get('keyvault_client_id') keyvault_secret_id = self._auth_params.get('keyvault_secret_id') # If user provided KeyVault secret, we will pull auth params information from it if keyvault_secret_id: self._auth_params.update( json.loads( get_keyvault_secret(keyvault_client_id, keyvault_secret_id))) client_id = self._auth_params.get('client_id') client_secret = self._auth_params.get('client_secret') access_token = self._auth_params.get('access_token') tenant_id = self._auth_params.get('tenant_id') use_msi = self._auth_params.get('use_msi') subscription_id = self._auth_params.get('subscription_id') if access_token and subscription_id: log.info("Creating session with Token Authentication") self.subscription_id = subscription_id self.credentials = BasicTokenAuthentication( token={'access_token': access_token}) self._is_token_auth = True elif client_id and client_secret and tenant_id and subscription_id: log.info( "Creating session with Service Principal Authentication") self.subscription_id = subscription_id self.credentials = ServicePrincipalCredentials( client_id=client_id, secret=client_secret, tenant=tenant_id, resource=self.resource_namespace) self.tenant_id = tenant_id elif use_msi and subscription_id: log.info("Creating session with MSI Authentication") self.subscription_id = subscription_id if client_id: self.credentials = MSIAuthentication( client_id=client_id, resource=self.resource_namespace) else: self.credentials = MSIAuthentication( resource=self.resource_namespace) elif self._auth_params.get('enable_cli_auth'): log.info("Creating session with Azure CLI Authentication") self._is_cli_auth = True (self.credentials, self.subscription_id, self.tenant_id) = Profile().get_login_credentials( resource=self.resource_namespace) log.info("Session using Subscription ID: %s" % self.subscription_id) except AuthenticationError as e: log.error('Azure Authentication Failure\n' 'Error: {0}'.format( json.dumps(e.inner_exception.error_response, indent=2))) sys.exit(1) except HTTPError as e: if keyvault_client_id and keyvault_secret_id: log.error( 'Azure Authentication Failure\n' 'Error: Cannot retrieve SP credentials from the Key Vault ' '(KV uses MSI to access) with client id: {0}'.format( keyvault_client_id)) elif use_msi: log.error( 'Azure Authentication Failure\n' 'Error: Could not authenticate using managed service identity {0}' .format(client_id if client_id else '(system identity)')) else: log.error('Azure Authentication Failure: %s' % e.response) sys.exit(1) except CLIError as e: log.error( 'Azure Authentication Failure\n' 'Error: Could not authenticate with Azure CLI credentials: {0}' .format(e)) sys.exit(1) except Exception as e: log.error('Azure Authentication Failure\n' 'Error: {0}'.format(e)) sys.exit(1)
def account_clear(cmd): """Clear all stored subscriptions. To clear individual, use 'logout'""" if in_cloud_console(): logger.warning(_CLOUD_CONSOLE_LOGOUT_WARNING) profile = Profile(cli_ctx=cmd.cli_ctx) profile.logout_all()
def aml_env_setup(self, line): from azure.cli.core._profile import Profile self._redirect_logging('az.azure.cli.core._profile') p = argparse.ArgumentParser() p.add_argument('-n', '--name', help='base name for your environment', required=True) p.add_argument('-k', dest='kubernetes', help='Flag to indicate kubernetes environment', required=False, action='store_true') p.add_argument('-l', dest='local_only', help='Flag to exclude ACS deployment', required=False, action='store_true') p.add_argument('-a', dest='service_principal_app_id', help='AppID of service principal', required=False) p.add_argument('-p', dest='service_principal_password', help='Client Secret of service principal', required=False) parsed_args = p.parse_args(line.split()) # validate that user has selected a subscription profile = Profile() subs = profile.load_cached_subscriptions() if not subs: print( 'Please run %select_sub before attempting to set up environment.' ) return from azure.cli.command_modules.ml.env import env_setup from azure.cli.command_modules.ml._util import JupyterContext c = JupyterContext() c.set_input_response('Continue with this subscription (Y/n)? ', 'y') print( 'Setting up AML environment. Feel free to continue exploring the rest ' 'of your notebook until this cell updates, though kernel will be busy...' ) with Capturing() as output: env_setup(None, parsed_args.name, parsed_args.kubernetes, parsed_args.local_only, parsed_args.service_principal_app_id, parsed_args.service_principal_password, c) acs_regex = r"az ml env setup -s (?P<deployment_id>[^']+)" env_regex = r'export (?P<k>[^=]+)=(?P<v>.+)' for line in output: s = re.search(acs_regex, line) if s: print( 'To check the status of the deployment, run line magic %check_deployment' ) else: s = re.search(env_regex, line) if s: self.print_and_update_env(s.group('k'), s.group('v')) else: print(line) print('These values have also been added to the current environment.')
def login(cmd, username=None, password=None, service_principal=None, tenant=None, allow_no_subscriptions=False, identity=False, use_device_code=False, use_cert_sn_issuer=None): """Log in to access Azure subscriptions""" from adal.adal_error import AdalError import requests # quick argument usage check if any([password, service_principal, tenant]) and identity: raise CLIError( "usage error: '--identity' is not applicable with other arguments") if any([password, service_principal, username, identity ]) and use_device_code: raise CLIError( "usage error: '--use-device-code' is not applicable with other arguments" ) if use_cert_sn_issuer and not service_principal: raise CLIError( "usage error: '--use-sn-issuer' is only applicable with a service principal" ) if service_principal and not username: raise CLIError( 'usage error: --service-principal --username NAME --password SECRET --tenant TENANT' ) interactive = False profile = Profile(cli_ctx=cmd.cli_ctx, async_persist=False) if identity: if in_cloud_console(): return profile.find_subscriptions_in_cloud_console() return profile.find_subscriptions_in_vm_with_msi( username, allow_no_subscriptions) elif in_cloud_console(): # tell users they might not need login logger.warning(_CLOUD_CONSOLE_LOGIN_WARNING) if username: if not password: try: password = prompt_pass('Password: '******'Please specify both username and password in non-interactive mode.' ) else: interactive = True try: subscriptions = profile.find_subscriptions_on_login( interactive, username, password, service_principal, tenant, use_device_code=use_device_code, allow_no_subscriptions=allow_no_subscriptions, use_cert_sn_issuer=use_cert_sn_issuer) except AdalError as err: # try polish unfriendly server errors if username: msg = str(err) suggestion = "For cross-check, try 'az login' to authenticate through browser." if ('ID3242:' in msg) or ('Server returned an unknown AccountType' in msg): raise CLIError("The user name might be invalid. " + suggestion) if 'Server returned error in RSTR - ErrorCode' in msg: raise CLIError( "Logging in through command line is not supported. " + suggestion) raise CLIError(err) except requests.exceptions.ConnectionError as err: raise CLIError( 'Please ensure you have network connection. Error detail: ' + str(err)) all_subscriptions = list(subscriptions) for sub in all_subscriptions: sub['cloudName'] = sub.pop('environmentName', None) return all_subscriptions
def _get_subscription_id(): _, sub_id, _ = Profile().get_login_credentials(subscription_id=None) return sub_id
def create_keyvault( client, resource_group_name, vault_name, location=None, #pylint:disable=too-many-arguments sku=SkuName.standard.value, enabled_for_deployment=None, enabled_for_disk_encryption=None, enabled_for_template_deployment=None, no_self_perms=None, tags=None): from azure.mgmt.keyvault.models import VaultCreateOrUpdateParameters from azure.cli.core._profile import Profile, CLOUD from azure.graphrbac.models import GraphErrorException profile = Profile() cred, _, tenant_id = profile.get_login_credentials( resource=CLOUD.endpoints.active_directory_graph_resource_id) graph_client = GraphRbacManagementClient(cred, tenant_id, base_url=CLOUD.endpoints.active_directory_graph_resource_id) # pylint: disable=line-too-long subscription = profile.get_subscription() if no_self_perms: access_policies = [] else: permissions = Permissions(keys=[ KeyPermissions.get, KeyPermissions.create, KeyPermissions.delete, KeyPermissions.list, KeyPermissions.update, KeyPermissions.import_enum, KeyPermissions.backup, KeyPermissions.restore ], secrets=[SecretPermissions.all], certificates=[CertificatePermissions.all]) try: object_id = _get_current_user_object_id(graph_client) except GraphErrorException: object_id = _get_object_id(graph_client, subscription=subscription) if not object_id: raise CLIError('Cannot create vault.\n' 'Unable to query active directory for information '\ 'about the current user.\n' 'You may try the --no-self-perms flag to create a vault'\ ' without permissions.') access_policies = [ AccessPolicyEntry(tenant_id=tenant_id, object_id=object_id, permissions=permissions) ] properties = VaultProperties( tenant_id=tenant_id, sku=Sku(name=sku), access_policies=access_policies, vault_uri=None, enabled_for_deployment=enabled_for_deployment, enabled_for_disk_encryption=enabled_for_disk_encryption, enabled_for_template_deployment=enabled_for_template_deployment) parameters = VaultCreateOrUpdateParameters(location=location, tags=tags, properties=properties) return client.create_or_update(resource_group_name=resource_group_name, vault_name=vault_name, parameters=parameters)
def logout(username=None): """Log out to remove access to Azure subscriptions""" profile = Profile() if not username: username = profile.get_current_account_user() profile.logout(username)
def _get_login_token(login_server, only_refresh_token=True, repository=None): """Obtains refresh and access tokens for an AAD-enabled registry. :param str login_server: The registry login server URL to log in to :param bool only_refresh_token: Whether to ask for only refresh token, or for both refresh and access tokens :param str repository: Repository for which the access token is requested """ login_server = login_server.rstrip('/') challenge = requests.get('https://' + login_server + '/v2/') if challenge.status_code not in [ 401 ] or 'WWW-Authenticate' not in challenge.headers: raise CLIError( "Registry '{}' did not issue a challenge.".format(login_server)) authenticate = challenge.headers['WWW-Authenticate'] tokens = authenticate.split(' ', 2) if len(tokens) < 2 or tokens[0].lower() != 'bearer': raise CLIError( "Registry '{}' does not support AAD login.".format(login_server)) params = { y[0]: y[1].strip('"') for y in (x.strip().split('=', 2) for x in tokens[1].split(',')) } if 'realm' not in params or 'service' not in params: raise CLIError( "Registry '{}' does not support AAD login.".format(login_server)) authurl = urlparse(params['realm']) authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/exchange', '', '', '')) from azure.cli.core._profile import Profile profile = Profile() sp_id, refresh, access, tenant = profile.get_refresh_token() headers = {'Content-Type': 'application/x-www-form-urlencoded'} if not sp_id: if not refresh: content = { 'grant_type': 'access_token', 'service': params['service'], 'tenant': tenant, 'access_token': access } else: content = { 'grant_type': 'access_token_refresh_token', 'service': params['service'], 'tenant': tenant, 'access_token': access, 'refresh_token': refresh } else: content = { 'grant_type': 'spn', 'service': params['service'], 'tenant': tenant, 'username': sp_id, 'password': refresh } response = requests.post(authhost, urlencode(content), headers=headers) if response.status_code not in [200]: raise CLIError( "Access to registry '{}' was denied. Response code: {}.".format( login_server, response.status_code)) refresh_token = loads(response.content.decode("utf-8"))["refresh_token"] if only_refresh_token: return refresh_token, None authhost = urlunparse( (authurl[0], authurl[1], '/oauth2/token', '', '', '')) if repository is None: scope = 'registry:catalog:*' else: scope = 'repository:' + repository + ':*' content = { 'grant_type': 'refresh_token', 'service': login_server, 'scope': scope, 'refresh_token': refresh_token } response = requests.post(authhost, urlencode(content), headers=headers) access_token = loads(response.content.decode("utf-8"))["access_token"] return refresh_token, access_token