Skip to content

Commit 66e59c6

Browse files
authored
Merge pull request #9 from Azure/dev
Update 4/25
2 parents 343c993 + f82c201 commit 66e59c6

File tree

395 files changed

+108598
-92269
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

395 files changed

+108598
-92269
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,5 @@
5656
/src/azure-cli/azure/cli/command_modules/sql/ @jaredmoo @Juliehzl @evelyn-ys
5757
/src/azure-cli/azure/cli/command_modules/storage/ @Juliehzl @jsntcy @zhoxing-ms @evelyn-ys
5858
/src/azure-cli/azure/cli/command_modules/synapse/ @idear1203 @sunsw1994 @aim-for-better
59-
/src/azure-cli/azure/cli/command_modules/util/ @jiasli @Juliehzl @zhoxing-ms
59+
/src/azure-cli/azure/cli/command_modules/util/ @jiasli @Juliehzl @zhoxing-ms @evelyn-ys
6060
/src/azure-cli/azure/cli/command_modules/vm/ @qwordy @houk-ms @yungezz

doc/track_2_migration_roadmap.md

Lines changed: 151 additions & 123 deletions
Large diffs are not rendered by default.

src/azure-cli-core/HISTORY.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Release History
44
===============
55

6+
2.22.1
7+
++++++
8+
* Minor fixes
9+
610
2.22.0
711
++++++
812
* Add spinner progress bar for long running operation (#17262)

src/azure-cli-core/azure/cli/core/__init__.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# --------------------------------------------------------------------------------------------
55
# pylint: disable=line-too-long
66

7-
__version__ = "2.22.0"
7+
__version__ = "2.22.1"
88

99
import os
1010
import sys
@@ -64,7 +64,6 @@ def __init__(self, **kwargs):
6464
from azure.cli.core.cloud import get_active_cloud
6565
from azure.cli.core.commands.transform import register_global_transforms
6666
from azure.cli.core._session import ACCOUNT, CONFIG, SESSION, INDEX, VERSIONS
67-
from azure.cli.core.style import format_styled_text
6867
from azure.cli.core.util import handle_version_update
6968
from azure.cli.core.commands.query_examples import register_global_query_examples_argument
7069

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

9796
self.progress_controller = None
9897

99-
if self.enable_color:
100-
theme = self.config.get('core', 'theme', fallback='dark')
101-
else:
102-
theme = 'none'
103-
format_styled_text.theme = theme
98+
self._configure_style()
10499

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

180+
def _configure_style(self):
181+
from azure.cli.core.util import in_cloud_console
182+
from azure.cli.core.style import format_styled_text, get_theme_dict, Style
183+
184+
# Configure Style
185+
if self.enable_color:
186+
theme = self.config.get('core', 'theme',
187+
fallback="cloud-shell" if in_cloud_console() else "dark")
188+
189+
theme_dict = get_theme_dict(theme)
190+
191+
if theme_dict:
192+
# If theme is used, also apply it to knack's logger
193+
from knack.util import color_map
194+
color_map['error'] = theme_dict[Style.ERROR]
195+
color_map['warning'] = theme_dict[Style.WARNING]
196+
else:
197+
theme = 'none'
198+
format_styled_text.theme = theme
199+
185200

186201
class MainCommandsLoader(CLICommandsLoader):
187202

src/azure-cli-core/azure/cli/core/_profile.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from azure.cli.core._environment import get_config_dir
2020
from azure.cli.core._session import ACCOUNT
2121
from azure.cli.core.util import get_file_json, in_cloud_console, open_page_in_browser, can_launch_browser,\
22-
is_windows, is_wsl
22+
is_windows, is_wsl, scopes_to_resource
2323
from azure.cli.core.cloud import get_active_cloud, set_cloud_subscription
2424

2525
logger = get_logger(__name__)
@@ -172,6 +172,7 @@ def find_subscriptions_on_login(self,
172172
password,
173173
is_service_principal,
174174
tenant,
175+
scopes=None,
175176
use_device_code=False,
176177
allow_no_subscriptions=False,
177178
subscription_finder=None,
@@ -180,6 +181,11 @@ def find_subscriptions_on_login(self,
180181
allow_debug_adal_connection()
181182
subscriptions = []
182183

184+
if scopes:
185+
auth_resource = scopes_to_resource(scopes)
186+
else:
187+
auth_resource = self._ad_resource_uri
188+
183189
if not subscription_finder:
184190
subscription_finder = SubscriptionFinder(self.cli_ctx,
185191
self.auth_ctx_factory,
@@ -193,14 +199,14 @@ def find_subscriptions_on_login(self,
193199
try:
194200
authority_url, _ = _get_authority_url(self.cli_ctx, tenant)
195201
subscriptions = subscription_finder.find_through_authorization_code_flow(
196-
tenant, self._ad_resource_uri, authority_url)
202+
tenant, self._ad_resource_uri, authority_url, auth_resource=auth_resource)
197203
except RuntimeError:
198204
use_device_code = True
199205
logger.warning('Not able to launch a browser to log you in, falling back to device code...')
200206

201207
if use_device_code:
202208
subscriptions = subscription_finder.find_through_interactive_flow(
203-
tenant, self._ad_resource_uri)
209+
tenant, self._ad_resource_uri, auth_resource=auth_resource)
204210
else:
205211
if is_service_principal:
206212
if not tenant:
@@ -561,8 +567,9 @@ def get_login_credentials(self, resource=None, subscription_id=None, aux_subscri
561567
if sub[_TENANT_ID] != account[_TENANT_ID]:
562568
external_tenants_info.append(sub[_TENANT_ID])
563569

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

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

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

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

897-
def find_through_authorization_code_flow(self, tenant, resource, authority_url):
903+
def find_through_authorization_code_flow(self, tenant, resource, authority_url, auth_resource=None):
898904
# launch browser and get the code
899-
results = _get_authorization_code(resource, authority_url)
905+
results = _get_authorization_code(auth_resource or resource, authority_url)
900906

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

916-
def find_through_interactive_flow(self, tenant, resource):
922+
def find_through_interactive_flow(self, tenant, resource, auth_resource=None):
917923
context = self._create_auth_context(tenant)
918-
code = context.acquire_user_code(resource, _CLIENT_ID)
924+
code = context.acquire_user_code(auth_resource or resource, _CLIENT_ID)
919925
logger.warning(code['message'])
920926
token_entry = context.acquire_token_with_device_code(resource, code, _CLIENT_ID)
921927
self.user_id = token_entry[_TOKEN_ENTRY_USER_ID]

src/azure-cli-core/azure/cli/core/commands/__init__.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,7 @@ def _generate_template_progress(self, correlation_id): # pylint: disable=no-sel
945945

946946
def __call__(self, poller):
947947
from msrest.exceptions import ClientException
948+
from azure.core.exceptions import HttpResponseError
948949

949950
correlation_message = ''
950951
self.progress_bar.begin()
@@ -984,14 +985,22 @@ def __call__(self, poller):
984985

985986
try:
986987
result = poller.result()
987-
except ClientException as client_exception:
988+
except (ClientException, HttpResponseError) as exception:
988989
from azure.cli.core.commands.arm import handle_long_running_operation_exception
989990
self.progress_bar.stop()
990-
handle_long_running_operation_exception(client_exception)
991-
992-
self.progress_bar.end()
993-
if poll_flag:
994-
telemetry.poll_end()
991+
if getattr(exception, 'status_code', None) == 404 and \
992+
('delete' in self.cli_ctx.data['command'] or 'purge' in self.cli_ctx.data['command']):
993+
logger.debug('Service returned 404 on the long-running delete or purge operation. CLI treats it as '
994+
'delete or purge successfully but service should fix this behavior.')
995+
return None
996+
if isinstance(exception, ClientException):
997+
handle_long_running_operation_exception(exception)
998+
else:
999+
raise exception
1000+
finally:
1001+
self.progress_bar.end()
1002+
if poll_flag:
1003+
telemetry.poll_end()
9951004

9961005
return result
9971006

src/azure-cli-core/azure/cli/core/commands/client_factory.py

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,6 @@ def _prepare_client_kwargs_track2(cli_ctx):
123123
# Prepare connection_verify to change SSL verification behavior, used by ConnectionConfiguration
124124
client_kwargs.update(_debug.change_ssl_cert_verification_track2())
125125

126-
# Enable NetworkTraceLoggingPolicy which logs all headers (except Authorization) without being redacted
127-
client_kwargs['logging_enable'] = True
128-
129-
# Disable ARMHttpLoggingPolicy which logs only allowed headers
130-
# from azure.core.pipeline.policies import SansIOHTTPPolicy
131-
# client_kwargs['http_logging_policy'] = SansIOHTTPPolicy()
132-
133126
# Prepare User-Agent header, used by UserAgentPolicy
134127
client_kwargs['user_agent'] = get_az_user_agent()
135128

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

153+
# Replace NetworkTraceLoggingPolicy to redact 'Authorization' and 'x-ms-authorization-auxiliary' headers.
154+
# NetworkTraceLoggingPolicy: log raw network trace, with all headers.
155+
from azure.cli.core.sdk.policies import SafeNetworkTraceLoggingPolicy
156+
client_kwargs['logging_policy'] = SafeNetworkTraceLoggingPolicy()
157+
158+
# Disable ARMHttpLoggingPolicy.
159+
# ARMHttpLoggingPolicy: Only log allowed information.
160+
from azure.core.pipeline.policies import SansIOHTTPPolicy
161+
client_kwargs['http_logging_policy'] = SansIOHTTPPolicy()
162+
163+
return client_kwargs
164+
165+
166+
def _prepare_mgmt_client_kwargs_track2(cli_ctx, cred):
167+
"""Prepare kwargs for Track 2 SDK mgmt client."""
168+
client_kwargs = _prepare_client_kwargs_track2(cli_ctx)
169+
170+
from azure.cli.core.util import resource_to_scopes
171+
# Track 2 SDK maintains `scopes` and passes `scopes` to get_token.
172+
scopes = resource_to_scopes(cli_ctx.cloud.endpoints.active_directory_resource_id)
173+
174+
client_kwargs['credential_scopes'] = scopes
175+
176+
# Track 2 currently lacks the ability to take external credentials.
177+
# https://github.com/Azure/azure-sdk-for-python/issues/8313
178+
# As a temporary workaround, manually add external tokens to 'x-ms-authorization-auxiliary' header.
179+
# https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/authenticate-multi-tenant
180+
if getattr(cred, "_external_tenant_token_retriever", None):
181+
*_, external_tenant_tokens = cred.get_all_tokens(*scopes)
182+
# Hard-code scheme to 'Bearer' as _BearerTokenCredentialPolicyBase._update_headers does.
183+
client_kwargs['headers']['x-ms-authorization-auxiliary'] = \
184+
', '.join("Bearer {}".format(t[1]) for t in external_tenant_tokens)
185+
160186
return client_kwargs
161187

162188

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

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

207221
if subscription_bound:
208222
client = client_type(cred, subscription_id, **client_kwargs)

src/azure-cli-core/azure/cli/core/extension/dynamic_install.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def _get_all_extensions(cmd_chain, ext_set=None):
6565
return ext_set
6666

6767

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

@@ -137,7 +139,8 @@ def _check_value_in_extensions(cli_ctx, parser, args, no_prompt): # pylint: dis
137139
from azure.cli.core.azclierror import NoTTYError
138140
exit_code = 2
139141
command_str = roughly_parse_command(args[1:])
140-
ext_name = _search_in_extension_commands(cli_ctx, command_str)
142+
allow_prefix_match = args[-1] == '-h' or args[-1] == '--help'
143+
ext_name = _search_in_extension_commands(cli_ctx, command_str, allow_prefix_match=allow_prefix_match)
141144
# ext_name is a list if the input command matches the prefix of one or more extension commands,
142145
# for instance: `az blueprint` when running `az blueprint -h`
143146
# ext_name is a str if the input command matches a complete command of an extension,

src/azure-cli-core/azure/cli/core/profiles/_shared.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def default_api_version(self):
135135

136136
AZURE_API_PROFILES = {
137137
'latest': {
138-
ResourceType.MGMT_STORAGE: '2021-01-01',
138+
ResourceType.MGMT_STORAGE: '2021-02-01',
139139
ResourceType.MGMT_NETWORK: '2020-11-01',
140140
ResourceType.MGMT_COMPUTE: SDKProfile('2020-12-01', {
141141
'resource_skus': '2019-04-01',
@@ -157,7 +157,7 @@ def default_api_version(self):
157157
ResourceType.MGMT_RESOURCE_DEPLOYMENTSCRIPTS: '2020-10-01',
158158
ResourceType.MGMT_RESOURCE_TEMPLATESPECS: '2019-06-01-preview',
159159
ResourceType.MGMT_NETWORK_DNS: '2018-05-01',
160-
ResourceType.MGMT_KEYVAULT: '2020-04-01-preview',
160+
ResourceType.MGMT_KEYVAULT: '2021-04-01-preview',
161161
ResourceType.MGMT_AUTHORIZATION: SDKProfile('2020-04-01-preview', {
162162
'classic_administrators': '2015-06-01',
163163
'role_definitions': '2018-01-01-preview',
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
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+
# --------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)