Skip to content

Commit 632a13e

Browse files
authored
[Containerapp] az containerapp sessionpool: Support managed identity when create session pool with --mi-system-assigned --mi-user-assigned (#8223)
1 parent 2394eaa commit 632a13e

16 files changed

+41081
-12512
lines changed

src/containerapp/HISTORY.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ Release History
55
upcoming
66
++++++
77
* 'az containerapp create': Fix Role assignment error when the default Azure Container Registry could not be found
8-
* Upgrade api-version to 2024-10-02-preview
8+
* Upgrade api-version to 2024-10-02-preview
99
* 'az containerapp create/update': `--yaml` support property pollingInterval and cooldownPeriod
10+
* 'az containerapp sessionpool create': Support managed identity when create session pool with --mi-system-assigned --mi-user-assigned
1011

1112
1.0.0b4
1213
++++++

src/containerapp/azext_containerapp/_clients.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ def list(cls, cmd, resource_group_name, environment_name):
10181018

10191019

10201020
class SessionPoolPreviewClient():
1021-
api_version = SESSION_API_VERSION
1021+
api_version = PREVIEW_API_VERSION
10221022

10231023
@classmethod
10241024
def create(cls, cmd, resource_group_name, name, session_pool_envelope, no_wait=False):

src/containerapp/azext_containerapp/_help.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,20 @@
18971897
--cpu 0.5 --memory 1Gi --target-port 80 --registry-server myregistry.azurecr.io \\
18981898
--registry-username myregistry --registry-password $REGISTRY_PASSWORD \\
18991899
--location eastasia
1900+
- name: Create or update a Session Pool with container type CustomContainer and Managed Identity to authenticate Azure container registry
1901+
text: |
1902+
az containerapp sessionpool create -n mysessionpool -g MyResourceGroup \\
1903+
--container-type CustomContainer --environment MyEnvironment --image MyImage \\
1904+
--cpu 0.5 --memory 1Gi --target-port 80 --registry-server myregistry.azurecr.io \\
1905+
--registry-identity MyUserIdentityResourceId \\
1906+
--location eastasia
1907+
- name: Create or update a Session Pool with container type CustomContainer with system assigned and user assigned identity.
1908+
text: |
1909+
az containerapp sessionpool create -n mysessionpool -g MyResourceGroup \\
1910+
--container-type CustomContainer --environment MyEnvironment --image MyImage \\
1911+
--cpu 0.5 --memory 1Gi --target-port 80 \\
1912+
--mi-system-assigned --mi-user-assigned MyUserIdentityResourceId \\
1913+
--location eastasia
19001914
- name: Create or update a Session Pool with container type CustomContainer with cooldown period 360s
19011915
text: |
19021916
az containerapp sessionpool create -n mysessionpool -g MyResourceGroup \\

src/containerapp/azext_containerapp/_params.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ def load_arguments(self, _):
398398
with self.argument_context('containerapp sessionpool') as c:
399399
c.argument('name', options_list=['--name', '-n'], help="The Session Pool name.")
400400
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)
401+
c.argument('mi_system_assigned', help='Boolean indicating whether to assign system-assigned identity.', action='store_true')
402+
c.argument('mi_user_assigned', nargs='+', help='Space-separated user identities to be assigned.')
401403

402404
with self.argument_context('containerapp sessionpool', arg_group='Configuration') as c:
403405
c.argument('container_type', arg_type=get_enum_type(["CustomContainer", "PythonLTS", "NodeLTS"]), help="The pool type of the Session Pool, default='PythonLTS'")
@@ -424,6 +426,7 @@ def load_arguments(self, _):
424426
c.argument('registry_server', validator=validate_registry_server, help="The container registry server hostname, e.g. myregistry.azurecr.io.")
425427
c.argument('registry_pass', validator=validate_registry_pass, options_list=['--registry-password'], help="The password to log in to container registry. If stored as a secret, value must start with \'secretref:\' followed by the secret name.")
426428
c.argument('registry_user', validator=validate_registry_user, options_list=['--registry-username'], help="The username to log in to container registry.")
429+
c.argument('registry_identity', validator=validate_registry_user, help="The managed identity with which to authenticate to the Azure Container Registry (instead of username/password). Use 'system' for a system-assigned identity, use a resource ID for a user-assigned identity. The managed identity should have been assigned acrpull permissions on the ACR before deployment (use 'az role assignment create --role acrpull ...').")
427430

428431
# sessions code interpreter commands
429432
with self.argument_context('containerapp session code-interpreter') as c:

src/containerapp/azext_containerapp/containerapp_sessionpool_decorator.py

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525
store_as_secret_and_return_secret_ref,
2626
_ensure_location_allowed, CONTAINER_APPS_RP,
2727
validate_container_app_name,
28-
safe_set, safe_get)
28+
safe_set, safe_get, _ensure_identity_resource_id)
2929
from azure.cli.command_modules.containerapp._clients import ManagedEnvironmentClient
3030
from azure.cli.command_modules.containerapp._client_factory import handle_non_404_status_code_exception
31+
from azure.cli.command_modules.containerapp._utils import is_registry_msi_system
3132
from azure.cli.core.commands.client_factory import get_subscription_id
3233

33-
from ._models import SessionPool as SessionPoolModel
34+
from ._models import ManagedServiceIdentity, SessionPool as SessionPoolModel
3435
from ._client_factory import handle_raw_exception
3536
from ._utils import AppType
3637

@@ -129,6 +130,15 @@ def get_argument_registry_pass(self):
129130
def get_argument_registry_user(self):
130131
return self.get_param("registry_user")
131132

133+
def get_argument_registry_identity(self):
134+
return self.get_param("registry_identity")
135+
136+
def get_argument_system_assigned(self):
137+
return self.get_param("mi_system_assigned")
138+
139+
def get_argument_user_assigned(self):
140+
return self.get_param("mi_user_assigned")
141+
132142
# pylint: disable=no-self-use
133143
def get_environment_client(self):
134144
return ManagedEnvironmentClient
@@ -153,6 +163,7 @@ def validate_arguments(self):
153163

154164
def construct_payload(self):
155165
self.session_pool_def["location"] = self.get_argument_location()
166+
self.set_up_managed_identity()
156167

157168
# We only support 'Dynamic' type in CLI
158169
self.session_pool_def["properties"]["poolManagementType"] = "Dynamic"
@@ -186,6 +197,59 @@ def construct_payload(self):
186197
safe_set(self.session_pool_def, "properties", "dynamicPoolConfiguration", value=dynamic_pool_def)
187198
safe_set(self.session_pool_def, "properties", "sessionNetworkConfiguration", value=session_network_def)
188199
safe_set(self.session_pool_def, "properties", "scaleConfiguration", value=session_scale_def)
200+
self.set_up_managed_identity_settings()
201+
202+
def set_up_managed_identity_settings(self):
203+
managed_identity_settings = []
204+
if self.get_argument_system_assigned():
205+
managed_identity_setting = {
206+
"identity": "system",
207+
"lifecycle": "Main"
208+
}
209+
managed_identity_settings.append(managed_identity_setting)
210+
211+
if self.get_argument_user_assigned():
212+
for x in self.get_argument_user_assigned():
213+
managed_identity_setting = {
214+
"identity": x.lower(),
215+
"lifecycle": "Main"
216+
}
217+
managed_identity_settings.append(managed_identity_setting)
218+
if managed_identity_settings:
219+
safe_set(self.session_pool_def, "properties", "managedIdentitySettings", value=managed_identity_settings)
220+
221+
def set_up_managed_identity(self):
222+
identity_def = deepcopy(ManagedServiceIdentity)
223+
identity_def["type"] = "None"
224+
225+
assign_system_identity = self.get_argument_system_assigned()
226+
if self.get_argument_user_assigned():
227+
assign_user_identities = [x.lower() for x in self.get_argument_user_assigned()]
228+
else:
229+
assign_user_identities = []
230+
231+
identity = self.get_argument_registry_identity()
232+
if identity:
233+
if is_registry_msi_system(identity):
234+
assign_system_identity = True
235+
else:
236+
assign_user_identities.append(self.get_argument_registry_identity())
237+
238+
if assign_system_identity and assign_user_identities:
239+
identity_def["type"] = "SystemAssigned, UserAssigned"
240+
elif assign_system_identity:
241+
identity_def["type"] = "SystemAssigned"
242+
elif assign_user_identities:
243+
identity_def["type"] = "UserAssigned"
244+
245+
if assign_user_identities:
246+
identity_def["userAssignedIdentities"] = {}
247+
subscription_id = get_subscription_id(self.cmd.cli_ctx)
248+
249+
for r in assign_user_identities:
250+
r = _ensure_identity_resource_id(subscription_id, self.get_argument_resource_group_name(), r)
251+
identity_def["userAssignedIdentities"][r] = {} # pylint: disable=unsupported-assignment-operation
252+
self.session_pool_def["identity"] = identity_def
189253

190254
def set_up_dynamic_configuration(self):
191255
dynamic_pool_def = {}
@@ -244,14 +308,20 @@ def set_up_registry_auth_configuration(self, secrets_def):
244308
if self.get_argument_registry_server() is not None:
245309
registry_def = {}
246310
registry_def["server"] = self.get_argument_registry_server()
247-
registry_def["username"] = self.get_argument_registry_user()
248-
249-
if secrets_def is None:
250-
secrets_def = []
251-
registry_def["passwordSecretRef"] = store_as_secret_and_return_secret_ref(secrets_def,
252-
self.get_argument_registry_user(),
253-
self.get_argument_registry_server(),
254-
self.get_argument_registry_pass())
311+
312+
if self.get_argument_registry_user():
313+
registry_def["username"] = self.get_argument_registry_user()
314+
315+
if secrets_def is None:
316+
secrets_def = []
317+
registry_def["passwordSecretRef"] = store_as_secret_and_return_secret_ref(secrets_def,
318+
self.get_argument_registry_user(),
319+
self.get_argument_registry_server(),
320+
self.get_argument_registry_pass())
321+
322+
if self.get_argument_registry_identity():
323+
registry_def["identity"] = self.get_argument_registry_identity()
324+
255325
return registry_def, secrets_def
256326

257327
def set_up_ingress(self):

src/containerapp/azext_containerapp/custom.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2864,7 +2864,10 @@ def create_session_pool(cmd,
28642864
target_port=None,
28652865
registry_server=None,
28662866
registry_pass=None,
2867-
registry_user=None):
2867+
registry_user=None,
2868+
mi_user_assigned=None,
2869+
registry_identity=None,
2870+
mi_system_assigned=False):
28682871
raw_parameters = locals()
28692872
session_pool_decorator = SessionPoolCreateDecorator(
28702873
cmd=cmd,

0 commit comments

Comments
 (0)