Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
a2f6b6c
Initial updates to use track 2 Hub GA SDK
c-ryan-k Mar 3, 2021
45cadfb
WIP initial user-assigned identity functionality
c-ryan-k Mar 16, 2021
2acf8e6
Added routing endpoint identity and added some preliminary test code
c-ryan-k Mar 17, 2021
b613397
Updated identity code with enums and minor logic updates
c-ryan-k Mar 18, 2021
b3208a4
More identity updates, role and scope assignment
c-ryan-k Mar 18, 2021
ee48812
Minor fixes to identity functions and return values
c-ryan-k Mar 19, 2021
ee807a0
Linting fixes
c-ryan-k Mar 19, 2021
ea84c66
WIP testing updates
c-ryan-k Mar 19, 2021
44117af
Merge branch 'azure-dev' into hub_track2
c-ryan-k Mar 26, 2021
fa2dbb8
Help/Param updates and minor tweaks/fixes
c-ryan-k Mar 30, 2021
691950d
Test updates, help updates, polling updates
c-ryan-k Mar 30, 2021
8fc4f2e
Test recording updates
c-ryan-k Mar 31, 2021
1282c05
test updates
c-ryan-k Apr 6, 2021
0a76443
minor fix in consumer_group_create, test updates
c-ryan-k Apr 6, 2021
5060def
Initial updates to use track 2 Hub GA SDK
c-ryan-k Mar 3, 2021
14fa27d
WIP initial user-assigned identity functionality
c-ryan-k Mar 16, 2021
bc5e641
Added routing endpoint identity and added some preliminary test code
c-ryan-k Mar 17, 2021
8779d9a
Updated identity code with enums and minor logic updates
c-ryan-k Mar 18, 2021
889f42c
More identity updates, role and scope assignment
c-ryan-k Mar 18, 2021
90342f9
Minor fixes to identity functions and return values
c-ryan-k Mar 19, 2021
9b25700
Linting fixes
c-ryan-k Mar 19, 2021
850ab5e
WIP testing updates
c-ryan-k Mar 19, 2021
a65084e
Help/Param updates and minor tweaks/fixes
c-ryan-k Mar 30, 2021
c92c45f
Test updates, help updates, polling updates
c-ryan-k Mar 30, 2021
5f5e1e2
Test recording updates
c-ryan-k Mar 31, 2021
df59fbf
test updates
c-ryan-k Apr 6, 2021
e6308da
minor fix in consumer_group_create, test updates
c-ryan-k Apr 6, 2021
47e700a
Merge branch 'hub_track2' of https://github.com/c-ryan-k/azure-cli in…
c-ryan-k Apr 12, 2021
0214f61
Test updates and new recordings
c-ryan-k Apr 12, 2021
3ea6fc4
Merge branch 'azure-dev' into hub_track2
c-ryan-k Apr 20, 2021
a29fe37
Fix for ARM issue - user identity object must be empty upon removal o…
c-ryan-k Apr 27, 2021
1a0ceeb
Updates to use stable multiapi SDK (2021-03-03) with backfill for dev…
c-ryan-k Apr 28, 2021
d1b5f0d
Merge branch 'dev' into hub_track2
c-ryan-k Apr 28, 2021
ef103b3
Help/Param string updates
c-ryan-k Apr 28, 2021
fb759e6
Version fix for new SDK (2021-03-31)
c-ryan-k Apr 29, 2021
bdc3f5a
Parameter updates
c-ryan-k May 11, 2021
a599f8b
Test and recording updates
c-ryan-k May 11, 2021
cd59e5a
Certificate create/update fixes and test updates
c-ryan-k May 11, 2021
4e8d6a3
Add DeviceConnectionStateEvents as a routing source type
c-ryan-k May 13, 2021
0ab2ae5
RoutingSource test updates
c-ryan-k May 14, 2021
20992a8
SDK version update to 2.0.0
c-ryan-k May 14, 2021
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
Prev Previous commit
Next Next commit
More identity updates, role and scope assignment
  • Loading branch information
c-ryan-k committed Mar 18, 2021
commit b3208a41008dd360cbae7dd498389bc0ffb65b3d
3 changes: 3 additions & 0 deletions src/azure-cli/azure/cli/command_modules/iot/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
c.argument('identities', options_list=['--assign-identity'],
nargs='*', help="Accepts system or user assigned identities separated by spaces. Use '[system]'"
" to refer to the system-assigned identity or a resource ID to refer to a user-assigned identity.")
c.argument('identity_role', options_list=['--role'], help="Role to assign to the hub's system-assigned identity")
c.argument('identity_scopes', options_list=['--scopes'],
nargs='*', help="Space separated list of scopes to assign the role (--role) to for the system-assigned managed identity.")

with self.argument_context('iot hub identity') as c:
c.argument('identities', options_list=['--identities'],
Expand Down
143 changes: 93 additions & 50 deletions src/azure-cli/azure/cli/command_modules/iot/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,9 @@ def iot_hub_create(cmd, client, hub_name, resource_group_name, location=None,
fileupload_storage_identity=None,
min_tls_version=None,
tags=None,
identities=None):
identities=None,
identity_role=None,
identity_scopes=None):
from datetime import timedelta
cli_ctx = cmd.cli_ctx
if enable_fileupload_notifications:
Expand Down Expand Up @@ -446,7 +448,7 @@ def iot_hub_create(cmd, client, hub_name, resource_group_name, location=None,
container_name=fileupload_storage_container_name if fileupload_storage_container_name else '',
authentication_type=fileupload_storage_authentication_type if fileupload_storage_authentication_type else None,
container_uri=fileupload_storage_container_uri if fileupload_storage_container_uri else '',
identity=ManagedIdentity(fileupload_storage_identity) if fileupload_storage_identity else None)
identity=ManagedIdentity(user_assigned_identity=fileupload_storage_identity) if fileupload_storage_identity else None)

properties = IotHubProperties(event_hub_endpoints=event_hub_dic,
messaging_endpoints=msg_endpoint_dic,
Expand All @@ -459,18 +461,29 @@ def iot_hub_create(cmd, client, hub_name, resource_group_name, location=None,
sku=sku,
properties=properties,
tags=tags)
if identities:
hub_description.identity = ArmIdentity()
user_identities = [identity for identity in identities if identity != SYSTEM_IDENTITY]
for identity in user_identities:
hub_description.identity.user_assigned_identities[identity] = {}

if SYSTEM_IDENTITY in identities:
hub_description.identity.type = IdentityType.SystemAssignedUserAssigned if hub_description.identity.user_assigned_identities else IdentityType.SystemAssigned
else:
hub_description.identity.type = IdentityType.UserAssigned

return client.iot_hub_resource.begin_create_or_update(resource_group_name, hub_name, hub_description)
hub_description.identity = _build_identity(identities) if identities else None
if identity_role and not identity_scopes:
raise CLIError('At least one scope required for identity role assignment')

def identity_assignment(lro):
try:
from azure.cli.core.commands.arm import assign_identity
instance = lro.resource().as_dict()
identity = instance.get("identity")
if identity:
principal_id = identity.get("principal_id")
if principal_id:
hub_description.identity.principal_id = principal_id
for scope in identity_scopes:
hub = assign_identity(cmd.cli_ctx, lambda: hub_description, lambda hub: hub_description, identity_role=identity_role, identity_scope=scope)
return hub
except CloudError as e:
raise e

create = client.iot_hub_resource.begin_create_or_update(resource_group_name, hub_name, hub_description)
if identity_role and identity_scopes:
create.add_done_callback(identity_assignment)
return create


def iot_hub_get(cmd, client, hub_name, resource_group_name=None):
Expand Down Expand Up @@ -561,10 +574,14 @@ def update_iot_hub_custom(instance,
# If we are now (or will be) using fsa=identity AND we've set a new identity
if instance.properties.storage_endpoints['$default'].authentication_type == AuthenticationType.IdentityBased and fileupload_storage_identity:
# setup new fsi
instance.properties.storage_endpoints['$default'].identity = ManagedIdentity(fileupload_storage_identity)
instance.properties.storage_endpoints['$default'].identity = ManagedIdentity(user_assigned_identity=fileupload_storage_identity) if fileupload_storage_identity not in [IdentityType.none.value, SYSTEM_IDENTITY] else None
# otherwise - let them know they need identity-based auth enabled
elif fileupload_storage_identity:
raise CLIError('In order to set a file upload storage identity, you must set the file upload storage authentication type (--fsa) to IdentityBased')

# TODO - ensure this is necessary
if not instance.identity.user_assigned_identities:
instance.identity.user_assigned_identities = None
return instance


Expand Down Expand Up @@ -636,51 +653,56 @@ def iot_hub_consumer_group_delete(client, hub_name, consumer_group_name, resourc

def iot_hub_identity_assign(cmd, client, hub_name, identities, role=None, scopes=None, resource_group_name=None):
resource_group_name = _ensure_resource_group_name(client, resource_group_name, hub_name)
hub = iot_hub_get(cmd, client, hub_name, resource_group_name)

# if assigning a [system] identity, use role and scopes to update it after
user_identities = [identity for identity in identities if identity != SYSTEM_IDENTITY]
for identity in user_identities:
hub.identity.user_assigned_identities[identity] = {}

if SYSTEM_IDENTITY in identities or hub.identity.type in [IdentityType.SystemAssignedUserAssigned, IdentityType.SystemAssigned]:
hub.identity.type = IdentityType.SystemAssignedUserAssigned if hub.identity.user_assigned_identities else IdentityType.SystemAssigned
else:
hub.identity.type = IdentityType.UserAssigned if hub.identity.user_assigned_identities else IdentityType.NoIdentity
def getter():
return iot_hub_get(cmd, client, hub_name, resource_group_name)
def setter(hub):
user_identities = [i for i in identities if i != SYSTEM_IDENTITY]
for identity in user_identities:
hub.identity.user_assigned_identities[identity] = hub.identity.user_assigned_identities.get(identity, {})

# user_assigned_identities must be 'None', not '{}' for SystemAssigned only
if hub.identity.type == IdentityType.SystemAssigned:
hub.identity.user_assigned_identities = None
has_system_identity = hub.identity.type in [IdentityType.system_assigned_user_assigned.value, IdentityType.system_assigned.value]

if SYSTEM_IDENTITY in identities:
if role and scopes:
# update hub
hub = client.iot_hub_resource.begin_create_or_update(resource_group_name, hub_name, hub, {'IF-MATCH': hub.etag})
# get hub identity
if SYSTEM_IDENTITY in identities or has_system_identity:
hub.identity.type = IdentityType.system_assigned_user_assigned.value if hub.identity.user_assigned_identities else IdentityType.system_assigned.value
else:
hub.identity.type = IdentityType.user_assigned.value if hub.identity.user_assigned_identities else IdentityType.none.value

# system_identity = hub.identity.principalId
hub.identity.user_assigned_identities = hub.identity.user_assigned_identities or None
poller = client.iot_hub_resource.begin_create_or_update(resource_group_name, hub_name, hub, {'IF-MATCH': hub.etag})
return LongRunningOperation(cmd.cli_ctx)(poller)

# setup scope and role for system_identity
return hub
if role and not scopes:
raise CLIError('At least one scope required for identity role assignment')

return client.iot_hub_resource.begin_create_or_update(resource_group_name, hub_name, hub, {'IF-MATCH': hub.etag})
if role and scopes:
from azure.cli.core.commands.arm import assign_identity
for scope in [scopes]:
hub = assign_identity(cmd.cli_ctx, getter, setter, identity_role=role, identity_scope=scope)
return hub
else:
return setter(getter())


def iot_hub_identity_show(cmd, client, hub_name, resource_group_name=None):
resource_group_name = _ensure_resource_group_name(client, resource_group_name, hub_name)
hub = iot_hub_get(cmd, client, hub_name, resource_group_name)
return hub.identity


def iot_hub_identity_remove(cmd, client, hub_name, identities, resource_group_name=None):
resource_group_name = _ensure_resource_group_name(client, resource_group_name, hub_name)
hub = iot_hub_get(cmd, client, hub_name, resource_group_name)
hub_identity = hub.identity

# if identity is '[system]', turn off system managed identity
if SYSTEM_IDENTITY in identities:
if hub_identity.type not in [IdentityType.SystemAssigned, IdentityType.SystemAssignedUserAssigned]:
raise CLIError('Hub {} is not currently using a System-assigned Identity'.format(hub_name))
hub_identity.type = IdentityType.UserAssigned if hub.identity.type in [IdentityType.UserAssigned, IdentityType.SystemAssignedUserAssigned] else IdentityType.NoIdentity
if hub_identity.type not in [
IdentityType.system_assigned.value,
IdentityType.system_assigned_user_assigned.value
]:
raise CLIError('Hub {} is not currently using a system-assigned identity'.format(hub_name))
hub_identity.type = IdentityType.user_assigned if hub.identity.type in [IdentityType.user_assigned.value, IdentityType.system_assigned_user_assigned.value] else IdentityType.none.value

# separate user identities from system identity
user_identities = [identity for identity in identities if identity != SYSTEM_IDENTITY]
Expand All @@ -691,14 +713,16 @@ def iot_hub_identity_remove(cmd, client, hub_name, identities, resource_group_na
raise CLIError('Hub {0} is not currently using a user-assigned identity with id: {1}'.format(hub_name, identity))
del hub_identity.user_assigned_identities[identity]

# assign identity type correctly
if hub_identity.type in [IdentityType.SystemAssigned, IdentityType.SystemAssignedUserAssigned]:
hub_identity.type = IdentityType.SystemAssignedUserAssigned if hub_identity.user_assigned_identities else IdentityType.SystemAssigned
if hub_identity.type in [
IdentityType.system_assigned.value,
IdentityType.system_assigned_user_assigned.value
]:
hub_identity.type = IdentityType.system_assigned_user_assigned.value if hub_identity.user_assigned_identities else IdentityType.system_assigned.value
else:
hub_identity.type = IdentityType.UserAssigned if hub_identity.user_assigned_identities else IdentityType.NoIdentity
hub_identity.type = IdentityType.user_assigned.value if hub_identity.user_assigned_identities else IdentityType.none.value

# user_assigned_identities must be 'None', not '{}' for SystemAssigned only
if hub_identity.type == IdentityType.SystemAssigned:
# TODO - ensure this is necessary
if hub_identity.type == IdentityType.system_assigned.value:
hub_identity.user_assigned_identities = None

hub.identity = hub_identity
Expand Down Expand Up @@ -822,7 +846,7 @@ def iot_hub_routing_endpoint_create(cmd, client, hub_name, endpoint_name, endpoi
authentication_type=authentication_type,
endpoint_uri=endpoint_uri,
entity_path=entity_path,
identity=ManagedIdentity(identity) if identity else None
identity=ManagedIdentity(user_assigned_identity=identity) if identity not in [IdentityType.none.value, SYSTEM_IDENTITY] else None
)
)
elif EndpointType.ServiceBusQueue.value == endpoint_type.lower():
Expand All @@ -835,7 +859,7 @@ def iot_hub_routing_endpoint_create(cmd, client, hub_name, endpoint_name, endpoi
authentication_type=authentication_type,
endpoint_uri=endpoint_uri,
entity_path=entity_path,
identity=ManagedIdentity(identity) if identity else None
identity=ManagedIdentity(user_assigned_identity=identity) if identity not in [IdentityType.none.value, SYSTEM_IDENTITY] else None
)
)
elif EndpointType.ServiceBusTopic.value == endpoint_type.lower():
Expand All @@ -848,7 +872,7 @@ def iot_hub_routing_endpoint_create(cmd, client, hub_name, endpoint_name, endpoi
authentication_type=authentication_type,
endpoint_uri=endpoint_uri,
entity_path=entity_path,
identity=ManagedIdentity(identity) if identity else None
identity=ManagedIdentity(user_assigned_identity=identity) if identity not in [IdentityType.none.value, SYSTEM_IDENTITY] else None
)
)
elif EndpointType.AzureStorageContainer.value == endpoint_type.lower():
Expand All @@ -867,7 +891,7 @@ def iot_hub_routing_endpoint_create(cmd, client, hub_name, endpoint_name, endpoi
max_chunk_size_in_bytes=(chunk_size_window * 1048576),
authentication_type=authentication_type,
endpoint_uri=endpoint_uri,
identity=ManagedIdentity(identity) if identity else None
identity=ManagedIdentity(user_assigned_identity=identity) if identity not in [IdentityType.none.value, SYSTEM_IDENTITY] else None
)
)
return client.iot_hub_resource.begin_create_or_update(resource_group_name, hub_name, hub, {'IF-MATCH': hub.etag})
Expand Down Expand Up @@ -1249,3 +1273,22 @@ def _get_iot_central_app_by_name(client, app_name):
raise CLIError(
"No IoT Central application found with name {} in current subscription.".format(app_name))
return target_app


def _build_identity(identities):
identities = identities or []
identity_type = IdentityType.none.value
if not identities or SYSTEM_IDENTITY in identities:
identity_type = IdentityType.system_assigned.value
user_identities = [i for i in identities if i != SYSTEM_IDENTITY]
if user_identities and identity_type == IdentityType.system_assigned.value:
identity_type = IdentityType.system_assigned_user_assigned.value
elif user_identities:
identity_type = IdentityType.user_assigned.value

identity = ArmIdentity(type=identity_type)
if user_identities:
identity.user_assigned_identities = {i: {} for i in user_identities}
# else:
# identity.user_assigned_identities = None
return identity
8 changes: 4 additions & 4 deletions src/azure-cli/azure/cli/command_modules/iot/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class IdentityType(Enum):
"""
Type of managed identity for the IoT Hub.
"""
SystemAssigned = 'SystemAssigned'
SystemAssignedUserAssigned = 'SystemAssigned,UserAssigned'
UserAssigned = 'UserAssigned'
NoIdentity = 'None'
system_assigned = "SystemAssigned"
user_assigned = "UserAssigned"
system_assigned_user_assigned = "SystemAssigned, UserAssigned"
none = "None"