Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Compute] az disk-encryption-set: Add parameter --federated-client-id to access key vault in a different tenant and new subgroup to support managed identities #22966

Merged
merged 16 commits into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
59 changes: 59 additions & 0 deletions src/azure-cli/azure/cli/command_modules/vm/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@
examples:
- name: Create a disk encryption set.
text: az disk-encryption-set create --resource-group MyResourceGroup --name MyDiskEncryptionSet --key-url MyKey --source-vault MyVault
- name: Create a disk encryption set with a system assigned identity.
text: az disk-encryption-set create --resource-group MyResourceGroup --name MyDiskEncryptionSet --key-url MyKey --source-vault MyVault --mi-system-assigned
- name: Create a disk encryption set with a user assigned identity.
text: az disk-encryption-set create --resource-group MyResourceGroup --name MyDiskEncryptionSet --key-url MyKey --source-vault MyVault --mi-user-assigned myAssignedId
- name: Create a disk encryption set with system assigned identity and a user assigned identity.
text: az disk-encryption-set create --resource-group MyResourceGroup --name MyDiskEncryptionSet --key-url MyKey --source-vault MyVault --mi-system-assigned --mi-user-assigned myAssignedId
- name: Create a disk encryption set with multi-tenant application client id to access key vault in a different tenant.
text: az disk-encryption-set create --resource-group MyResourceGroup --name MyDiskEncryptionSet --key-url MyKey --source-vault MyVault --federatedClientId myFederatedClientId
- name: Create a disk encryption set that supports double encryption.
text: az disk-encryption-set create --resource-group MyResourceGroup --name MyDiskEncryptionSet --key-url MyKey --source-vault MyVault --encryption-type EncryptionAtRestWithPlatformAndCustomerKeys
"""
Expand Down Expand Up @@ -255,6 +263,57 @@
text: |
az disk-encryption-set update --name MyDiskEncryptionSet --resource-group MyResourceGroup --key-url MyKey --source-vault MyVault
crafted: true
- name: Update multi-tenant application client id of a disk encryption set.
text: |
az disk-encryption-set update --name MyDiskEncryptionSet --resource-group MyResourceGroup --key-url MyKey --source-vault MyVault --federatedClientId myFederatedClientId
- name: Clear multi-tenant application client id of a disk encryption set.
text: |
az disk-encryption-set update --name MyDiskEncryptionSet --resource-group MyResourceGroup --key-url MyKey --source-vault MyVault --federatedClientId None
"""

helps['disk-encryption-set identity'] = """
type: group
short-summary: Manage identities of a disk encryption set.
"""

helps['disk-encryption-set identity assign'] = """
type: command
short-summary: Add managed identities to an existing disk encryption set.
examples:
- name: Add a system assigned managed identity to an existing disk encryption set.
text: >
az disk-encryption-set identity assign --name MyDiskEncryptionSet --resource-group MyResourceGroup --system-assigned
- name: Add a user assigned managed identity to an existing disk encryption set.
text: >
az disk-encryption-set identity assign --name MyDiskEncryptionSet --resource-group MyResourceGroup --user-assigned MyAssignedId
- name: Add system assigned identity and a user assigned managed identity to an existing disk encryption set.
text: >
az disk-encryption-set identity assign --name MyDiskEncryptionSet --resource-group MyResourceGroup --system-assigned --user-assigned MyAssignedId
"""

helps['disk-encryption-set identity remove'] = """
type: command
short-summary: Remove managed identities from an existing disk encryption set.
examples:
- name: Remove a system assigned managed identity from an existing disk encryption set.
text: >
az disk-encryption-set identity remove --name MyDiskEncryptionSet --resource-group MyResourceGroup --system-assigned
- name: Remove a user assigned managed identity from an existing disk encryption set.
text: >
az disk-encryption-set identity remove --name MyDiskEncryptionSet --resource-group MyResourceGroup --user-assigned MyAssignedId
- name: Remove all user assigned managed identities from an existing disk encryption set.
text: >
az disk-encryption-set identity remove --name MyDiskEncryptionSet --resource-group MyResourceGroup --user-assigned
"""

helps['disk-encryption-set identity show'] = """
type: command
short-summary: Display managed identities of a disk encryption set.
examples:
- name: Display managed identities of a disk encryption set. (autogenerated)
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
text: |
az disk-encryption-set identity show --name MyDiskEncryptionSet --resource-group MyResourceGroup
crafted: true
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
"""

helps['image'] = """
Expand Down
23 changes: 23 additions & 0 deletions src/azure-cli/azure/cli/command_modules/vm/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,29 @@ def load_arguments(self, _):
c.argument('enable_auto_key_rotation', arg_type=get_three_state_flag(), min_api='2020-12-01',
options_list=['--enable-auto-key-rotation', '--auto-rotation'],
help='Enable automatic rotation of keys.')

with self.argument_context('disk-encryption-set create', operation_group='disk_encryption_sets',
min_api='2022-03-02') as c:
c.argument('federatedClientId', help='The federated client id used in cross tenant scenario.')
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
c.argument('mi_system_assigned', arg_group='Managed Identity', arg_type=get_three_state_flag(),
help='Provide this flag to use system assigned identity. Check out help for more examples')
c.argument('mi_user_assigned', arg_group='Managed Identity', nargs='+',
help='User Assigned Identity ids to be used for disk encryption set. '
'Check out help for more examples')

with self.argument_context('disk-encryption-set update', operation_group='disk_encryption_sets',
min_api='2022-03-02') as c:
c.argument('federatedClientId', help='The federated client id used in cross tenant scenario.')
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved

with self.argument_context('disk-encryption-set identity', operation_group='disk_encryption_sets',
min_api='2022-03-02') as c:
c.argument('mi_system_assigned', options_list=['--system-assigned'],
arg_group='Managed Identity', arg_type=get_three_state_flag(),
help='Provide this flag to use system assigned identity for disk encryption set. '
'Check out help for more examples')
c.argument('mi_user_assigned', options_list=['--user-assigned'], arg_group='Managed Identity', nargs='*',
help='User Assigned Identity ids to be used for disk encryption set. '
'Check out help for more examples')
# endregion

# region DiskAccess
Expand Down
4 changes: 4 additions & 0 deletions src/azure-cli/azure/cli/command_modules/vm/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ def load_command_table(self, _):
g.wait_command('wait')

with self.command_group('disk-encryption-set', compute_disk_encryption_set_sdk, operation_group='disk_encryption_sets', client_factory=cf_disk_encryption_set, min_api='2019-07-01') as g:
g.custom_command('identity assign', 'assign_disk_encryption_set_identity', min_api='2022-03-02')
g.custom_command('identity remove', 'remove_disk_encryption_set_identity', min_api='2022-03-02')
g.custom_show_command('identity show', 'show_disk_encryption_set_identity', min_api='2022-03-02')
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved

g.custom_command('create', 'create_disk_encryption_set', supports_no_wait=True)
g.command('delete', 'begin_delete')
g.generic_update_command('update', custom_func_name='update_disk_encryption_set', setter_arg_name='disk_encryption_set', setter_name='begin_create_or_update')
Expand Down
164 changes: 160 additions & 4 deletions src/azure-cli/azure/cli/command_modules/vm/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,13 @@ def patch_vm(cmd, resource_group_name, vm_name, vm):
return LongRunningOperation(cmd.cli_ctx)(poller)


def patch_disk_encryption_set(cmd, resource_group_name, disk_encryption_set_name, disk_encryption_set_update):
client = _compute_client_factory(cmd.cli_ctx)
poller = client.disk_encryption_sets.begin_update(resource_group_name, disk_encryption_set_name,
disk_encryption_set_update)
return LongRunningOperation(cmd.cli_ctx)(poller)


def show_vm(cmd, resource_group_name, vm_name, show_details=False, include_user_data=False):
if show_details:
return get_vm_details(cmd, resource_group_name, vm_name, include_user_data)
Expand Down Expand Up @@ -2002,6 +2009,48 @@ def setter(resource_group_name, vm_name, vm):
identities = [MSI_LOCAL_ID]

return _remove_identities(cmd, resource_group_name, vm_name, identities, get_vm, setter)


# region VirtualMachines Identity
def _remove_disk_encryption_set_identities(cmd, resource_group_name, name,
mi_system_assigned, mi_user_assigned, getter, setter):
IdentityType = cmd.get_models('DiskEncryptionSetIdentityType', resource_type=ResourceType.MGMT_COMPUTE)
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
remove_system_assigned_identity = mi_system_assigned is not None

resource = getter(cmd, resource_group_name, name)
if resource is None or resource.identity is None:
return None
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved

user_identities_to_remove = []
if mi_user_assigned is not None:
existing_user_identities = {x.lower() for x in list((resource.identity.user_assigned_identities or {}).keys())}
# all user assigned identities will be removed if the length of mi_user_assigned is 0,
# otherwise the specified identity
user_identities_to_remove = {x.lower() for x in mi_user_assigned} \
if len(mi_user_assigned) > 0 else existing_user_identities
non_existing = user_identities_to_remove.difference(existing_user_identities)
if non_existing:
raise CLIError("'{}' are not associated with '{}'".format(','.join(non_existing), name))
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
if not list(existing_user_identities - user_identities_to_remove):
if resource.identity.type == IdentityType.USER_ASSIGNED:
resource.identity.type = IdentityType.NONE
elif resource.identity.type == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED:
resource.identity.type = IdentityType.SYSTEM_ASSIGNED

resource.identity.user_assigned_identities = None
if remove_system_assigned_identity:
resource.identity.type = (IdentityType.NONE
if resource.identity.type == IdentityType.SYSTEM_ASSIGNED
else IdentityType.USER_ASSIGNED)

if user_identities_to_remove:
if resource.identity.type not in [IdentityType.NONE, IdentityType.SYSTEM_ASSIGNED]:
resource.identity.user_assigned_identities = {}
for identity in user_identities_to_remove:
resource.identity.user_assigned_identities[identity] = None

result = LongRunningOperation(cmd.cli_ctx)(setter(resource_group_name, name, resource))
return result.identity
# endregion


Expand Down Expand Up @@ -3283,6 +3332,24 @@ def _build_identities_info(identities):
return (info, identity_types, external_identities, 'SystemAssigned' in identity_types)


def _build_identities_info_from_system_user_assigned(cmd, mi_system_assigned, mi_user_assigned):
IdentityType = cmd.get_models('DiskEncryptionSetIdentityType', resource_type=ResourceType.MGMT_COMPUTE)
UserAssignedIdentitiesValue = cmd.get_models('UserAssignedIdentitiesValue', resource_type=ResourceType.MGMT_COMPUTE)
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved

identity_types = IdentityType.SYSTEM_ASSIGNED
user_assigned_identities = None
if mi_user_assigned:
if mi_system_assigned:
identity_types = IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED
else:
identity_types = IdentityType.USER_ASSIGNED

default_user_identity = UserAssignedIdentitiesValue()
user_assigned_identities = dict.fromkeys(mi_user_assigned, default_user_identity)

return identity_types, user_assigned_identities


def deallocate_vmss(cmd, resource_group_name, vm_scale_set_name, instance_ids=None, no_wait=False):
client = _compute_client_factory(cmd.cli_ctx)
if instance_ids and len(instance_ids) == 1:
Expand Down Expand Up @@ -4565,21 +4632,43 @@ def _set_log_analytics_workspace_extension(cmd, resource_group_name, vm, vm_name
# disk encryption set
def create_disk_encryption_set(
cmd, client, resource_group_name, disk_encryption_set_name, key_url, source_vault=None, encryption_type=None,
location=None, tags=None, no_wait=False, enable_auto_key_rotation=None):
location=None, tags=None, no_wait=False, enable_auto_key_rotation=None, federatedClientId=None,
mi_system_assigned=None, mi_user_assigned=None):
from msrestazure.tools import resource_id, is_valid_resource_id
from azure.cli.core.commands.client_factory import get_subscription_id
DiskEncryptionSet, EncryptionSetIdentity, KeyForDiskEncryptionSet, SourceVault = cmd.get_models(
'DiskEncryptionSet', 'EncryptionSetIdentity', 'KeyForDiskEncryptionSet', 'SourceVault')
encryption_set_identity = EncryptionSetIdentity(type='SystemAssigned')

identity_type = 'SystemAssigned'
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
user_assigned_identities = None
if cmd.supported_api_version(min_api='2022-03-02', resource_type=ResourceType.MGMT_COMPUTE,
operation_group='disk_encryption_sets'):
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
identity_type, user_assigned_identities = \
_build_identities_info_from_system_user_assigned(cmd, mi_system_assigned, mi_user_assigned)

encryption_set_identity = EncryptionSetIdentity(type=identity_type)
if user_assigned_identities is not None:
if not hasattr(encryption_set_identity, 'user_assigned_identities'):
raise CLIError('User assigned identities in disk encryption set is not supported')
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
encryption_set_identity.user_assigned_identities = user_assigned_identities

if source_vault is not None:
if not is_valid_resource_id(source_vault):
source_vault = resource_id(subscription=get_subscription_id(cmd.cli_ctx), resource_group=resource_group_name,
source_vault = resource_id(subscription=get_subscription_id(cmd.cli_ctx),
resource_group=resource_group_name,
namespace='Microsoft.KeyVault', type='vaults', name=source_vault)
source_vault = SourceVault(id=source_vault)

key_for_disk_emcryption_set = KeyForDiskEncryptionSet(source_vault=source_vault, key_url=key_url)
disk_encryption_set = DiskEncryptionSet(location=location, tags=tags, identity=encryption_set_identity,
active_key=key_for_disk_emcryption_set, encryption_type=encryption_type,
rotation_to_latest_key_version_enabled=enable_auto_key_rotation)

if federatedClientId is not None:
if not hasattr(disk_encryption_set, 'federated_client_id'):
raise CLIError('Multi-tenant application client id in disk encryption set is not supported')
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
disk_encryption_set.federated_client_id = federatedClientId

return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, disk_encryption_set_name,
disk_encryption_set)

Expand All @@ -4591,7 +4680,7 @@ def list_disk_encryption_sets(cmd, client, resource_group_name=None):


def update_disk_encryption_set(cmd, instance, client, resource_group_name, key_url=None, source_vault=None,
enable_auto_key_rotation=None):
enable_auto_key_rotation=None, federatedClientId=None):
from msrestazure.tools import resource_id, is_valid_resource_id
from azure.cli.core.commands.client_factory import get_subscription_id
if key_url:
Expand All @@ -4607,8 +4696,75 @@ def update_disk_encryption_set(cmd, instance, client, resource_group_name, key_u
if enable_auto_key_rotation is not None:
instance.rotation_to_latest_key_version_enabled = enable_auto_key_rotation

if federatedClientId is not None:
if not hasattr(instance, 'federated_client_id'):
raise CLIError('Multi-tenant application client id in disk encryption set is not supported')
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
instance.federated_client_id = federatedClientId

return instance


def assign_disk_encryption_set_identity(cmd, client, resource_group_name, disk_encryption_set_name,
mi_system_assigned=None, mi_user_assigned=None):
DiskEncryptionSetUpdate, EncryptionSetIdentity = cmd.get_models('DiskEncryptionSetUpdate', 'EncryptionSetIdentity')
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
from azure.cli.core.commands.arm import assign_identity as assign_identity_helper
client = _compute_client_factory(cmd.cli_ctx)

def getter():
return client.disk_encryption_sets.get(resource_group_name, disk_encryption_set_name)

def setter(disk_encryption_set, mi_system_assigned=mi_system_assigned, mi_user_assigned=mi_user_assigned):
IdentityType = cmd.get_models('DiskEncryptionSetIdentityType', resource_type=ResourceType.MGMT_COMPUTE)
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
existing_system_identity = False
existing_user_identities = set()
if disk_encryption_set.identity is not None:
existing_system_identity = disk_encryption_set.identity.type in [IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED,
IdentityType.SYSTEM_ASSIGNED]
existing_user_identities = {x.lower() for x in
list((disk_encryption_set.identity.user_assigned_identities or {}).keys())}

add_system_assigned = mi_system_assigned is True
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
add_user_assigned = {x.lower() for x in (mi_user_assigned or [])}

updated_system_assigned = existing_system_identity or add_system_assigned
updated_user_assigned = list(existing_user_identities | add_user_assigned)
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved

identity_types, user_assigned_identities = _build_identities_info_from_system_user_assigned(
cmd, updated_system_assigned, updated_user_assigned)

encryption_set_identity = EncryptionSetIdentity(type=identity_types,
user_assigned_identities=user_assigned_identities)

disk_encryption_set_update = DiskEncryptionSetUpdate()
disk_encryption_set_update.identity = encryption_set_identity
return patch_disk_encryption_set(cmd, resource_group_name, disk_encryption_set_name, disk_encryption_set_update)

assign_identity_helper(cmd.cli_ctx, getter, setter)
disk_encryption_set = client.disk_encryption_sets.get(resource_group_name, disk_encryption_set_name)
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
return disk_encryption_set.identity


def remove_disk_encryption_set_identity(cmd, client, resource_group_name, disk_encryption_set_name,
mi_system_assigned=None, mi_user_assigned=None):
DiskEncryptionSetUpdate = cmd.get_models('DiskEncryptionSetUpdate')
zhoxing-ms marked this conversation as resolved.
Show resolved Hide resolved
client = _compute_client_factory(cmd.cli_ctx)

def getter(cmd, resource_group_name, disk_encryption_set_name):
return client.disk_encryption_sets.get(resource_group_name, disk_encryption_set_name)

def setter(resource_group_name, disk_encryption_set_name, disk_encryption_set):
disk_encryption_set_update = DiskEncryptionSetUpdate(identity=disk_encryption_set.identity)
return client.disk_encryption_sets.begin_update(resource_group_name, disk_encryption_set_name,
disk_encryption_set_update)

return _remove_disk_encryption_set_identities(cmd, resource_group_name, disk_encryption_set_name,
mi_system_assigned, mi_user_assigned, getter, setter)


def show_disk_encryption_set_identity(cmd, resource_group_name, disk_encryption_set_name):
client = _compute_client_factory(cmd.cli_ctx)
return client.disk_encryption_sets.get(resource_group_name, disk_encryption_set_name).identity

# endregion


Expand Down
Loading