-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
Describe the bug
When using az resource commands with the --ids parameter, the commands incorrectly use the default subscription (or fail if none is set) to determine the API version, instead of using the subscription specified in the resource ID. This affects all az resource commands that support the --ids parameter.
Two manifestations of the same bug:
-
When default subscription differs from resource's subscription:
The command queries the wrong subscription's provider information for API version determination. This can cause failures or incorrect behavior when the resource uses a preview API version that's only available in its subscription (due to feature flags or allowlisting), but not in the default subscription. -
When no default subscription is set:
The command fails completely with "No subscription found. Run 'az account set' to select a subscription" even though the resource ID contains all necessary information including the subscription.
Affected commands:
az resource showaz resource createaz resource deleteaz resource updateaz resource patchaz resource tagaz resource invoke-action
Related command
az resource show
az resource create
az resource delete
az resource update
az resource patch
az resource tag
az resource invoke-action
Errors
Scenario 1: No default subscription set
No subscription found. Run 'az account set' to select a subscription.
Scenario 2: Feature-flagged API version
When the resource uses a preview API version only available in its subscription:
The resource type 'resourceType' could not be found in the namespace 'Microsoft.Provider' for api version '...'
Or the command may silently use an incorrect/incompatible API version.
Issue script & Debug output
# Scenario 1: No default subscription set
# (Can occur if user hasn't run 'az account set' or manually modified azureProfile.json)
az resource show --ids /subscriptions/sub-B/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/myVM
# Error: No subscription found. Run 'az account set' to select a subscription.
# Expected: Should work using sub-B from the resource ID
# Scenario 2: Default subscription differs from resource subscription
az account set --subscription sub-A
az resource show --ids /subscriptions/sub-B/resourceGroups/myRG/providers/Microsoft.Compute/virtualMachines/myVM
# Queries sub-A's provider information instead of sub-B's
# May fail or use wrong API version if sub-B has different available API versions (e.g., feature-flagged APIs)Expected behavior
When az resource commands are called with a resource ID via --ids, they should:
- Parse the subscription ID from the resource ID
- Create a resource client for that specific subscription
- Query that subscription's provider information to determine the correct API version
- Execute the command using the correct subscription context
The commands should work correctly regardless of whether:
- A default subscription is set or not
- The resource's subscription matches the default subscription
The resource ID contains all necessary information, so no default subscription should be required.
Environment Summary
azure-cli 2.80.0
core 2.80.0
telemetry 1.1.0
Dependencies:
msal 1.34.0b1
azure-mgmt-resource 23.3.0
Python location '/opt/az/bin/python3'
Config directory '/tmp/tmp.DbT0g2h54k'
Extensions directory '/tmp/tmp.DbT0g2h54k/cliextensions'
Python (Linux) 3.13.9 (main, Nov 11 2025, 12:31:34) [GCC 11.4.0]
Legal docs and information: aka.ms/AzureCliLegal
Your CLI is up-to-date.
Additional context
Root Cause
File: src/azure-cli/azure/cli/command_modules/resource/custom.py
The bug occurs in two places:
-
_ResourceUtils.__init__(line ~3776): Creates the resource client factory without using the subscription from the resource ID:self.rcf = rcf or _resource_client_factory(cli_ctx) # Uses default subscription
-
_resolve_api_version_by_id(line ~4030): Parses the subscription from the resource ID but doesn't use it to query provider information:parts = parse_resource_id(resource_id) # Extracts subscription # ... but then uses rcf created with default subscription: return _ResourceUtils.resolve_api_version(rcf, namespace, parent, resource_type, ...)
-
resolve_api_version(line ~4007): Queries the provider using whatever subscription the rcf was created with:provider = rcf.providers.get(resource_provider_namespace) # Wrong subscription!
Suggested Fix
1. Update _resource_client_factory to accept subscription_id parameter:
File: src/azure-cli/azure/cli/command_modules/resource/_client_factory.py
def _resource_client_factory(cli_ctx, subscription_id=None, **_):
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.cli.core.profiles import ResourceType
return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES,
subscription_id=subscription_id)2. Update _ResourceUtils.__init__ to use subscription from resource ID:
File: src/azure-cli/azure/cli/command_modules/resource/custom.py
self.rcf = rcf
if not self.rcf:
# If we have a resource_id, extract the subscription and use it for the client factory
subscription_id_for_client = None
if resource_id:
parsed_resource_id = parse_resource_id(resource_id)
subscription_id_for_client = parsed_resource_id.get('subscription')
self.rcf = _resource_client_factory(cli_ctx, subscription_id=subscription_id_for_client)3. Update _resolve_api_version_by_id to handle subscription comparison:
@staticmethod
def _resolve_api_version_by_id(rcf, resource_id, latest_include_preview=False, cli_ctx=None):
parts = parse_resource_id(resource_id)
# ... existing parsing logic ...
# Use the subscription from the resource ID if different from default
resource_subscription = parts.get('subscription')
if resource_subscription and cli_ctx:
try:
default_subscription = get_subscription_id(cli_ctx)
except Exception: # pylint: disable=broad-except
# No default subscription set
default_subscription = None
if resource_subscription != default_subscription:
rcf = _resource_client_factory(cli_ctx, subscription_id=resource_subscription)
return _ResourceUtils.resolve_api_version(rcf, namespace, parent, resource_type,
latest_include_preview=latest_include_preview)4. Update call site to pass cli_ctx:
api_version = _ResourceUtils._resolve_api_version_by_id(self.rcf, resource_id,
latest_include_preview=latest_include_preview,
cli_ctx=cli_ctx)