Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1aa2ffe
[refractor] - Migrated 'az vm identity show' command
william051200 Dec 15, 2025
d1ca1d5
[Refractor] - Refractored show_vm_dentity function and migrated get_v…
william051200 Dec 16, 2025
161a76f
[Refractor] - Migrated assign_vm_identity function
william051200 Dec 16, 2025
c7e4ef1
[Refractor] - Preserve old function to avoid breaking change, updated…
william051200 Dec 16, 2025
3e9b52d
[Refractor] - Refractored assign_identity_helper function
william051200 Dec 16, 2025
ad87d37
[Refractor] - Edit so the response is same as original when identity …
william051200 Dec 16, 2025
d0f907c
[refractor] - Added handling to assign_vm_identity function
william051200 Dec 18, 2025
ee6d900
[Refractor] - Migrated remove_vm_identity function
william051200 Dec 19, 2025
50d505d
[Refractor] - Edited function name
william051200 Dec 19, 2025
22bf942
[style] - Update code styling
william051200 Dec 19, 2025
1c9997d
[style] - Update code styling
william051200 Dec 21, 2025
b314bd7
[test] - fixed test_vm_explicit_msi test case
william051200 Dec 23, 2025
be8b5b6
[style] - Update code styling
william051200 Dec 23, 2025
4082bb3
[style] - Update code styling
william051200 Dec 23, 2025
e420114
[style] - Update code styling
william051200 Dec 23, 2025
12bc6e0
[test] - Added handling to vm create and vmss create command
william051200 Dec 23, 2025
d99a84f
[Refractor] - Resolve copilot suggestion
william051200 Dec 23, 2025
31fbfa5
Add recording for test_vm_msi
ReaNAiveD Dec 23, 2025
045372b
[Fix] - Fixed import show function
william051200 Dec 23, 2025
9d93ff8
[Test] - Re-record test case
william051200 Dec 23, 2025
0edb99d
[style] - Update code styling
william051200 Dec 23, 2025
4b36898
[Fix] - Fixed import patch function
william051200 Dec 23, 2025
7936f69
[Fix] - Added handling when assigning vm identities
william051200 Dec 23, 2025
d70670d
[Fix] - Fixed schema output issue
william051200 Dec 24, 2025
becdd56
[Test] - Fixed test case failure and re-record test case
william051200 Dec 24, 2025
f543fb2
[Test] - Re-record test case
william051200 Dec 26, 2025
d3c2862
Removed unused parameter in commands.py
william051200 Dec 30, 2025
4ff821f
Refractor assign_vm_identity in custom.py
william051200 Dec 31, 2025
80edfc1
Refractor code
william051200 Dec 31, 2025
5ebd103
Fix refractored code bug
william051200 Dec 31, 2025
624b244
Removed redundant lines
william051200 Dec 31, 2025
2d9b71c
Removed redundant import
william051200 Dec 31, 2025
4223871
Reset test cases and recordings
william051200 Jan 6, 2026
eb37208
Edit test recording
william051200 Jan 6, 2026
4f79ae1
reset test recording
william051200 Jan 6, 2026
ae7a11c
reset test recording
william051200 Jan 6, 2026
b1736f5
Updated test case
william051200 Jan 7, 2026
9118ac8
Update vm/vm_utils and core/commands/arm
william051200 Jan 7, 2026
fb76319
Updated test recording
william051200 Jan 7, 2026
4d957f5
Reset test_vm_explicit_msi test case and recording
william051200 Jan 7, 2026
0ec7875
Updated test_vm_explicit_msi test case
william051200 Jan 7, 2026
332cd85
Remove unused import
william051200 Jan 7, 2026
673917e
Re-record test case
william051200 Jan 12, 2026
29a399e
Updated test case and re-record test case
william051200 Jan 12, 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
Copy Markdown
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
Copy Markdown
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