Skip to content

Commit

Permalink
Merge pull request #9 from Azure/dev
Browse files Browse the repository at this point in the history
Update 4/25
  • Loading branch information
BigCat20196 authored Apr 25, 2021
2 parents 343c993 + f82c201 commit 66e59c6
Show file tree
Hide file tree
Showing 395 changed files with 108,598 additions and 92,269 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@
/src/azure-cli/azure/cli/command_modules/sql/ @jaredmoo @Juliehzl @evelyn-ys
/src/azure-cli/azure/cli/command_modules/storage/ @Juliehzl @jsntcy @zhoxing-ms @evelyn-ys
/src/azure-cli/azure/cli/command_modules/synapse/ @idear1203 @sunsw1994 @aim-for-better
/src/azure-cli/azure/cli/command_modules/util/ @jiasli @Juliehzl @zhoxing-ms
/src/azure-cli/azure/cli/command_modules/util/ @jiasli @Juliehzl @zhoxing-ms @evelyn-ys
/src/azure-cli/azure/cli/command_modules/vm/ @qwordy @houk-ms @yungezz
274 changes: 151 additions & 123 deletions doc/track_2_migration_roadmap.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/azure-cli-core/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
Release History
===============

2.22.1
++++++
* Minor fixes

2.22.0
++++++
* Add spinner progress bar for long running operation (#17262)
Expand Down
29 changes: 22 additions & 7 deletions src/azure-cli-core/azure/cli/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# --------------------------------------------------------------------------------------------
# pylint: disable=line-too-long

__version__ = "2.22.0"
__version__ = "2.22.1"

import os
import sys
Expand Down Expand Up @@ -64,7 +64,6 @@ def __init__(self, **kwargs):
from azure.cli.core.cloud import get_active_cloud
from azure.cli.core.commands.transform import register_global_transforms
from azure.cli.core._session import ACCOUNT, CONFIG, SESSION, INDEX, VERSIONS
from azure.cli.core.style import format_styled_text
from azure.cli.core.util import handle_version_update
from azure.cli.core.commands.query_examples import register_global_query_examples_argument

Expand Down Expand Up @@ -96,11 +95,7 @@ def __init__(self, **kwargs):

self.progress_controller = None

if self.enable_color:
theme = self.config.get('core', 'theme', fallback='dark')
else:
theme = 'none'
format_styled_text.theme = theme
self._configure_style()

def refresh_request_id(self):
"""Assign a new random GUID as x-ms-client-request-id
Expand Down Expand Up @@ -182,6 +177,26 @@ def save_local_context(self, parsed_args, argument_definitions, specified_argume
logger.warning('Your preference of %s now saved to local context. To learn more, type in `az '
'local-context --help`', ', '.join(args_str) + ' is' if len(args_str) == 1 else ' are')

def _configure_style(self):
from azure.cli.core.util import in_cloud_console
from azure.cli.core.style import format_styled_text, get_theme_dict, Style

# Configure Style
if self.enable_color:
theme = self.config.get('core', 'theme',
fallback="cloud-shell" if in_cloud_console() else "dark")

theme_dict = get_theme_dict(theme)

if theme_dict:
# If theme is used, also apply it to knack's logger
from knack.util import color_map
color_map['error'] = theme_dict[Style.ERROR]
color_map['warning'] = theme_dict[Style.WARNING]
else:
theme = 'none'
format_styled_text.theme = theme


class MainCommandsLoader(CLICommandsLoader):

Expand Down
26 changes: 16 additions & 10 deletions src/azure-cli-core/azure/cli/core/_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from azure.cli.core._environment import get_config_dir
from azure.cli.core._session import ACCOUNT
from azure.cli.core.util import get_file_json, in_cloud_console, open_page_in_browser, can_launch_browser,\
is_windows, is_wsl
is_windows, is_wsl, scopes_to_resource
from azure.cli.core.cloud import get_active_cloud, set_cloud_subscription

logger = get_logger(__name__)
Expand Down Expand Up @@ -172,6 +172,7 @@ def find_subscriptions_on_login(self,
password,
is_service_principal,
tenant,
scopes=None,
use_device_code=False,
allow_no_subscriptions=False,
subscription_finder=None,
Expand All @@ -180,6 +181,11 @@ def find_subscriptions_on_login(self,
allow_debug_adal_connection()
subscriptions = []

if scopes:
auth_resource = scopes_to_resource(scopes)
else:
auth_resource = self._ad_resource_uri

if not subscription_finder:
subscription_finder = SubscriptionFinder(self.cli_ctx,
self.auth_ctx_factory,
Expand All @@ -193,14 +199,14 @@ def find_subscriptions_on_login(self,
try:
authority_url, _ = _get_authority_url(self.cli_ctx, tenant)
subscriptions = subscription_finder.find_through_authorization_code_flow(
tenant, self._ad_resource_uri, authority_url)
tenant, self._ad_resource_uri, authority_url, auth_resource=auth_resource)
except RuntimeError:
use_device_code = True
logger.warning('Not able to launch a browser to log you in, falling back to device code...')

if use_device_code:
subscriptions = subscription_finder.find_through_interactive_flow(
tenant, self._ad_resource_uri)
tenant, self._ad_resource_uri, auth_resource=auth_resource)
else:
if is_service_principal:
if not tenant:
Expand Down Expand Up @@ -561,8 +567,9 @@ def get_login_credentials(self, resource=None, subscription_id=None, aux_subscri
if sub[_TENANT_ID] != account[_TENANT_ID]:
external_tenants_info.append(sub[_TENANT_ID])

if external_tenants_info and (identity_type or in_cloud_console()):
raise CLIError("Cross-tenant authentication is not supported by managed identity and Cloud Shell. "
if external_tenants_info and \
(in_cloud_console() and account[_USER_ENTITY].get(_CLOUD_SHELL_ID) or identity_type):
raise CLIError("Cross-tenant authentication is not supported by managed identity and Cloud Shell account. "
"Please run `az login` with a user account or a service principal.")

if identity_type is None:
Expand Down Expand Up @@ -638,7 +645,6 @@ def get_msal_token(self, scopes, data):
if 'error' in result:
logger.warning(result['error_description'])

from azure.cli.core.util import scopes_to_resource
token_entry = self._login_with_authorization_code_flow(tenant, scopes_to_resource(scopes))
result = cred.acquire_token_by_refresh_token(token_entry['refreshToken'], scopes, data=data)

Expand Down Expand Up @@ -894,9 +900,9 @@ def find_from_user_account(self, username, password, tenant, resource):
result = self._find_using_specific_tenant(tenant, token_entry[_ACCESS_TOKEN])
return result

def find_through_authorization_code_flow(self, tenant, resource, authority_url):
def find_through_authorization_code_flow(self, tenant, resource, authority_url, auth_resource=None):
# launch browser and get the code
results = _get_authorization_code(resource, authority_url)
results = _get_authorization_code(auth_resource or resource, authority_url)

if not results.get('code'):
raise CLIError('Login failed') # error detail is already displayed through previous steps
Expand All @@ -913,9 +919,9 @@ def find_through_authorization_code_flow(self, tenant, resource, authority_url):
result = self._find_using_specific_tenant(tenant, token_entry[_ACCESS_TOKEN])
return result

def find_through_interactive_flow(self, tenant, resource):
def find_through_interactive_flow(self, tenant, resource, auth_resource=None):
context = self._create_auth_context(tenant)
code = context.acquire_user_code(resource, _CLIENT_ID)
code = context.acquire_user_code(auth_resource or resource, _CLIENT_ID)
logger.warning(code['message'])
token_entry = context.acquire_token_with_device_code(resource, code, _CLIENT_ID)
self.user_id = token_entry[_TOKEN_ENTRY_USER_ID]
Expand Down
21 changes: 15 additions & 6 deletions src/azure-cli-core/azure/cli/core/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,7 @@ def _generate_template_progress(self, correlation_id): # pylint: disable=no-sel

def __call__(self, poller):
from msrest.exceptions import ClientException
from azure.core.exceptions import HttpResponseError

correlation_message = ''
self.progress_bar.begin()
Expand Down Expand Up @@ -984,14 +985,22 @@ def __call__(self, poller):

try:
result = poller.result()
except ClientException as client_exception:
except (ClientException, HttpResponseError) as exception:
from azure.cli.core.commands.arm import handle_long_running_operation_exception
self.progress_bar.stop()
handle_long_running_operation_exception(client_exception)

self.progress_bar.end()
if poll_flag:
telemetry.poll_end()
if getattr(exception, 'status_code', None) == 404 and \
('delete' in self.cli_ctx.data['command'] or 'purge' in self.cli_ctx.data['command']):
logger.debug('Service returned 404 on the long-running delete or purge operation. CLI treats it as '
'delete or purge successfully but service should fix this behavior.')
return None
if isinstance(exception, ClientException):
handle_long_running_operation_exception(exception)
else:
raise exception
finally:
self.progress_bar.end()
if poll_flag:
telemetry.poll_end()

return result

Expand Down
54 changes: 34 additions & 20 deletions src/azure-cli-core/azure/cli/core/commands/client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,6 @@ def _prepare_client_kwargs_track2(cli_ctx):
# Prepare connection_verify to change SSL verification behavior, used by ConnectionConfiguration
client_kwargs.update(_debug.change_ssl_cert_verification_track2())

# Enable NetworkTraceLoggingPolicy which logs all headers (except Authorization) without being redacted
client_kwargs['logging_enable'] = True

# Disable ARMHttpLoggingPolicy which logs only allowed headers
# from azure.core.pipeline.policies import SansIOHTTPPolicy
# client_kwargs['http_logging_policy'] = SansIOHTTPPolicy()

# Prepare User-Agent header, used by UserAgentPolicy
client_kwargs['user_agent'] = get_az_user_agent()

Expand Down Expand Up @@ -157,6 +150,39 @@ def _prepare_client_kwargs_track2(cli_ctx):
if 'x-ms-client-request-id' in cli_ctx.data['headers']:
client_kwargs['request_id'] = cli_ctx.data['headers']['x-ms-client-request-id']

# Replace NetworkTraceLoggingPolicy to redact 'Authorization' and 'x-ms-authorization-auxiliary' headers.
# NetworkTraceLoggingPolicy: log raw network trace, with all headers.
from azure.cli.core.sdk.policies import SafeNetworkTraceLoggingPolicy
client_kwargs['logging_policy'] = SafeNetworkTraceLoggingPolicy()

# Disable ARMHttpLoggingPolicy.
# ARMHttpLoggingPolicy: Only log allowed information.
from azure.core.pipeline.policies import SansIOHTTPPolicy
client_kwargs['http_logging_policy'] = SansIOHTTPPolicy()

return client_kwargs


def _prepare_mgmt_client_kwargs_track2(cli_ctx, cred):
"""Prepare kwargs for Track 2 SDK mgmt client."""
client_kwargs = _prepare_client_kwargs_track2(cli_ctx)

from azure.cli.core.util import resource_to_scopes
# Track 2 SDK maintains `scopes` and passes `scopes` to get_token.
scopes = resource_to_scopes(cli_ctx.cloud.endpoints.active_directory_resource_id)

client_kwargs['credential_scopes'] = scopes

# Track 2 currently lacks the ability to take external credentials.
# https://github.com/Azure/azure-sdk-for-python/issues/8313
# As a temporary workaround, manually add external tokens to 'x-ms-authorization-auxiliary' header.
# https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/authenticate-multi-tenant
if getattr(cred, "_external_tenant_token_retriever", None):
*_, external_tenant_tokens = cred.get_all_tokens(*scopes)
# Hard-code scheme to 'Bearer' as _BearerTokenCredentialPolicyBase._update_headers does.
client_kwargs['headers']['x-ms-authorization-auxiliary'] = \
', '.join("Bearer {}".format(t[1]) for t in external_tenant_tokens)

return client_kwargs


Expand All @@ -172,7 +198,6 @@ def _get_mgmt_service_client(cli_ctx,
aux_tenants=None,
**kwargs):
from azure.cli.core._profile import Profile
from azure.cli.core.util import resource_to_scopes
logger.debug('Getting management service client client_type=%s', client_type.__name__)
resource = resource or cli_ctx.cloud.endpoints.active_directory_resource_id
profile = Profile(cli_ctx=cli_ctx)
Expand All @@ -191,18 +216,7 @@ def _get_mgmt_service_client(cli_ctx,
client_kwargs.update(kwargs)

if is_track2(client_type):
client_kwargs.update(_prepare_client_kwargs_track2(cli_ctx))
client_kwargs['credential_scopes'] = resource_to_scopes(resource)

# Track 2 currently lacks the ability to take external credentials.
# https://github.com/Azure/azure-sdk-for-python/issues/8313
# As a temporary workaround, manually add external tokens to 'x-ms-authorization-auxiliary' header.
# https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/authenticate-multi-tenant
if getattr(cred, "_external_tenant_token_retriever", None):
*_, external_tenant_tokens = cred.get_all_tokens(*resource_to_scopes(resource))
# Hard-code scheme to 'Bearer' as _BearerTokenCredentialPolicyBase._update_headers does.
client_kwargs['headers']['x-ms-authorization-auxiliary'] = \
', '.join("Bearer {}".format(t[1]) for t in external_tenant_tokens)
client_kwargs.update(_prepare_mgmt_client_kwargs_track2(cli_ctx, cred))

if subscription_bound:
client = client_type(cred, subscription_id, **client_kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def _get_all_extensions(cmd_chain, ext_set=None):
return ext_set


def _search_in_extension_commands(cli_ctx, command_str):
def _search_in_extension_commands(cli_ctx, command_str, allow_prefix_match=False):
"""Search the command in an extension commands dict which mimics a prefix tree.
If the value of the dict item is a string, then the key represents the end of a complete command
and the value is the name of the extension that the command belongs to.
Expand Down Expand Up @@ -95,6 +95,8 @@ def _search_in_extension_commands(cli_ctx, command_str):
except KeyError:
return None
# command_str is prefix of one or more complete commands.
if not allow_prefix_match:
return None
all_exts = _get_all_extensions(cmd_chain)
return list(all_exts) if all_exts else None

Expand Down Expand Up @@ -137,7 +139,8 @@ def _check_value_in_extensions(cli_ctx, parser, args, no_prompt): # pylint: dis
from azure.cli.core.azclierror import NoTTYError
exit_code = 2
command_str = roughly_parse_command(args[1:])
ext_name = _search_in_extension_commands(cli_ctx, command_str)
allow_prefix_match = args[-1] == '-h' or args[-1] == '--help'
ext_name = _search_in_extension_commands(cli_ctx, command_str, allow_prefix_match=allow_prefix_match)
# ext_name is a list if the input command matches the prefix of one or more extension commands,
# for instance: `az blueprint` when running `az blueprint -h`
# ext_name is a str if the input command matches a complete command of an extension,
Expand Down
4 changes: 2 additions & 2 deletions src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def default_api_version(self):

AZURE_API_PROFILES = {
'latest': {
ResourceType.MGMT_STORAGE: '2021-01-01',
ResourceType.MGMT_STORAGE: '2021-02-01',
ResourceType.MGMT_NETWORK: '2020-11-01',
ResourceType.MGMT_COMPUTE: SDKProfile('2020-12-01', {
'resource_skus': '2019-04-01',
Expand All @@ -157,7 +157,7 @@ def default_api_version(self):
ResourceType.MGMT_RESOURCE_DEPLOYMENTSCRIPTS: '2020-10-01',
ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2019-06-01-preview',
ResourceType.MGMT_NETWORK_DNS: '2018-05-01',
ResourceType.MGMT_KEYVAULT: '2020-04-01-preview',
ResourceType.MGMT_KEYVAULT: '2021-04-01-preview',
ResourceType.MGMT_AUTHORIZATION: SDKProfile('2020-04-01-preview', {
'classic_administrators': '2015-06-01',
'role_definitions': '2018-01-01-preview',
Expand Down
4 changes: 4 additions & 0 deletions src/azure-cli-core/azure/cli/core/sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
Loading

0 comments on commit 66e59c6

Please sign in to comment.