Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b86c5b2
[refractor] - Migrated 'az vm identity show' command
william051200 Dec 15, 2025
be83580
[Refractor] - Refractored show_vm_dentity function and migrated get_v…
william051200 Dec 16, 2025
22901c0
[Refractor] - Migrated assign_vm_identity function
william051200 Dec 16, 2025
d0aca8d
[Refractor] - Preserve old function to avoid breaking change, updated…
william051200 Dec 16, 2025
30f59be
[Refractor] - Refractored assign_identity_helper function
william051200 Dec 16, 2025
cd88db3
[Refractor] - Edit so the response is same as original when identity …
william051200 Dec 16, 2025
aeb7aab
[refractor] - Added handling to assign_vm_identity function
william051200 Dec 18, 2025
f4bb17e
[Refractor] - Migrated remove_vm_identity function
william051200 Dec 19, 2025
95b1ebe
[Refractor] - Edited function name
william051200 Dec 19, 2025
240fbc5
[style] - Update code styling
william051200 Dec 19, 2025
23714e9
[style] - Update code styling
william051200 Dec 21, 2025
a7791f1
[test] - fixed test_vm_explicit_msi test case
william051200 Dec 23, 2025
0000e4a
[style] - Update code styling
william051200 Dec 23, 2025
d8c4cca
[style] - Update code styling
william051200 Dec 23, 2025
e79f11a
[style] - Update code styling
william051200 Dec 23, 2025
19ca657
[test] - Added handling to vm create and vmss create command
william051200 Dec 23, 2025
2a18517
[Refractor] - Resolve copilot suggestion
william051200 Dec 23, 2025
42cd619
Add recording for test_vm_msi
ReaNAiveD Dec 23, 2025
9d7c786
[Fix] - Fixed import show function
william051200 Dec 23, 2025
a5e9d85
[Test] - Re-record test case
william051200 Dec 23, 2025
ca27b82
[style] - Update code styling
william051200 Dec 23, 2025
67185e0
[Fix] - Fixed import patch function
william051200 Dec 23, 2025
db62d98
[Fix] - Added handling when assigning vm identities
william051200 Dec 23, 2025
c0e2e4c
[Fix] - Fixed schema output issue
william051200 Dec 24, 2025
d70e35b
[Test] - Fixed test case failure and re-record test case
william051200 Dec 24, 2025
d33499e
[Test] - Re-record test case
william051200 Dec 26, 2025
3077fa6
Removed unused parameter in commands.py
william051200 Dec 30, 2025
7055f81
Refractor assign_vm_identity in custom.py
william051200 Dec 31, 2025
35e49c4
Refractor code
william051200 Dec 31, 2025
0866581
Fix refractored code bug
william051200 Dec 31, 2025
ebb360c
Removed redundant lines
william051200 Dec 31, 2025
79e595a
Removed redundant import
william051200 Dec 31, 2025
a4c47ef
Reset test cases and recordings
william051200 Jan 6, 2026
5614d4e
Edit test recording
william051200 Jan 6, 2026
3ad006b
reset test recording
william051200 Jan 6, 2026
158134f
reset test recording
william051200 Jan 6, 2026
4f944c4
Updated test case
william051200 Jan 7, 2026
bb53ae2
Update vm/vm_utils and core/commands/arm
william051200 Jan 7, 2026
9119b6f
Updated test recording
william051200 Jan 7, 2026
8eacff2
Reset test_vm_explicit_msi test case and recording
william051200 Jan 7, 2026
7907d99
Updated test_vm_explicit_msi test case
william051200 Jan 7, 2026
98d1c6b
Remove unused import
william051200 Jan 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 33 additions & 29 deletions src/azure-cli-core/azure/cli/core/commands/arm.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,46 +759,50 @@ def _find_property(instance, path):


def assign_identity(cli_ctx, getter, setter, identity_role=None, identity_scope=None):
import time
from azure.core.exceptions import HttpResponseError

# get
resource = getter()
resource = setter(resource)

# create role assignment:
if identity_scope:
principal_id = resource.identity.principal_id
create_role_assignment(cli_ctx, principal_id, identity_role, identity_scope)

identity_role_id = resolve_role_id(cli_ctx, identity_role, identity_scope)
assignments_client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION).role_assignments
RoleAssignmentCreateParameters = get_sdk(cli_ctx, ResourceType.MGMT_AUTHORIZATION,
'RoleAssignmentCreateParameters', mod='models',
operation_group='role_assignments')
parameters = RoleAssignmentCreateParameters(role_definition_id=identity_role_id, principal_id=principal_id,
principal_type=None)

logger.info("Creating an assignment with a role '%s' on the scope of '%s'", identity_role_id, identity_scope)
retry_times = 36
assignment_name = _gen_guid()
for retry_time in range(0, retry_times):
try:
assignments_client.create(scope=identity_scope, role_assignment_name=assignment_name,
parameters=parameters)
break
except HttpResponseError as ex:
if ex.error.code == 'RoleAssignmentExists':
logger.info('Role assignment already exists')
break
if retry_time < retry_times and ' does not exist in the directory ' in ex.message:
time.sleep(5)
logger.warning('Retrying role assignment creation: %s/%s', retry_time + 1,
retry_times)
continue
raise
return resource


def create_role_assignment(cli_ctx, principal_id, identity_role=None, identity_scope=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @zhoxing-ms could you please help review the changes to this file?
It simply separates the logic for calling assignments_client.create() from the assign_identity() function, since we also need to invoke assignments_client.create() in the vm module

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

import time
from azure.core.exceptions import HttpResponseError

identity_role_id = resolve_role_id(cli_ctx, identity_role, identity_scope)
assignments_client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION).role_assignments
RoleAssignmentCreateParameters = get_sdk(cli_ctx, ResourceType.MGMT_AUTHORIZATION,
'RoleAssignmentCreateParameters', mod='models',
operation_group='role_assignments')
parameters = RoleAssignmentCreateParameters(role_definition_id=identity_role_id, principal_id=principal_id,
principal_type=None)

logger.info("Creating an assignment with a role '%s' on the scope of '%s'", identity_role_id, identity_scope)
retry_times = 36
assignment_name = _gen_guid()
for retry_time in range(0, retry_times):
try:
assignments_client.create(scope=identity_scope, role_assignment_name=assignment_name,
parameters=parameters)
break
except HttpResponseError as ex:
if ex.error.code == 'RoleAssignmentExists':
logger.info('Role assignment already exists')
break
if retry_time < retry_times and ' does not exist in the directory ' in ex.message:
time.sleep(5)
logger.warning('Retrying role assignment creation: %s/%s', retry_time + 1,
retry_times)
continue
raise


def resolve_role_id(cli_ctx, role, scope):
import uuid
client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION).role_definitions
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __init__(self, name_prefix=sqlvm_name_prefix, location='westus',
def create_resource(self, name, **kwargs):
group = self._get_resource_group(**kwargs)
template = ('az vm create -l {} -g {} -n {} --admin-username {} --admin-password {} --image {}'
' --size Standard_DS2_v2 --nsg-rule NONE')
' --size Standard_B2ms --nsg-rule NONE')
execute(DummyCli(), template.format(self.location, group, name, self.vm_user, self.vm_password, self.image))
return {self.parameter_name: name}

Expand Down
37 changes: 30 additions & 7 deletions src/azure-cli/azure/cli/command_modules/vm/_vm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
import os
import re
import importlib
from enum import Enum

from urllib.parse import urlparse

from azure.cli.core.commands.arm import ArmTemplateBuilder
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.cli.core.profiles import ResourceType

from knack.log import get_logger
from knack.util import CLIError
Expand All @@ -32,7 +35,7 @@ def get_target_network_api(cli_ctx):
if cli_ctx.cloud.profile == 'latest':
version = '2022-01-01'
else:
from azure.cli.core.profiles import get_api_version, ResourceType
from azure.cli.core.profiles import get_api_version
version = get_api_version(cli_ctx, ResourceType.MGMT_NETWORK)
return version

Expand All @@ -46,8 +49,6 @@ def read_content_if_is_file(string_or_file):


def _resolve_api_version(cli_ctx, provider_namespace, resource_type, parent_path):
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.cli.core.profiles import ResourceType
client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES)
provider = client.providers.get(provider_namespace)

Expand Down Expand Up @@ -75,10 +76,8 @@ def log_pprint_template(template):
def check_existence(cli_ctx, value, resource_group, provider_namespace, resource_type,
parent_name=None, parent_type=None, static_version=None):
# check for name or ID and set the type flags
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.core.exceptions import HttpResponseError
from azure.mgmt.core.tools import parse_resource_id
from azure.cli.core.profiles import ResourceType
id_parts = parse_resource_id(value)
resource_client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES,
subscription_id=id_parts.get('subscription', None)).resources
Expand Down Expand Up @@ -414,8 +413,6 @@ def _update(model, lun, value):


def get_storage_blob_uri(cli_ctx, storage):
from azure.cli.core.profiles._shared import ResourceType
from azure.cli.core.commands.client_factory import get_mgmt_service_client
if urlparse(storage).scheme:
storage_uri = storage
else:
Expand Down Expand Up @@ -757,3 +754,29 @@ def _open(filename, mode):
f.write(public_bytes)

return public_bytes.decode()


def _gen_guid():
import uuid
return uuid.uuid4()


def assign_identity(cli_ctx, getter, setter, identity_role=None, identity_scope=None):
from azure.cli.core.commands.arm import create_role_assignment

# get
resource = getter()
resource = setter(resource)

# create role assignment:
if identity_scope:
principal_id = resource.get('identity', {}).get('principalId') or resource.get('identity', {}).get('principal_id')
create_role_assignment(cli_ctx, principal_id, identity_role, identity_scope)
return resource


class IdentityType(Enum):
SYSTEM_ASSIGNED = 'SystemAssigned'
USER_ASSIGNED = 'UserAssigned'
SYSTEM_ASSIGNED_USER_ASSIGNED = 'SystemAssigned, UserAssigned'
NONE = 'None'
9 changes: 5 additions & 4 deletions src/azure-cli/azure/cli/command_modules/vm/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,12 @@ def load_command_table(self, _):
from .operations.snapshot import SnapshotUpdate
self.command_table['snapshot update'] = SnapshotUpdate(loader=self)

with self.command_group('vm', compute_vm_sdk) as g:
g.custom_command('identity assign', 'assign_vm_identity', validator=process_assign_identity_namespace)
g.custom_command('identity remove', 'remove_vm_identity', validator=process_remove_identity_namespace, min_api='2017-12-01')
g.custom_show_command('identity show', 'show_vm_identity')
with self.command_group('vm identity') as g:
g.custom_command('assign', 'assign_vm_identity', validator=process_assign_identity_namespace)
g.custom_command('remove', 'remove_vm_identity', validator=process_remove_identity_namespace, min_api='2017-12-01')
g.custom_show_command('show', 'show_vm_identity')

with self.command_group('vm', compute_vm_sdk) as g:
g.custom_command('application set', 'set_vm_applications', validator=process_set_applications_namespace, min_api='2021-07-01')
g.custom_command('application list', 'list_vm_applications', min_api='2021-07-01')

Expand Down
Loading
Loading