Skip to content

[ACR] az acr task create/update and az acr build/run: Add ABAC support for ACR Tasks #31069

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

Merged
merged 18 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,10 @@ def default_api_version(self):
'provider_operations_metadata': '2018-01-01-preview'
}),
ResourceType.MGMT_CONTAINERREGISTRY: SDKProfile('2025-03-01-preview', {
'agent_pools': '2019-06-01-preview',
'tasks': '2019-06-01-preview',
'task_runs': '2019-06-01-preview',
'runs': '2019-06-01-preview',
'agent_pools': '2025-03-01-preview',
'tasks': '2025-03-01-preview',
'task_runs': '2025-03-01-preview',
'runs': '2025-03-01-preview',
'network_rule': '2021-08-01-preview',
'cache_rules': '2023-01-01-preview',
'credential_sets': '2023-01-01-preview'
Expand Down
11 changes: 6 additions & 5 deletions src/azure-cli/azure/cli/command_modules/acr/_client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
VERSION_2022_02_01_PREVIEW = "2022-02-01-preview"
VERSION_2023_01_01_PREVIEW = "2023-01-01-preview"
VERSION_2024_11_01_PREVIEW = "2024-11-01-preview"
VERSION_2025_03_01_PREVIEW = "2025-03-01-preview"


def get_acr_service_client(cli_ctx, api_version=None):
Expand All @@ -38,7 +39,7 @@ def cf_acr_network_rules(cli_ctx, *_):


def cf_acr_registries_tasks(cli_ctx, *_):
return get_acr_service_client(cli_ctx, api_version=VERSION_2019_06_01_PREVIEW).registries
return get_acr_service_client(cli_ctx, api_version=VERSION_2025_03_01_PREVIEW).registries


def cf_acr_replications(cli_ctx, *_):
Expand All @@ -54,15 +55,15 @@ def cf_acr_private_endpoint_connections(cli_ctx, *_):


def cf_acr_tasks(cli_ctx, *_):
return get_acr_service_client(cli_ctx, VERSION_2019_06_01_PREVIEW).tasks
return get_acr_service_client(cli_ctx, VERSION_2025_03_01_PREVIEW).tasks


def cf_acr_taskruns(cli_ctx, *_):
return get_acr_service_client(cli_ctx, VERSION_2019_06_01_PREVIEW).task_runs
return get_acr_service_client(cli_ctx, VERSION_2025_03_01_PREVIEW).task_runs


def cf_acr_runs(cli_ctx, *_):
return get_acr_service_client(cli_ctx, VERSION_2019_06_01_PREVIEW).runs
return get_acr_service_client(cli_ctx, VERSION_2025_03_01_PREVIEW).runs


def cf_acr_scope_maps(cli_ctx, *_):
Expand All @@ -78,7 +79,7 @@ def cf_acr_token_credentials(cli_ctx, *_):


def cf_acr_agentpool(cli_ctx, *_):
return get_acr_service_client(cli_ctx, VERSION_2019_06_01_PREVIEW).agent_pools
return get_acr_service_client(cli_ctx, VERSION_2025_03_01_PREVIEW).agent_pools


def cf_acr_connected_registries(cli_ctx, *_):
Expand Down
24 changes: 23 additions & 1 deletion src/azure-cli/azure/cli/command_modules/acr/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
- name: Queue a local context as a Linux build on arm/v7 architecture, tag it, and push it to the registry.
text: >
az acr build -t sample/hello-world:{{.Run.ID}} -r myregistry . --platform linux/arm/v7
- name: Queue a local context as a Linux build, tag it, and push it to the ABAC-based Repository Permission enabled registry and use the caller's Entra identity to authenticate with the source registry.
text: >
az acr build -t sample/hello-world:{{.Run.ID}} -r myregistry . --source-acr-auth-id [caller]
"""

helps['acr check-health'] = """
Expand Down Expand Up @@ -812,6 +815,9 @@
- name: Queue a remote OCI Artifact context and runs the task.
text: >
az acr run -r myregistry oci://myregistry.azurecr.io/myartifact:mytag -f hello-world.yaml
- name: Queue a run to execute a container command in an ABAC-based Repository Permission enabled registry and use the caller's Entra identity to authenticate with the source registry.
text: >
az acr run -r myregistry --cmd '$Registry/myimage' /dev/null --source-acr-auth-id [caller]
"""

helps['acr scope-map'] = """
Expand Down Expand Up @@ -941,12 +947,22 @@
az acr task create -t acb:{{.Run.ID}} -n acb-win -r myregistry \\
-c https://github.com/Azure/acr-builder.git -f Windows.Dockerfile \\
--commit-trigger-enabled false --platform Windows/amd64
- name: Create a Linux multi-step task from a public GitHub repository with with both system-assigned and user-assigned managed identities and base image, git commit, pull request, and timer triggers that run the task at noon on Mondays through Fridays with the timer trigger name provided.
- name: Create a Linux multi-step task from a public GitHub repository with both system-assigned and user-assigned managed identities and base image, git commit, pull request, and timer triggers that run the task at noon on Mondays through Fridays with the timer trigger name provided.
text: |
az acr task create -t hello-world:{{.Run.ID}} -n hello-world -r myregistry \\
--pull-request-trigger-enabled true --schedule "dailyTimer:0 12 * * Mon-Fri" \\
-c https://github.com/Azure-Samples/acr-tasks.git#:multipleRegistries -f testtask.yaml \\
--assign-identity [system] "/subscriptions/<subscriptionId>/resourcegroups/<myResourceGroup>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<myUserAssignedIdentitiy>"
- name: Create a task without the source location in an ABAC-based Repository Permission registry and specify a system-assigned managed identity used for auth with the source registry.
text: >
az acr task create -n hello-world -r myregistry --cmd '$Registry/myimage' -c /dev/null --source-acr-auth-id [system]
- name: Create a task without the source location in an ABAC-based Repository Permission registry and specify a user-assigned managed identity used for auth with the source registry.
text: >
az acr task create -n hello-world -r myregistry --cmd '$Registry/myimage' -c /dev/null --source-acr-auth-id "/subscriptions/<subscriptionId>/resourcegroups/<myResourceGroup>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<myUserAssignedIdentitiy>"
- name: Create a task without the source location in an ABAC-based Repository Permission registry with both system-assigned and user-assigned managed identities, and specify the system-assigned managed identity used for auth with the source registry.
text: |
az acr task create -n hello-world -r myregistry --cmd '$Registry/myimage' -c /dev/null --source-acr-auth-id [system]
--assign-identity [system] "/subscriptions/<subscriptionId>/resourcegroups/<myResourceGroup>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<myUserAssignedIdentitiy>"
"""

helps['acr task credential'] = """
Expand Down Expand Up @@ -1220,6 +1236,12 @@
az acr task update --image MyImage --name MyTask --registry myregistry \\
--context https://github.com/Azure-Samples/acr-build-helloworld-node.git
crafted: true
- name: Update the task using the system-assigned managed identity for authentication with the source registry in Azure Container Registry.
text: >
az acr task update -n MyTask -r myregistry --source-acr-auth-id [system]
- name: Update the task using the user-assigned managed identity for authentication with the source registry in Azure Container Registry.
text: >
az acr task update -n MyTask -r myregistry --source-acr-auth-id "/subscriptions/<subscriptionId>/resourcegroups/<myResourceGroup>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<myUserAssignedIdentitiy>"
"""

helps['acr task update-run'] = """
Expand Down
10 changes: 8 additions & 2 deletions src/azure-cli/azure/cli/command_modules/acr/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
c.argument('set_secret', help="Secret value in '--set name[=value]' format. Multiples supported by passing --set multiple times.", action='append', validator=validate_set_secret)
c.argument('agent_pool_name', options_list=['--agent-pool'], help='The name of the agent pool.', is_preview=True)
c.argument('log_template', options_list=['--log-template'], help="The repository and tag template for run log artifact using the format: 'log/repo:tag' (e.g., 'acr/logs:{{.Run.ID}}'). Only applicable to CMK enabled registry.", is_preview=True)
c.argument('source_acr_auth_id', is_preview=True, arg_type=get_enum_type(["[caller]", "none"]), help="Assign the identity used for source registry login. Use '[caller]' for caller identity.")

with self.argument_context('acr pack build') as c:
c.argument('registry_name', options_list=['--registry', '-r'])
Expand All @@ -334,6 +335,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
c.argument('secret_arg', options_list=['--secret-build-arg'], help="Secret build argument in '--secret-build-arg name[=value]' format. Multiples are supported by passing '--secret-build-arg name[=value]' multiple times. This parameter value is not surfaced to the ACR team and is more suitable for sensitive information.", action='append', validator=validate_secret_arg)
c.argument('agent_pool_name', options_list=['--agent-pool'], help='The name of the agent pool.', is_preview=True)
c.argument('log_template', options_list=['--log-template'], help="The repository and tag template for run log artifact using the format: 'log/repo:tag' (e.g., 'acr/logs:{{.Run.ID}}'). Only applicable to CMK enabled registry.", is_preview=True)
c.argument('source_acr_auth_id', is_preview=True, arg_type=get_enum_type(["[caller]", "none"]), help="Assign the identity used for source registry login. Use '[caller]' for caller identity.")

with self.argument_context('acr task') as c:
c.argument('registry_name', options_list=['--registry', '-r'])
Expand Down Expand Up @@ -376,16 +378,20 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
c.argument('cpu', type=int, help='The CPU configuration in terms of number of cores required for the run.')

# MSI parameter
c.argument('assign_identity', nargs='*', help="Assigns managed identities to the task. Use '[system]' to refer to the system-assigned identity or a resource ID to refer to a user-assigned identity. Please see https://aka.ms/acr/tasks/task-create-managed-identity for more information.")
c.argument('assign_identity', nargs='*', help="Assign managed identities to the task. Use '[system]' to refer to the system-assigned identity or a resource ID to refer to a user-assigned identity. Please see https://aka.ms/acr/tasks/task-create-managed-identity for more information.")

# Agent Pool Parameter
c.argument('agent_pool_name', options_list=['--agent-pool'], help='The name of the agent pool.', is_preview=True)

with self.argument_context('acr task create') as c:
c.argument('task_name', completer=None)
c.argument('source_acr_auth_id', is_preview=True, help="Assign the managed identity used for source registry login. Use '[system]' to refer to the system-assigned identity or a resource ID to refer to a user-assigned managed identity.")

with self.argument_context('acr task update') as c:
c.argument('source_acr_auth_id', is_preview=True, help="Assign the managed identity used for source registry login. Use '[system]' to refer to the system-assigned identity or a resource ID to refer to a user-assigned managed identity.")

with self.argument_context('acr task identity') as c:
c.argument('identities', options_list=['--identities'], nargs='*', help="Assigns managed identities to the task. Use '[system]' to refer to the system-assigned identity or a resource ID to refer to a user-assigned identity.")
c.argument('identities', options_list=['--identities'], nargs='*', help="Assign managed identities to the task. Use '[system]' to refer to the system-assigned identity or a resource ID to refer to a user-assigned identity.")

with self.argument_context('acr task credential') as c:
# Custom registry credentials
Expand Down
57 changes: 48 additions & 9 deletions src/azure-cli/azure/cli/command_modules/acr/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@

from ._archive_utils import upload_source_code, check_remote_source_code

CALLER_IDENTITY_ALIAS = '[caller]'
SYSTEM_ASSIGNED_IDENTITY_ALIAS = '[system]'

logger = get_logger(__name__)


Expand Down Expand Up @@ -278,30 +281,59 @@ def get_yaml_template(cmd_value, timeout, file):
return yaml_template


def get_custom_registry_credentials(cmd,
auth_mode=None,
login_server=None,
username=None,
password=None,
identity=None,
is_remove=False):
def get_source_and_custom_registry_credentials(cmd,
auth_mode=None,
login_server=None,
username=None,
password=None,
identity=None,
is_remove=False,
source_acr_auth_id=None,
registry_abac_enabled=False,
deprecate_auth_mode=False):
"""Get the credential object from the input
:param str auth_mode: The login mode for the source registry
:param str login_server: The login server of custom registry
:param str username: The username for custom registry (plain text or a key vault secret URI)
:param str password: The password for custom registry (plain text or a key vault secret URI)
:param str identity: The task managed identity used for the credential
:param str source_acr_auth_id: the managed identity used for the source registry authentication
:param bool registry_abac_enabled: whether the registry is ABAC-enabled
:param bool deprecate_auth_mode: whether to print the auth mode deprecation warning
"""
Credentials, CustomRegistryCredentials, SourceRegistryCredentials, SecretObject, \
SecretObjectType = cmd.get_models(
'Credentials', 'CustomRegistryCredentials', 'SourceRegistryCredentials', 'SecretObject',
'SecretObjectType',
operation_group='tasks')

if deprecate_auth_mode:
check_auth_mode_for_abac(registry_abac_enabled, auth_mode)

source_registry_identity = None
if source_acr_auth_id:
# "Default" and "None" are the allowed values for source registry auth mode.
# For a non-ABAC-enabled registry, "--source-acr-auth-id" will not take effect, and authentication
# will fail if the auth mode is "None". Therefore, we need to throw an error here.
if not registry_abac_enabled and auth_mode and auth_mode.lower() == "none":
raise CLIError('Error: Conflicting Authentication Parameters for Task Access to Source Registry. Task '
'authentication mode for source registry access is set to "None", but an identity was '
'provided for authentication. Remove the identity or update the authentication mode to '
'resolve this conflict.')

if source_acr_auth_id.lower() == "none":
source_registry_identity = None
elif source_acr_auth_id.startswith('/subscriptions/'): # user-assigned MI resource ID
source_registry_identity = resolve_identity_client_id(cmd.cli_ctx, source_acr_auth_id)
elif source_acr_auth_id == CALLER_IDENTITY_ALIAS or source_acr_auth_id == SYSTEM_ASSIGNED_IDENTITY_ALIAS:
source_registry_identity = source_acr_auth_id
else:
raise CLIError('Error: Invalid value for --source-acr-auth-id.')

source_registry_credentials = None
if auth_mode:
if auth_mode or source_registry_identity:
source_registry_credentials = SourceRegistryCredentials(
login_mode=auth_mode)
login_mode=auth_mode, identity=source_registry_identity)

custom_registries = None
if login_server:
Expand Down Expand Up @@ -606,3 +638,10 @@ def get_task_details_by_name(cli_ctx, resource_group_name, registry_name, task_n
from ._client_factory import cf_acr_tasks
client = cf_acr_tasks(cli_ctx)
return client.get_details(resource_group_name, registry_name, task_name)


def check_auth_mode_for_abac(registry_abac_enabled, auth_mode):
if registry_abac_enabled and auth_mode is not None:
logger.warning("The --auth-mode flag is deprecated for specifying access to an ABAC-enabled source registry. "
"Please use --source-acr-auth-id to specify an Entra identity for use in accessing an "
"ABAC-enabled source registry.")
24 changes: 15 additions & 9 deletions src/azure-cli/azure/cli/command_modules/acr/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@

from knack.log import get_logger
from knack.util import CLIError
from azure.cli.core.commands import LongRunningOperation

from ._utils import validate_managed_registry, get_validate_platform, get_custom_registry_credentials
from ._utils import validate_managed_registry, get_validate_platform, get_source_and_custom_registry_credentials
from ._stream_utils import stream_logs
from ._archive_utils import upload_source_code, check_remote_source_code

Expand All @@ -40,8 +39,9 @@ def acr_build(cmd, # pylint: disable=too-many-locals
platform=None,
target=None,
auth_mode=None,
log_template=None):
_, resource_group_name = validate_managed_registry(
log_template=None,
source_acr_auth_id=None):
registry, resource_group_name = validate_managed_registry(
cmd, registry_name, resource_group_name, BUILD_NOT_SUPPORTED)

from ._client_factory import cf_acr_registries_tasks
Expand Down Expand Up @@ -97,11 +97,14 @@ def acr_build(cmd, # pylint: disable=too-many-locals

platform_os, platform_arch, platform_variant = get_validate_platform(cmd, platform)

DockerBuildRequest, PlatformProperties = cmd.get_models(
DockerBuildRequest, PlatformProperties, RoleAssignmentMode = cmd.get_models(
'DockerBuildRequest',
'PlatformProperties',
'RoleAssignmentMode',
operation_group='runs')

registry_abac_enabled = registry.role_assignment_mode == RoleAssignmentMode.ABAC_REPOSITORY_PERMISSIONS

docker_build_request = DockerBuildRequest(
agent_pool_name=agent_pool_name,
image_names=image_names,
Expand All @@ -116,17 +119,20 @@ def acr_build(cmd, # pylint: disable=too-many-locals
timeout=timeout,
arguments=(arg if arg else []) + (secret_arg if secret_arg else []),
target=target,
credentials=get_custom_registry_credentials(
credentials=get_source_and_custom_registry_credentials(
cmd=cmd,
auth_mode=auth_mode
auth_mode=auth_mode,
source_acr_auth_id=source_acr_auth_id,
registry_abac_enabled=registry_abac_enabled,
deprecate_auth_mode=True
),
log_template=log_template
)

queued = LongRunningOperation(cmd.cli_ctx)(client_registries.begin_schedule_run(
queued = client_registries.schedule_run(
resource_group_name=resource_group_name,
registry_name=registry_name,
run_request=docker_build_request))
run_request=docker_build_request)

run_id = queued.run_id
logger.warning("Queued a build with ID: %s", run_id)
Expand Down
9 changes: 4 additions & 5 deletions src/azure-cli/azure/cli/command_modules/acr/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
import base64
from knack.log import get_logger
from knack.util import CLIError
from azure.cli.core.commands import LongRunningOperation

from ._constants import ACR_CACHED_BUILDER_IMAGES
from ._stream_utils import stream_logs
from ._utils import (
get_registry_by_name,
get_validate_platform,
get_custom_registry_credentials
get_source_and_custom_registry_credentials
)
from ._client_factory import cf_acr_registries_tasks
from .run import prepare_source_location
Expand Down Expand Up @@ -88,17 +87,17 @@ def acr_pack_build(cmd, # pylint: disable=too-many-locals
architecture=platform_arch,
variant=platform_variant
),
credentials=get_custom_registry_credentials(
credentials=get_source_and_custom_registry_credentials(
cmd=cmd,
auth_mode=auth_mode
),
agent_pool_name=agent_pool_name
)

queued = LongRunningOperation(cmd.cli_ctx)(client_registries.begin_schedule_run(
queued = client_registries.schedule_run(
resource_group_name=resource_group_name,
registry_name=registry_name,
run_request=request))
run_request=request)

run_id = queued.run_id
logger.warning('Queued a run with ID: %s', run_id)
Expand Down
Loading