Skip to content

Commit

Permalink
[ContainerApp] support pull image using env msi (Azure#7876)
Browse files Browse the repository at this point in the history
  • Loading branch information
njuCZ authored Aug 15, 2024
1 parent 1cd7895 commit 2c16b96
Show file tree
Hide file tree
Showing 15 changed files with 25,012 additions and 1,986 deletions.
4 changes: 4 additions & 0 deletions src/containerapp/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Release History
===============
upcoming
++++++
* 'az containerapp create': Support --registry-identity to use Environment identity for ACR authentication.
* 'az containerapp job create': Support --registry-identity to use Environment identity for ACR authentication.
* 'az containerapp registry set': Support --identity to use Environment identity for ACR authentication.
* 'az containerapp job registry set': Support --identity to use Environment identity for ACR authentication.

0.3.54
++++++
Expand Down
28 changes: 28 additions & 0 deletions src/containerapp/azext_containerapp/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -2014,3 +2014,31 @@
text: |
az containerapp env dotnet-component show -n MyDotNetComponentName --environment MyContainerappEnvironment -g MyResourceGroup
"""

helps['containerapp registry set'] = """
type: command
short-summary: Add or update a container registry's details.
examples:
- name: Configure a container app to use a registry.
text: |
az containerapp registry set -n my-containerapp -g MyResourceGroup \\
--server MyExistingContainerappRegistry.azurecr.io --username MyRegistryUsername --password MyRegistryPassword
- name: Configure a container app to use environment system assigned managed identity to authenticate Azure container registry.
text: |
az containerapp registry set -n my-containerapp -g MyResourceGroup \\
--server MyExistingContainerappRegistry.azurecr.io --identity system-environment
"""

helps['containerapp job registry set'] = """
type: command
short-summary: Add or update a container registry's details in a Container App Job.
examples:
- name: Configure a Container App Job to use a registry.
text: |
az containerapp job registry set -n my-containerapp-job -g MyResourceGroup \\
--server MyContainerappJobRegistry.azurecr.io --username MyRegistryUsername --password MyRegistryPassword
- name: Configure a Container App Job to use environment system assigned managed identity to authenticate Azure container registry.
text: |
az containerapp job registry set -n my-containerapp-job -g MyResourceGroup \\
--server MyContainerappJobRegistry.azurecr.io --identity system-environment
"""
8 changes: 8 additions & 0 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def load_arguments(self, _):
c.argument('build_env_vars', nargs='*', help="A list of environment variable(s) for the build. Space-separated values in 'key=value' format.",
validator=validate_build_env_vars, is_preview=True)
c.argument('max_inactive_revisions', type=int, help="Max inactive revisions a Container App can have.", is_preview=True)
c.argument('registry_identity', help="The managed identity with which to authenticate to the Azure Container Registry (instead of username/password). Use 'system' for a system-defined identity, Use 'system-environment' for an environment level system-defined identity or a resource id for a user-defined environment/containerapp level identity. The managed identity should have been assigned acrpull permissions on the ACR before deployment (use 'az role assignment create --role acrpull ...').")

# Springboard
with self.argument_context('containerapp create', arg_group='Service Binding', is_preview=True) as c:
Expand Down Expand Up @@ -280,6 +281,7 @@ def load_arguments(self, _):
c.argument('min_executions', type=int, help="Minimum number of job executions that are created for a trigger")
c.argument('max_executions', type=int, help="Maximum number of job executions that are created for a trigger")
c.argument('polling_interval', type=int, help="Interval to check each event source in seconds.")
c.argument('registry_identity', help="The managed identity with which to authenticate to the Azure Container Registry (instead of username/password). Use 'system' for a system-defined identity, Use 'system-environment' for an environment level system-defined identity or a resource id for a user-defined environment/containerappjob level identity. The managed identity should have been assigned acrpull permissions on the ACR before deployment (use 'az role assignment create --role acrpull ...').")

with self.argument_context('containerapp job create') as c:
c.argument('system_assigned', options_list=['--mi-system-assigned', c.deprecate(target='--system-assigned', redirect='--mi-system-assigned', hide=True)], help='Boolean indicating whether to assign system-assigned identity.', action='store_true')
Expand Down Expand Up @@ -350,6 +352,9 @@ def load_arguments(self, _):
c.argument('build_env_vars', nargs='*', help="A list of environment variable(s) for the build. Space-separated values in 'key=value' format.",
validator=validate_build_env_vars, is_preview=True)

with self.argument_context('containerapp registry') as c:
c.argument('identity', help="The managed identity with which to authenticate to the Azure Container Registry (instead of username/password). Use 'system' for a system-defined identity, Use 'system-environment' for an environment level system-defined identity or a resource id for a user-defined environment/containerapp level identity. The managed identity should have been assigned acrpull permissions on the ACR before deployment (use 'az role assignment create --role acrpull ...').")

with self.argument_context('containerapp env java-component') as c:
c.argument('java_component_name', options_list=['--name', '-n'], help="The Java component name.")
c.argument('environment_name', options_list=['--environment'], help="The environment name.")
Expand All @@ -374,6 +379,9 @@ def load_arguments(self, _):
c.argument('name', name_type, id_part=None, help="The name of the Containerapp.")
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)

with self.argument_context('containerapp job registry') as c:
c.argument('identity', help="The managed identity with which to authenticate to the Azure Container Registry (instead of username/password). Use 'system' for a system-defined identity, Use 'system-environment' for an environment level system-defined identity or a resource id for a user-defined environment/containerapp level identity. The managed identity should have been assigned acrpull permissions on the ACR before deployment (use 'az role assignment create --role acrpull ...').")

with self.argument_context('containerapp env dotnet-component') as c:
c.argument('dotnet_component_name', options_list=['--name', '-n'], help="The DotNet component name.")
c.argument('environment_name', options_list=['--environment'], help="The environment name.")
Expand Down
25 changes: 24 additions & 1 deletion src/containerapp/azext_containerapp/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@
from azure.cli.core._profile import Profile
from azure.cli.core.azclierror import (ValidationError, ResourceNotFoundError, CLIError, InvalidArgumentValueError)
from azure.cli.core.commands.client_factory import get_mgmt_service_client, get_subscription_id
from azure.cli.command_modules.containerapp._utils import is_registry_msi_system

from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.servicelinker import ServiceLinkerManagementClient

from knack.log import get_logger
from msrestazure.tools import parse_resource_id, is_valid_resource_id

from ._managed_service_utils import ManagedRedisUtils, ManagedCosmosDBUtils, ManagedPostgreSQLFlexibleUtils, ManagedMySQLFlexibleUtils
from ._clients import ConnectedEnvCertificateClient, ContainerAppPreviewClient, JavaComponentPreviewClient
from ._clients import ConnectedEnvCertificateClient, ContainerAppPreviewClient, JavaComponentPreviewClient, ManagedEnvironmentPreviewClient
from ._client_factory import custom_location_client_factory, k8s_extension_client_factory, providers_client_factory, \
connected_k8s_client_factory, handle_non_404_status_code_exception
from ._models import OryxRunImageTagProperty
Expand Down Expand Up @@ -737,3 +739,24 @@ class AppType(Enum):
ContainerApp = 1
ContainerAppJob = 2
SessionPool = 3


def is_registry_msi_system_environment(identity):
if identity is None:
return False
return identity.lower() == "system-environment"


def env_has_managed_identity(cmd, resource_group_name, env_name, identity):
identity = identity.lower()
# caller should handle exception
managed_env_info = ManagedEnvironmentPreviewClient.show(cmd=cmd, resource_group_name=resource_group_name, name=env_name)
if is_registry_msi_system(identity) and managed_env_info["identity"]["type"].__contains__("SystemAssigned"):
return True

result = False
for msi in managed_env_info["identity"]["userAssignedIdentities"]:
if msi.lower() == identity:
result = True
break
return result
5 changes: 3 additions & 2 deletions src/containerapp/azext_containerapp/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from azure.cli.core.azclierror import (ValidationError, InvalidArgumentValueError,
MutuallyExclusiveArgumentError, RequiredArgumentMissingError)
from azure.cli.command_modules.containerapp._utils import is_registry_msi_system
from ._utils import is_registry_msi_system_environment

from ._constants import ACR_IMAGE_SUFFIX, \
CONNECTED_ENVIRONMENT_TYPE, \
Expand Down Expand Up @@ -44,8 +45,8 @@ def validate_create(registry_identity, registry_pass, registry_user, registry_se
raise MutuallyExclusiveArgumentError("Cannot provide both registry identity and username/password")
if is_registry_msi_system(registry_identity) and no_wait:
raise MutuallyExclusiveArgumentError("--no-wait is not supported with system registry identity")
if registry_identity and not is_valid_resource_id(registry_identity) and not is_registry_msi_system(registry_identity):
raise InvalidArgumentValueError("--registry-identity must be an identity resource ID or 'system'")
if registry_identity and not is_valid_resource_id(registry_identity) and not is_registry_msi_system(registry_identity) and not is_registry_msi_system_environment(registry_identity):
raise InvalidArgumentValueError("--registry-identity must be an identity resource ID or 'system' or 'system-environment'")
if registry_identity and ACR_IMAGE_SUFFIX not in (registry_server or ""):
raise InvalidArgumentValueError("--registry-identity: expected an ACR registry (*.azurecr.io) for --registry-server")

Expand Down
6 changes: 6 additions & 0 deletions src/containerapp/azext_containerapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ def load_command_table(self, args):
g.custom_command('update', 'update_containerappsjob', supports_no_wait=True, exception_handler=ex_handler_factory(), transform=transform_sensitive_values)
g.custom_command('delete', 'delete_containerappsjob', supports_no_wait=True, confirmation=True, exception_handler=ex_handler_factory())

with self.command_group('containerapp job registry', is_preview=True) as g:
g.custom_command('set', 'set_registry_job', exception_handler=ex_handler_factory())

with self.command_group('containerapp env certificate') as g:
g.custom_command('upload', 'upload_certificate')
g.custom_command('list', 'list_certificates')
Expand Down Expand Up @@ -94,6 +97,9 @@ def load_command_table(self, args):
g.custom_command('create', 'create_milvus_service', supports_no_wait=True)
g.custom_command('delete', 'delete_milvus_service', confirmation=True, supports_no_wait=True)

with self.command_group('containerapp registry', is_preview=True) as g:
g.custom_command('set', 'set_registry', exception_handler=ex_handler_factory())

with self.command_group('containerapp resiliency', is_preview=True) as g:
g.custom_command('create', 'create_container_app_resiliency', supports_no_wait=True, exception_handler=ex_handler_factory())
g.custom_show_command('update', 'update_container_app_resiliency', supports_no_wait=True, exception_handler=ex_handler_factory())
Expand Down
Loading

0 comments on commit 2c16b96

Please sign in to comment.