Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package azure-cli-core for openSUSE:Factory 
checked in at 2026-05-05 17:58:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/azure-cli-core (Old)
 and      /work/SRC/openSUSE:Factory/.azure-cli-core.new.30200 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "azure-cli-core"

Tue May  5 17:58:12 2026 rev:95 rq:1350989 version:2.86.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/azure-cli-core/azure-cli-core.changes    
2026-04-20 16:14:09.660281103 +0200
+++ /work/SRC/openSUSE:Factory/.azure-cli-core.new.30200/azure-cli-core.changes 
2026-05-05 17:58:19.627109098 +0200
@@ -1,0 +2,9 @@
+Tue May  5 11:41:23 UTC 2026 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- New upstream release
+  + Version 2.86.0
+  + For detailed information about changes see the
+    HISTORY.rst file provided with this package
+- Update Requires from setup.py
+
+-------------------------------------------------------------------

Old:
----
  azure_cli_core-2.85.0.tar.gz

New:
----
  azure_cli_core-2.86.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ azure-cli-core.spec ++++++
--- /var/tmp/diff_new_pack.5C44VW/_old  2026-05-05 17:58:20.235134243 +0200
+++ /var/tmp/diff_new_pack.5C44VW/_new  2026-05-05 17:58:20.239134409 +0200
@@ -24,7 +24,7 @@
 %global _sitelibdir %{%{pythons}_sitelib}
 
 Name:           azure-cli-core
-Version:        2.85.0
+Version:        2.86.0
 Release:        0
 Summary:        Microsoft Azure CLI Core Module
 License:        MIT
@@ -56,7 +56,7 @@
 Requires:       %{pythons}-msal < 2.0.0
 Requires:       %{pythons}-msal >= 1.35.1
 Requires:       %{pythons}-msal-extensions < 2.0.0
-Requires:       %{pythons}-msal-extensions >= 1.2.0
+Requires:       %{pythons}-msal-extensions >= 1.3.1
 Requires:       %{pythons}-packaging >= 20.9
 Requires:       %{pythons}-pip
 Requires:       %{pythons}-pkginfo >= 1.5.0.1

++++++ azure_cli_core-2.85.0.tar.gz -> azure_cli_core-2.86.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/HISTORY.rst 
new/azure_cli_core-2.86.0/HISTORY.rst
--- old/azure_cli_core-2.85.0/HISTORY.rst       2026-03-31 09:18:57.000000000 
+0200
+++ new/azure_cli_core-2.86.0/HISTORY.rst       2026-05-01 02:40:32.000000000 
+0200
@@ -3,6 +3,12 @@
 Release History
 ===============
 
+2.86.0
+++++++
+* Resolve CVE-2025-15467 (#33201)
+* Resolve CVE-2025-69419 (#33201)
+* Resolve CVE-2026-39892 (#33154)
+
 2.85.0
 ++++++
 * Resolve CVE-2026-26007 (#32879)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/PKG-INFO 
new/azure_cli_core-2.86.0/PKG-INFO
--- old/azure_cli_core-2.85.0/PKG-INFO  2026-03-31 09:19:45.048640300 +0200
+++ new/azure_cli_core-2.86.0/PKG-INFO  2026-05-01 02:41:23.844106700 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: azure-cli-core
-Version: 2.85.0
+Version: 2.86.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -28,7 +28,7 @@
 Requires-Dist: jmespath
 Requires-Dist: knack~=0.11.0
 Requires-Dist: microsoft-security-utilities-secret-masker~=1.0.0b4
-Requires-Dist: msal-extensions==1.2.0
+Requires-Dist: msal-extensions==1.3.1
 Requires-Dist: msal[broker]==1.35.1; sys_platform == "win32"
 Requires-Dist: msal==1.35.1; sys_platform != "win32"
 Requires-Dist: packaging>=20.9
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/azure/cli/core/__init__.py 
new/azure_cli_core-2.86.0/azure/cli/core/__init__.py
--- old/azure_cli_core-2.85.0/azure/cli/core/__init__.py        2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/__init__.py        2026-05-01 
02:40:32.000000000 +0200
@@ -4,7 +4,7 @@
 # 
--------------------------------------------------------------------------------------------
 # pylint: disable=line-too-long
 
-__version__ = "2.85.0"
+__version__ = "2.86.0"
 
 import os
 import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/azure/cli/core/_help.py 
new/azure_cli_core-2.86.0/azure/cli/core/_help.py
--- old/azure_cli_core-2.85.0/azure/cli/core/_help.py   2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/_help.py   2026-05-01 
02:40:32.000000000 +0200
@@ -129,7 +129,6 @@
     def _print_detailed_help(self, cli_name, help_file):
         CLIPrintMixin._print_extensions_msg(help_file)
         super()._print_detailed_help(cli_name, help_file)
-        self._print_az_find_message(help_file.command)
 
     @staticmethod
     def _get_choices_defaults_sources_str(p):
@@ -155,12 +154,6 @@
             print('')
 
     @staticmethod
-    def _print_az_find_message(command):
-        indent = 0
-        message = 'To search AI knowledge base for examples, use: az find "az 
{}"'.format(command)
-        _print_indent(message + '\n', indent)
-
-    @staticmethod
     def _process_value_sources(p):
         commands, strings, urls = [], [], []
 
@@ -401,10 +394,7 @@
 
         self._print_cached_help_section(groups_items, "Subgroups:", 
max_line_len)
         self._print_cached_help_section(commands_items, "Commands:", 
max_line_len)
-
-        # Use same az find message as non-cached path
-        print()  # Blank line before the message
-        self._print_az_find_message('')
+        print()
 
         from azure.cli.core.util import show_updates_available
         show_updates_available(new_line_after=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/azure/cli/core/_profile.py 
new/azure_cli_core-2.86.0/azure/cli/core/_profile.py
--- old/azure_cli_core-2.85.0/azure/cli/core/_profile.py        2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/_profile.py        2026-05-01 
02:40:32.000000000 +0200
@@ -152,7 +152,9 @@
               allow_no_subscriptions=False,
               use_cert_sn_issuer=None,
               show_progress=False,
-              claims_challenge=None):
+              claims_challenge=None,
+              skip_subscription_discovery=False,
+              subscription=None):
         """
         For service principal, `password` is a dict returned by 
ServicePrincipalAuth.build_credential
         """
@@ -198,15 +200,31 @@
         else:
             credential = identity.get_service_principal_credential(username)
 
-        if tenant:
+        is_bare_mode = skip_subscription_discovery and not subscription
+
+        if skip_subscription_discovery and subscription:
+            # Fast path: fetch only the specified subscription (1 API call)
+            subscriptions = subscription_finder.find_specific_subscriptions(
+                tenant, credential, [subscription])
+        elif is_bare_mode:
+            # Bare mode: no ARM subscription calls. Tenant-level account will 
be created below
+            subscriptions = []
+            subscription_finder.tenants.append(tenant)
+        elif tenant:
             subscriptions = 
subscription_finder.find_using_specific_tenant(tenant, credential)
         else:
             subscriptions = 
subscription_finder.find_using_common_tenant(username, credential)
 
         if not subscriptions and not allow_no_subscriptions:
-            raise CLIError("No subscriptions found for {}.".format(username))
+            if skip_subscription_discovery and subscription:
+                raise CLIError(
+                    "The subscription '{}' could not be retrieved for '{}'. "
+                    "Ensure the subscription exists and that you have access 
to it.".format(
+                        subscription, username))
+            if not is_bare_mode:
+                raise CLIError("No subscriptions found for 
{}.".format(username))
 
-        if allow_no_subscriptions:
+        if allow_no_subscriptions or is_bare_mode:
             t_list = [s.tenant_id for s in subscriptions]
             bare_tenants = [t for t in subscription_finder.tenants if t not in 
t_list]
             tenant_accounts = self._build_tenant_level_accounts(bare_tenants)
@@ -216,8 +234,24 @@
 
         consolidated = self._normalize_properties(username, subscriptions,
                                                   is_service_principal, 
bool(use_cert_sn_issuer))
+        self._set_subscriptions(consolidated, 
preferred_subscription=subscription)
+
+        if subscription:
+            matches = [s for s in consolidated
+                       if s[_SUBSCRIPTION_ID].lower() == subscription.lower() 
or
+                       s.get(_SUBSCRIPTION_NAME, '').lower() == 
subscription.lower()]
+            if matches:
+                return deepcopy(matches)
+            if skip_subscription_discovery:
+                # --skip-subscription-discovery + --subscription S, but S is 
inaccessible.
+                # without --allow-no-subscriptions → already errored above
+                # with --allow-no-subscriptions → tenant-level account only 
(we reach here)
+                logger.warning("Subscription '%s' not found. Profile has 
tenant-level account only.",
+                               subscription)
+            else:
+                raise CLIError("Subscription '{}' not found. Check the ID or 
name and try again."
+                               .format(subscription))
 
-        self._set_subscriptions(consolidated)
         return deepcopy(consolidated)
 
     def login_with_managed_identity(self, client_id=None, object_id=None, 
resource_id=None,
@@ -463,7 +497,8 @@
         s.state = 'Enabled'
         return s
 
-    def _set_subscriptions(self, new_subscriptions, merge=True, 
secondary_key_name=None):
+    def _set_subscriptions(self, new_subscriptions, merge=True, 
secondary_key_name=None,
+                           preferred_subscription=None):
 
         def _get_key_name(account, secondary_key_name):
             return (account[_SUBSCRIPTION_ID] if secondary_key_name is None
@@ -489,17 +524,26 @@
         dic.update((_get_key_name(x, secondary_key_name), x) for x in 
new_subscriptions)
         subscriptions = list(dic.values())
         if subscriptions:
-            if active_one:
+            new_active_one = None
+
+            # If a preferred subscription is specified, try to use it as 
default
+            if preferred_subscription:
+                preferred_lower = preferred_subscription.lower()
+                new_active_one = next(
+                    (x for x in new_subscriptions
+                     if x[_SUBSCRIPTION_ID].lower() == preferred_lower or
+                     x.get(_SUBSCRIPTION_NAME, '').lower() == 
preferred_lower), None)
+
+            # Fall back to previously active subscription if still present
+            if not new_active_one and active_one:
                 new_active_one = next(
                     (x for x in new_subscriptions if _match_account(x, 
active_subscription_id, secondary_key_name,
                                                                     
active_secondary_key_val)), None)
 
-                for s in subscriptions:
-                    s[_IS_DEFAULT_SUBSCRIPTION] = False
+            for s in subscriptions:
+                s[_IS_DEFAULT_SUBSCRIPTION] = False
 
-                if not new_active_one:
-                    new_active_one = 
Profile._pick_working_subscription(new_subscriptions)
-            else:
+            if not new_active_one:
                 new_active_one = 
Profile._pick_working_subscription(new_subscriptions)
 
             new_active_one[_IS_DEFAULT_SUBSCRIPTION] = True
@@ -507,6 +551,7 @@
 
             set_cloud_subscription(self.cli_ctx, active_cloud.name, 
default_sub_id)
         self._storage[_SUBSCRIPTIONS] = subscriptions
+        return subscriptions
 
     @staticmethod
     def _pick_working_subscription(subscriptions):
@@ -856,6 +901,23 @@
         self.tenants.append(tenant)
         return all_subscriptions
 
+    def find_specific_subscriptions(self, tenant, credential, 
subscription_ids):
+        """Fetch specific subscriptions by ID using GET /subscriptions/{id}
+        instead of listing all subscriptions.
+        https://learn.microsoft.com/en-us/rest/api/resources/subscriptions/get
+        """
+        client = self._create_subscription_client(credential)
+        all_subscriptions = []
+        for sub_id in subscription_ids:
+            try:
+                s = client.subscriptions.get(sub_id)
+                _attach_token_tenant(s, tenant)
+                all_subscriptions.append(s)
+            except Exception as ex:  # pylint: disable=broad-except
+                logger.warning("Failed to retrieve subscription %s: %s", 
sub_id, ex)
+        self.tenants.append(tenant)
+        return all_subscriptions
+
     def _create_subscription_client(self, credential):
         from azure.cli.core.profiles import ResourceType, get_api_version
         from azure.cli.core.profiles._shared import get_client_class
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/azure/cli/core/aaz/__init__.py 
new/azure_cli_core-2.86.0/azure/cli/core/aaz/__init__.py
--- old/azure_cli_core-2.85.0/azure/cli/core/aaz/__init__.py    2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/aaz/__init__.py    2026-05-01 
02:40:32.000000000 +0200
@@ -21,7 +21,8 @@
     AAZPaginationTokenArgFormat
 from ._base import has_value, AAZValuePatch, AAZUndefined
 from ._command import AAZCommand, AAZWaitCommand, AAZCommandGroup, \
-    register_callback, register_command, register_command_group, 
load_aaz_command_table, link_helper
+    register_callback, register_command, register_command_group, 
load_aaz_command_table, \
+    load_aaz_command_table_args_guided, link_helper
 from ._field_type import AAZIntType, AAZFloatType, AAZStrType, AAZBoolType, 
AAZDictType, AAZFreeFormDictType, \
     AAZListType, AAZObjectType, AAZIdentityObjectType, AAZAnyType
 from ._operation import AAZHttpOperation, AAZJsonInstanceUpdateOperation, 
AAZGenericInstanceUpdateOperation, \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/azure/cli/core/aaz/_command.py 
new/azure_cli_core-2.86.0/azure/cli/core/aaz/_command.py
--- old/azure_cli_core-2.85.0/azure/cli/core/aaz/_command.py    2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/aaz/_command.py    2026-05-01 
02:40:32.000000000 +0200
@@ -387,6 +387,181 @@
 AAZ_PACKAGE_FULL_LOAD_ENV_NAME = 'AZURE_AAZ_FULL_LOAD'
 
 
+def load_aaz_command_table_args_guided(loader, aaz_pkg_name, args):
+    """Args-guided AAZ command tree loader.
+
+    Instead of importing the entire AAZ package tree (all __init__.py files 
which eagerly
+    import all command classes), this function navigates only to the relevant 
subtree based
+    on CLI args. For example, ``az monitor log-analytics workspace create 
--help`` only loads
+    the ``workspace`` sub-package and the ``_create`` module, skipping all 
other commands.
+
+    This requires that AAZ ``__init__.py`` files do NOT contain wildcard 
imports
+    (``from ._create import *`` etc.) -- they should be empty (just the 
license header).
+    """
+    profile_pkg = _get_profile_pkg(aaz_pkg_name, loader.cli_ctx.cloud)
+
+    command_table = {}
+    command_group_table = {}
+    if args is None or os.environ.get(AAZ_PACKAGE_FULL_LOAD_ENV_NAME, 
'False').lower() == 'true':
+        effective_args = None  # fully load
+    else:
+        effective_args = list(args)
+    if profile_pkg is not None:
+        _load_aaz_by_pkg(loader, profile_pkg, effective_args,
+                         command_table, command_group_table)
+
+    for group_name, command_group in command_group_table.items():
+        loader.command_group_table[group_name] = command_group
+    for command_name, command in command_table.items():
+        loader.command_table[command_name] = command
+    return command_table, command_group_table
+
+
+def _try_import_module(relative_name, package):
+    """Try to import a module by relative name, return None on failure."""
+    try:
+        return importlib.import_module(relative_name, package)
+    except ModuleNotFoundError as ex:
+        # Only treat "module not found" for the requested module as a benign 
miss.
+        target_mod_name = f"{package}.{relative_name.lstrip('.')}"
+        if ex.name == target_mod_name:
+            return None
+        # Different module is missing; propagate so the real error surfaces.
+        raise
+    except ImportError:
+        logger.error("Error importing module %r from package %r", 
relative_name, package)
+        raise
+
+
+def _register_from_module(loader, mod, command_table, command_group_table):
+    """Scan a module's namespace for AAZCommand/AAZCommandGroup classes and 
register them."""
+    for value in mod.__dict__.values():
+        if not isinstance(value, type):
+            continue
+        if value.__module__ != mod.__name__:  # skip imported classes
+            continue
+        if issubclass(value, AAZCommandGroup) and value.AZ_NAME:
+            command_group_table[value.AZ_NAME] = value(cli_ctx=loader.cli_ctx)
+        elif issubclass(value, AAZCommand) and value.AZ_NAME:
+            command_table[value.AZ_NAME] = value(loader=loader)
+
+
+def _get_pkg_children(pkg):
+    """List child entries of a package using pkgutil.
+
+    Returns two sets: (file_stems, subdir_names).
+    - file_stems: module-like stems, e.g. {'_create', '_list', '__cmd_group'}
+    - subdir_names: sub-package directory names, e.g. {'namespace', 'eventhub'}
+    """
+    import pkgutil
+    file_stems = set()
+    subdir_names = set()
+
+    pkg_path = getattr(pkg, '__path__', None)
+    if not pkg_path:
+        return file_stems, subdir_names
+
+    for _importer, name, ispkg in pkgutil.iter_modules(pkg_path):
+        if ispkg:
+            if not name.startswith('_'):
+                subdir_names.add(name)
+        else:
+            file_stems.add(name)
+
+    return file_stems, subdir_names
+
+
+def _load_aaz_by_pkg(loader, pkg, args, command_table, command_group_table):
+    """Recursively navigate the AAZ package tree guided by CLI args.
+
+    - args is None           -> full recursive load of all commands under this 
package.
+    - args is empty list     -> args exhausted; load current level's commands 
and sub-group headers.
+    - args has items         -> try to match first arg as a command module or 
sub-package,
+                                recurse with remaining args on match.
+    - no match on first arg  -> load current level's commands and sub-group 
headers.
+    """
+    base_module = pkg.__name__
+    file_stems, subdir_names = _get_pkg_children(pkg)
+
+    if args is not None and args and not args[0].startswith('-'):
+        first_arg = args[0].lower().replace('-', '_')
+
+        # First arg matches a command module (e.g. "create" -> "_create")
+        if f"_{first_arg}" in file_stems:
+            mod = _try_import_module(f"._{first_arg}", base_module)
+            if mod:
+                _register_from_module(loader, mod, command_table, 
command_group_table)
+                return
+
+        # First arg matches a sub-package (command group)
+        if first_arg in subdir_names:
+            sub_module = f"{base_module}.{first_arg}"
+            mod = _try_import_module('.__cmd_group', sub_module)
+            if mod:
+                _register_from_module(loader, mod, command_table, 
command_group_table)
+            sub_pkg = _try_import_module(f'.{first_arg}', base_module)
+            if sub_pkg:
+                _load_aaz_by_pkg(loader, sub_pkg, args[1:], command_table, 
command_group_table)
+                return
+
+    # Load __cmd_group + all command modules at this level
+    mod = _try_import_module('.__cmd_group', base_module)
+    if mod:
+        _register_from_module(loader, mod, command_table, command_group_table)
+
+    for stem in file_stems:
+        if stem.startswith('_') and not stem.startswith('__'):
+            mod = _try_import_module(f'.{stem}', base_module)
+            if mod:
+                _register_from_module(loader, mod, command_table, 
command_group_table)
+
+    for subdir in subdir_names:
+        sub_module = f"{base_module}.{subdir}"
+        if args is None:
+            # Full load -> recurse into every sub-package
+            sub_pkg = _try_import_module(f'.{subdir}', base_module)
+            if sub_pkg:
+                _load_aaz_by_pkg(loader, sub_pkg, None, command_table, 
command_group_table)
+        else:
+            # Args exhausted / not matched -> load sub-group header and the 
first
+            # command so the group is non-empty and the parser creates a 
subparser
+            # for it (required for help output).
+            # TODO: After optimized loading is applied to the whole CLI, 
revisit
+            # this and consider a lighter approach (e.g. parser-level fix) to
+            # avoid importing one command per trimmed sub-group.
+            mod = _try_import_module('.__cmd_group', sub_module)
+            if mod:
+                _register_from_module(loader, mod, command_table, 
command_group_table)
+            sub_pkg = _try_import_module(f'.{subdir}', base_module)
+            if sub_pkg:
+                _load_first_command(loader, sub_pkg, command_table)
+
+
+def _load_first_command(loader, pkg, command_table):
+    """Load the first available command module from a package.
+
+    This ensures the command group is non-empty so the parser creates a 
subparser
+    for it, which is required for it to appear in help output.
+    """
+    file_stems, subdir_names = _get_pkg_children(pkg)
+    base_module = pkg.__name__
+
+    # Try to load a command module at this level first
+    for stem in sorted(file_stems):
+        if stem.startswith('_') and not stem.startswith('__'):
+            mod = _try_import_module(f'.{stem}', base_module)
+            if mod:
+                _register_from_module(loader, mod, command_table, {})
+                return
+
+    # No command at this level, recurse into the first sub-package
+    for subdir in sorted(subdir_names):
+        sub_pkg = _try_import_module(f'.{subdir}', base_module)
+        if sub_pkg:
+            _load_first_command(loader, sub_pkg, command_table)
+            return
+
+
 def load_aaz_command_table(loader, aaz_pkg_name, args):
     """ This function is used in AzCommandsLoader.load_command_table.
     It will load commands in module's aaz package.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/azure/cli/core/azclierror.py 
new/azure_cli_core-2.86.0/azure/cli/core/azclierror.py
--- old/azure_cli_core-2.85.0/azure/cli/core/azclierror.py      2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/azclierror.py      2026-05-01 
02:40:32.000000000 +0200
@@ -33,8 +33,8 @@
         self.recommendations = []
         self.set_recommendation(recommendation)
 
-        # AI recommendations provided by Aladdin service, with tuple form: 
(recommendation, description)
-        self.aladdin_recommendations = []
+        # example recommendations with tuple form: (recommendation, 
description)
+        self.example_recommendations = []
 
         # exception trace for the error
         self.exception_trace = None
@@ -50,11 +50,11 @@
         elif isinstance(recommendation, list):
             self.recommendations.extend(recommendation)
 
-    def set_aladdin_recommendation(self, recommendations):
-        """ Set aladdin recommendations for the error.
+    def set_example_recommendation(self, recommendations):
+        """ Set example recommendations for the error.
         One item should be a tuple with the form: (recommendation, description)
         """
-        self.aladdin_recommendations.extend(recommendations)
+        self.example_recommendations.extend(recommendations)
 
     def set_exception_trace(self, exception_trace):
         self.exception_trace = exception_trace
@@ -80,9 +80,12 @@
             for recommendation in self.recommendations:
                 print(recommendation, file=sys.stderr)
 
-        if self.aladdin_recommendations:
-            print('\nExamples from AI knowledge base:', file=sys.stderr)
-            for recommendation, description in self.aladdin_recommendations:
+        if self.example_recommendations:
+            print(file=sys.stderr)
+            if len(self.example_recommendations) > 1:  # contains help examples
+                print("Examples from command's help:", file=sys.stderr)
+
+            for recommendation, description in self.example_recommendations:
                 print_styled_text(recommendation, file=sys.stderr)
                 print_styled_text(description, file=sys.stderr)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.85.0/azure/cli/core/breaking_change.py 
new/azure_cli_core-2.86.0/azure/cli/core/breaking_change.py
--- old/azure_cli_core-2.85.0/azure/cli/core/breaking_change.py 2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/breaking_change.py 2026-05-01 
02:40:32.000000000 +0200
@@ -15,8 +15,8 @@
 
 logger = get_logger()
 
-NEXT_BREAKING_CHANGE_RELEASE = '2.86.0'
-NEXT_BREAKING_CHANGE_DATE = 'May 2026'
+NEXT_BREAKING_CHANGE_RELEASE = '2.87.0'
+NEXT_BREAKING_CHANGE_DATE = 'June 2026'
 DEFAULT_BREAKING_CHANGE_TAG = '[Breaking Change]'
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/azure/cli/core/cloud.py 
new/azure_cli_core-2.86.0/azure/cli/core/cloud.py
--- old/azure_cli_core-2.85.0/azure/cli/core/cloud.py   2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/cloud.py   2026-05-01 
02:40:32.000000000 +0200
@@ -23,9 +23,6 @@
 # Add names of clouds that don't allow telemetry data collection here such as 
some air-gapped clouds.
 CLOUDS_FORBIDDING_TELEMETRY = ['USSec', 'USNat']
 
-# Add names of clouds that don't allow Aladdin requests for command 
recommendations here
-CLOUDS_FORBIDDING_ALADDIN_REQUEST = ['USSec', 'USNat']
-
 
 class CloudNotRegisteredException(Exception):
     def __init__(self, cloud_name):
@@ -501,7 +498,7 @@
         sql_management='https://management.database.sovcloud-api.fr:8443/',
         batch_resource_id='https://batch.sovcloud-api.fr/',
         gallery='https://gallery.sovcloud-api.fr/',
-        active_directory='https://login.sovcloud-api.fr',
+        active_directory='https://login.sovcloud-identity.fr',
         active_directory_resource_id='https://management.sovcloud-api.fr/',
         active_directory_graph_resource_id='https://graph.svc.sovcloud.fr/',
         microsoft_graph_resource_id='https://graph.svc.sovcloud.fr',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.85.0/azure/cli/core/commandIndex.latest.json 
new/azure_cli_core-2.86.0/azure/cli/core/commandIndex.latest.json
--- old/azure_cli_core-2.85.0/azure/cli/core/commandIndex.latest.json   
2026-03-31 09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/commandIndex.latest.json   
2026-05-01 02:40:32.000000000 +0200
@@ -1,5 +1,5 @@
 {
-  "version": "2.85.0",
+  "version": "2.86.0",
   "cloudProfile": "latest",
   "commandIndex": {
     "account": [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.85.0/azure/cli/core/command_recommender.py 
new/azure_cli_core-2.86.0/azure/cli/core/command_recommender.py
--- old/azure_cli_core-2.85.0/azure/cli/core/command_recommender.py     
2026-03-31 09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/command_recommender.py     
2026-05-01 02:40:32.000000000 +0200
@@ -3,8 +3,6 @@
 # Licensed under the MIT License. See License.txt in the project root for 
license information.
 # 
--------------------------------------------------------------------------------------------
 
-from enum import Enum
-
 from azure.cli.core import telemetry
 from knack.log import get_logger
 
@@ -12,80 +10,9 @@
 logger = get_logger(__name__)
 
 
-class AladdinUserFaultType(Enum):
-    """Define the userfault types required by aladdin service
-    to get the command recommendations"""
-
-    ExpectedArgument = 'ExpectedArgument'
-    UnrecognizedArguments = 'UnrecognizedArguments'
-    ValidationError = 'ValidationError'
-    UnknownSubcommand = 'UnknownSubcommand'
-    MissingRequiredParameters = 'MissingRequiredParameters'
-    MissingRequiredSubcommand = 'MissingRequiredSubcommand'
-    StorageAccountNotFound = 'StorageAccountNotFound'
-    Unknown = 'Unknown'
-    InvalidJMESPathQuery = 'InvalidJMESPathQuery'
-    InvalidOutputType = 'InvalidOutputType'
-    InvalidParameterValue = 'InvalidParameterValue'
-    UnableToParseCommandInput = 'UnableToParseCommandInput'
-    ResourceGroupNotFound = 'ResourceGroupNotFound'
-    InvalidDateTimeArgumentValue = 'InvalidDateTimeArgumentValue'
-    InvalidResourceGroupName = 'InvalidResourceGroupName'
-    AzureResourceNotFound = 'AzureResourceNotFound'
-    InvalidAccountName = 'InvalidAccountName'
-
-
-def get_error_type(error_msg):
-    """The the error type of the failed command from the error message.
-    The error types are only consumed by aladdin service for better 
recommendations.
-    """
-
-    error_type = AladdinUserFaultType.Unknown
-    if not error_msg:
-        return error_type.value
-
-    error_msg = error_msg.lower()
-    if 'unrecognized' in error_msg:
-        error_type = AladdinUserFaultType.UnrecognizedArguments
-    elif 'expected one argument' in error_msg or 'expected at least one 
argument' in error_msg \
-            or 'value required' in error_msg:
-        error_type = AladdinUserFaultType.ExpectedArgument
-    elif 'misspelled' in error_msg:
-        error_type = AladdinUserFaultType.UnknownSubcommand
-    elif 'arguments are required' in error_msg or 'argument required' in 
error_msg:
-        error_type = AladdinUserFaultType.MissingRequiredParameters
-        if '_subcommand' in error_msg:
-            error_type = AladdinUserFaultType.MissingRequiredSubcommand
-        elif '_command_package' in error_msg:
-            error_type = AladdinUserFaultType.UnableToParseCommandInput
-    elif 'not found' in error_msg or 'could not be found' in error_msg \
-            or 'resource not found' in error_msg:
-        error_type = AladdinUserFaultType.AzureResourceNotFound
-        if 'storage_account' in error_msg or 'storage account' in error_msg:
-            error_type = AladdinUserFaultType.StorageAccountNotFound
-        elif 'resource_group' in error_msg or 'resource group' in error_msg:
-            error_type = AladdinUserFaultType.ResourceGroupNotFound
-    elif 'pattern' in error_msg or 'is not a valid value' in error_msg or 
'invalid' in error_msg:
-        error_type = AladdinUserFaultType.InvalidParameterValue
-        if 'jmespath_type' in error_msg:
-            error_type = AladdinUserFaultType.InvalidJMESPathQuery
-        elif 'datetime_type' in error_msg:
-            error_type = AladdinUserFaultType.InvalidDateTimeArgumentValue
-        elif '--output' in error_msg:
-            error_type = AladdinUserFaultType.InvalidOutputType
-        elif 'resource_group' in error_msg:
-            error_type = AladdinUserFaultType.InvalidResourceGroupName
-        elif 'storage_account' in error_msg:
-            error_type = AladdinUserFaultType.InvalidAccountName
-    elif "validation error" in error_msg:
-        error_type = AladdinUserFaultType.ValidationError
-
-    return error_type.value
-
-
 class CommandRecommender:  # pylint: disable=too-few-public-methods
     """Recommend a command for user when user's command fails.
-    It combines Aladdin recommendations and examples in help files."""
+    It uses examples from help files to provide recommendations."""
 
     def __init__(self, command, parameters, extension, error_msg, cli_ctx):
         """
@@ -107,8 +34,6 @@
         self.cli_ctx = cli_ctx
         # the item is a dict with the form {'command': #, 'description': #}
         self.help_examples = []
-        # the item is a dict with the form {'command': #, 'description': #, 
'link': #}
-        self.aladdin_recommendations = []
 
     def set_help_examples(self, examples):
         """Set help examples.
@@ -119,89 +44,10 @@
 
         self.help_examples.extend(examples)
 
-    def _set_aladdin_recommendations(self):  # pylint: disable=too-many-locals
-        """Set Aladdin recommendations.
-        Call the API, parse the response and set aladdin_recommendations.
-        """
-
-        import hashlib
-        import json
-        import requests
-        from requests import RequestException
-        from http import HTTPStatus
-        from azure.cli.core import __version__ as version
-
-        api_url = 'https://app.aladdin.microsoft.com/api/v1.0/suggestions'
-        correlation_id = telemetry._session.correlation_id  # pylint: 
disable=protected-access
-        subscription_id = telemetry._get_azure_subscription_id()  # pylint: 
disable=protected-access
-        event_id = telemetry._session.event_id  # pylint: 
disable=protected-access
-        # Used for DDOS protection and rate limiting
-        user_id = telemetry._get_user_azure_id()  # pylint: 
disable=protected-access
-        hashed_user_id = hashlib.sha256(user_id.encode('utf-8')).hexdigest()
-
-        headers = {
-            'Content-Type': 'application/json',
-            'X-UserId': hashed_user_id
-        }
-        context = {
-            'versionNumber': version,
-            'errorType': get_error_type(self.error_msg)
-        }
-
-        if telemetry.is_telemetry_enabled():
-            if correlation_id:
-                context['correlationId'] = correlation_id
-            if subscription_id:
-                context['subscriptionId'] = subscription_id
-            if event_id:
-                context['eventId'] = event_id
-
-        parameters = self._normalize_parameters(self.parameters)
-        parameters = [item for item in set(parameters) if item not in 
['--debug', '--verbose', '--only-show-errors']]
-        query = {
-            "command": self.command,
-            "parameters": ','.join(parameters)
-        }
-
-        response = None
-        try:
-            response = requests.get(
-                api_url,
-                params={
-                    'query': json.dumps(query),
-                    'clientType': 'AzureCli',
-                    'context': json.dumps(context)
-                },
-                headers=headers,
-                timeout=1)
-            telemetry.set_debug_info('AladdinResponseTime', 
response.elapsed.total_seconds())
-
-        except RequestException as ex:
-            logger.debug('Recommendation requests.get() exception: %s', ex)
-            telemetry.set_debug_info('AladdinException', ex.__class__.__name__)
-
-        recommendations = []
-        if response and response.status_code == HTTPStatus.OK:
-            for result in response.json():
-                # parse the response to get the raw command
-                raw_command = 'az {} '.format(result['command'])
-                for parameter, placeholder in 
zip(result['parameters'].split(','), result['placeholders'].split('♠')):
-                    raw_command += '{} {}{}'.format(parameter, placeholder, ' 
' if placeholder else '')
-
-                # format the recommendation
-                recommendation = {
-                    'command': raw_command.strip(),
-                    'description': result['description'],
-                    'link': result['link']
-                }
-                recommendations.append(recommendation)
-
-        self.aladdin_recommendations.extend(recommendations)
-
     def provide_recommendations(self):
         """Provide recommendations when a command fails.
 
-        The recommendations are either from Aladdin service or CLI help 
examples,
+        The recommendations are from CLI help examples,
         which include both commands and reference links along with their 
descriptions.
 
         :return: The decorated recommendations
@@ -273,14 +119,7 @@
         if self.cli_ctx and self.cli_ctx.config.get('core', 
'error_recommendation', 'on').upper() == 'OFF':
             return []
 
-        # get recommendations from Aladdin service
-        if not self._disable_aladdin_service():
-            self._set_aladdin_recommendations()
-
-        # recommendations are either all from Aladdin or all from help examples
-        recommendations = self.aladdin_recommendations
-        if not recommendations:
-            recommendations = self.help_examples
+        recommendations = self.help_examples
 
         # sort the recommendations by parameter matching, get the top 3 
recommended commands
         recommendations = sort_recommendations(recommendations)[:3]
@@ -305,8 +144,6 @@
 
         # add reference link as a recommendation
         decorated_link = [(Style.HYPERLINK, OVERVIEW_REFERENCE)]
-        if self.aladdin_recommendations:
-            decorated_link = [(Style.HYPERLINK, 
self.aladdin_recommendations[0]['link'])]
 
         decorated_description = [(Style.SECONDARY, 'Read more about the 
command in reference docs')]
         decorated_recommendations.append((decorated_link, 
decorated_description))
@@ -319,49 +156,11 @@
     def _set_recommended_command_to_telemetry(self, raw_commands):
         """Set the recommended commands to Telemetry
 
-        Aladdin recommended commands and commands from CLI help examples are
-        set to different properties in Telemetry.
-
         :param raw_commands: The recommended raw commands
         :type raw_commands: list
         """
 
-        if self.aladdin_recommendations:
-            telemetry.set_debug_info('AladdinRecommendCommand', 
';'.join(raw_commands))
-        else:
-            telemetry.set_debug_info('ExampleRecommendCommand', 
';'.join(raw_commands))
-
-    def _disable_aladdin_service(self):
-        """Decide whether to disable aladdin request when a command fails.
-
-        The possible cases to disable it are:
-            1. CLI context is missing
-            2. In air-gapped clouds
-            3. In testing environments
-            4. In autocomplete mode
-
-        :return: whether Aladdin service need to be disabled or not
-        :type: bool
-        """
-
-        from azure.cli.core.cloud import CLOUDS_FORBIDDING_ALADDIN_REQUEST
-
-        # CLI is not started well
-        if not self.cli_ctx or not self.cli_ctx.cloud:
-            return True
-
-        # for air-gapped clouds
-        if self.cli_ctx.cloud.name in CLOUDS_FORBIDDING_ALADDIN_REQUEST:
-            return True
-
-        # for testing environments
-        if self.cli_ctx.__class__.__name__ == 'DummyCli':
-            return True
-
-        if self.cli_ctx.data['completer_active']:
-            return True
-
-        return False
+        telemetry.set_debug_info('ExampleRecommendCommand', 
';'.join(raw_commands))
 
     def _normalize_parameters(self, args):
         """Normalize a parameter list.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.85.0/azure/cli/core/helpIndex.latest.json 
new/azure_cli_core-2.86.0/azure/cli/core/helpIndex.latest.json
--- old/azure_cli_core-2.85.0/azure/cli/core/helpIndex.latest.json      
2026-03-31 09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/helpIndex.latest.json      
2026-05-01 02:40:32.000000000 +0200
@@ -1,5 +1,5 @@
 {
-  "version": "2.85.0",
+  "version": "2.86.0",
   "cloudProfile": "latest",
   "helpIndex": {
     "groups": {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/azure/cli/core/parser.py 
new/azure_cli_core-2.86.0/azure/cli/core/parser.py
--- old/azure_cli_core-2.85.0/azure/cli/core/parser.py  2026-03-31 
09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure/cli/core/parser.py  2026-05-01 
02:40:32.000000000 +0200
@@ -169,7 +169,7 @@
             from azure.cli.core.util import QUERY_REFERENCE
             az_error.set_recommendation(QUERY_REFERENCE)
         elif recommendations:
-            az_error.set_aladdin_recommendation(recommendations)
+            az_error.set_example_recommendation(recommendations)
         az_error.print_error()
         az_error.send_telemetry()
         self.exit(2)
@@ -324,7 +324,7 @@
             
recommender.set_help_examples(self.get_examples(command_name_inferred))
             recommendations = recommender.provide_recommendations()
             if recommendations:
-                az_error.set_aladdin_recommendation(recommendations)
+                az_error.set_example_recommendation(recommendations)
 
             # remind user to check extensions if we can not find a command to 
recommend
             if isinstance(az_error, CommandNotFoundError) \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.85.0/azure_cli_core.egg-info/PKG-INFO 
new/azure_cli_core-2.86.0/azure_cli_core.egg-info/PKG-INFO
--- old/azure_cli_core-2.85.0/azure_cli_core.egg-info/PKG-INFO  2026-03-31 
09:19:44.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure_cli_core.egg-info/PKG-INFO  2026-05-01 
02:41:23.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: azure-cli-core
-Version: 2.85.0
+Version: 2.86.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -28,7 +28,7 @@
 Requires-Dist: jmespath
 Requires-Dist: knack~=0.11.0
 Requires-Dist: microsoft-security-utilities-secret-masker~=1.0.0b4
-Requires-Dist: msal-extensions==1.2.0
+Requires-Dist: msal-extensions==1.3.1
 Requires-Dist: msal[broker]==1.35.1; sys_platform == "win32"
 Requires-Dist: msal==1.35.1; sys_platform != "win32"
 Requires-Dist: packaging>=20.9
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.85.0/azure_cli_core.egg-info/requires.txt 
new/azure_cli_core-2.86.0/azure_cli_core.egg-info/requires.txt
--- old/azure_cli_core-2.85.0/azure_cli_core.egg-info/requires.txt      
2026-03-31 09:19:44.000000000 +0200
+++ new/azure_cli_core-2.86.0/azure_cli_core.egg-info/requires.txt      
2026-05-01 02:41:23.000000000 +0200
@@ -7,7 +7,7 @@
 jmespath
 knack~=0.11.0
 microsoft-security-utilities-secret-masker~=1.0.0b4
-msal-extensions==1.2.0
+msal-extensions==1.3.1
 packaging>=20.9
 pkginfo>=1.5.0.1
 PyJWT>=2.1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.85.0/setup.py 
new/azure_cli_core-2.86.0/setup.py
--- old/azure_cli_core-2.85.0/setup.py  2026-03-31 09:18:57.000000000 +0200
+++ new/azure_cli_core-2.86.0/setup.py  2026-05-01 02:40:32.000000000 +0200
@@ -8,7 +8,7 @@
 from codecs import open
 from setuptools import setup, find_packages
 
-VERSION = "2.85.0"
+VERSION = "2.86.0"
 
 # If we have source, validate that our version numbers match
 # This should prevent uploading releases with mismatched versions.
@@ -54,7 +54,7 @@
     'jmespath',
     'knack~=0.11.0',
     'microsoft-security-utilities-secret-masker~=1.0.0b4',
-    'msal-extensions==1.2.0',
+    'msal-extensions==1.3.1',
     'msal[broker]==1.35.1; sys_platform == "win32"',
     'msal==1.35.1; sys_platform != "win32"',
     'packaging>=20.9',

Reply via email to