Skip to content

Commit 7802f06

Browse files
committed
[ACR] az acr tasks create | update, az acr build | run: Add ABAC support for ACR Tasks
1 parent 29a241f commit 7802f06

File tree

15 files changed

+151
-79
lines changed

15 files changed

+151
-79
lines changed

src/azure-cli-core/azure/cli/core/profiles/_shared.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,11 @@ def default_api_version(self):
195195
'role_definitions': '2022-05-01-preview',
196196
'provider_operations_metadata': '2018-01-01-preview'
197197
}),
198-
ResourceType.MGMT_CONTAINERREGISTRY: SDKProfile('2023-11-01-preview', {
199-
'agent_pools': '2019-06-01-preview',
200-
'tasks': '2019-06-01-preview',
201-
'task_runs': '2019-06-01-preview',
202-
'runs': '2019-06-01-preview',
198+
ResourceType.MGMT_CONTAINERREGISTRY: SDKProfile('2025-03-01-preview', {
199+
'agent_pools': '2025-03-01-preview',
200+
'tasks': '2025-03-01-preview',
201+
'task_runs': '2025-03-01-preview',
202+
'runs': '2025-03-01-preview',
203203
'network_rule': '2021-08-01-preview',
204204
'cache_rules': '2023-01-01-preview',
205205
'credential_sets': '2023-01-01-preview'
@@ -439,6 +439,7 @@ def default_api_version(self):
439439
'VERSION_2021_08_01_PREVIEW': "2021-08-01-preview",
440440
'VERSION_2022_02_01_PREVIEW': "2022-02-01-preview",
441441
'VERSION_2023_11_01_PREVIEW': "2023-11-01-preview",
442+
'VERSION_2025_03_01_PREVIEW': "2025-03-01-preview",
442443
},
443444
ResourceType.MGMT_CONTAINERSERVICE: {
444445
# src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py:50

src/azure-cli/azure/cli/command_modules/acr/_client_factory.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
VERSION_2021_08_01_PREVIEW = "2021-08-01-preview"
1212
VERSION_2022_02_01_PREVIEW = "2022-02-01-preview"
1313
VERSION_2023_01_01_PREVIEW = "2023-01-01-preview"
14+
VERSION_2025_03_01_PREVIEW = "2025-03-01-preview"
1415

1516

1617
def get_acr_service_client(cli_ctx, api_version=None):
@@ -37,7 +38,7 @@ def cf_acr_network_rules(cli_ctx, *_):
3738

3839

3940
def cf_acr_registries_tasks(cli_ctx, *_):
40-
return get_acr_service_client(cli_ctx, api_version=VERSION_2019_06_01_PREVIEW).registries
41+
return get_acr_service_client(cli_ctx, api_version=VERSION_2025_03_01_PREVIEW).registries
4142

4243

4344
def cf_acr_replications(cli_ctx, *_):
@@ -53,15 +54,15 @@ def cf_acr_private_endpoint_connections(cli_ctx, *_):
5354

5455

5556
def cf_acr_tasks(cli_ctx, *_):
56-
return get_acr_service_client(cli_ctx, VERSION_2019_06_01_PREVIEW).tasks
57+
return get_acr_service_client(cli_ctx, VERSION_2025_03_01_PREVIEW).tasks
5758

5859

5960
def cf_acr_taskruns(cli_ctx, *_):
60-
return get_acr_service_client(cli_ctx, VERSION_2019_06_01_PREVIEW).task_runs
61+
return get_acr_service_client(cli_ctx, VERSION_2025_03_01_PREVIEW).task_runs
6162

6263

6364
def cf_acr_runs(cli_ctx, *_):
64-
return get_acr_service_client(cli_ctx, VERSION_2019_06_01_PREVIEW).runs
65+
return get_acr_service_client(cli_ctx, VERSION_2025_03_01_PREVIEW).runs
6566

6667

6768
def cf_acr_scope_maps(cli_ctx, *_):
@@ -77,7 +78,7 @@ def cf_acr_token_credentials(cli_ctx, *_):
7778

7879

7980
def cf_acr_agentpool(cli_ctx, *_):
80-
return get_acr_service_client(cli_ctx, VERSION_2019_06_01_PREVIEW).agent_pools
81+
return get_acr_service_client(cli_ctx, VERSION_2025_03_01_PREVIEW).agent_pools
8182

8283

8384
def cf_acr_connected_registries(cli_ctx, *_):

src/azure-cli/azure/cli/command_modules/acr/_help.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
- name: Queue a local context as a Linux build on arm/v7 architecture, tag it, and push it to the registry.
3838
text: >
3939
az acr build -t sample/hello-world:{{.Run.ID}} -r myregistry . --platform linux/arm/v7
40+
- 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.
41+
text: >
42+
az acr build -t sample/hello-world:{{.Run.ID}} -r myregistry . --source-registry-auth-id [caller]
4043
"""
4144

4245
helps['acr check-health'] = """
@@ -806,6 +809,9 @@
806809
- name: Queue a remote OCI Artifact context and runs the task.
807810
text: >
808811
az acr run -r myregistry oci://myregistry.azurecr.io/myartifact:mytag -f hello-world.yaml
812+
- 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.
813+
text: >
814+
az acr run -r myregistry --cmd '$Registry/myimage' /dev/null --source-registry-auth-id [caller]
809815
"""
810816

811817
helps['acr scope-map'] = """
@@ -938,6 +944,12 @@
938944
--pull-request-trigger-enabled true --schedule "dailyTimer:0 12 * * Mon-Fri" \\
939945
-c https://github.com/Azure-Samples/acr-tasks.git#:multipleRegistries -f testtask.yaml \\
940946
--assign-identity [system] "/subscriptions/<subscriptionId>/resourcegroups/<myResourceGroup>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<myUserAssignedIdentitiy>"
947+
- name: Create a task without the source location in an ABAC-based Repository Permission registry and specify a system-assigned MI used for auth with the source registry.
948+
text: >
949+
az acr task create -n hello-world -r myregistry --cmd '$Registry/myimage' -c /dev/null --source-registry-auth-id [system]
950+
- name: Create a task without the source location in an ABAC-based Repository Permission registry and specify a user-assigned MI used for auth with the source registry.
951+
text: >
952+
az acr task create -n hello-world -r myregistry --cmd '$Registry/myimage' -c /dev/null --source-registry-auth-id 00000000-0000-0000-0000-000000000000
941953
"""
942954

943955
helps['acr task credential'] = """

src/azure-cli/azure/cli/command_modules/acr/_params.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
306306
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)
307307
c.argument('agent_pool_name', options_list=['--agent-pool'], help='The name of the agent pool.', is_preview=True)
308308
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)
309+
c.argument('source_registry_auth_id', arg_type=get_enum_type(["[caller]", "none"]), help="Assigns the identity used for source registry login. Use '[caller]' for caller identity.")
309310

310311
with self.argument_context('acr pack build') as c:
311312
c.argument('registry_name', options_list=['--registry', '-r'])
@@ -325,6 +326,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
325326
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)
326327
c.argument('agent_pool_name', options_list=['--agent-pool'], help='The name of the agent pool.', is_preview=True)
327328
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)
329+
c.argument('source_registry_auth_id', arg_type=get_enum_type(["[caller]", "none"]), help="Assigns the identity used for source registry login. Use '[caller]' for caller identity.")
328330

329331
with self.argument_context('acr task') as c:
330332
c.argument('registry_name', options_list=['--registry', '-r'])
@@ -374,6 +376,10 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
374376

375377
with self.argument_context('acr task create') as c:
376378
c.argument('task_name', completer=None)
379+
c.argument('source_registry_auth_id', help="Assigns the managed identity used for source registry login. Use '[system]' to refer to the system-assigned identity or a client ID to refer to a user-assigned managed identity.")
380+
381+
with self.argument_context('acr task update') as c:
382+
c.argument('source_registry_auth_id', help="Assigns the managed identity used for source registry login. Use '[system]' to refer to the system-assigned identity or a client ID to refer to a user-assigned managed identity.")
377383

378384
with self.argument_context('acr task identity') as c:
379385
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.")

src/azure-cli/azure/cli/command_modules/acr/_utils.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,24 +281,37 @@ def get_custom_registry_credentials(cmd,
281281
username=None,
282282
password=None,
283283
identity=None,
284-
is_remove=False):
284+
is_remove=False,
285+
source_registry_auth_id=None,
286+
registry_abac_enabled=False):
285287
"""Get the credential object from the input
286288
:param str auth_mode: The login mode for the source registry
287289
:param str login_server: The login server of custom registry
288290
:param str username: The username for custom registry (plain text or a key vault secret URI)
289291
:param str password: The password for custom registry (plain text or a key vault secret URI)
290292
:param str identity: The task managed identity used for the credential
293+
:param str source_registry_auth_id: the managed identity used for the source registry authentication
294+
:param bool registry_abac_enabled: whether the registry is ABAC-enabled
291295
"""
292296
Credentials, CustomRegistryCredentials, SourceRegistryCredentials, SecretObject, \
293297
SecretObjectType = cmd.get_models(
294298
'Credentials', 'CustomRegistryCredentials', 'SourceRegistryCredentials', 'SecretObject',
295299
'SecretObjectType',
296300
operation_group='tasks')
297301

302+
# "Default" and "None" are the allowed values for source registry auth mode.
303+
# For a non-ABAC-enabled registry, "--source-registry-auth-id" will not take effect, and authentication
304+
# will fail if the auth mode is "None". Therefore, we need to throw an error here.
305+
if not registry_abac_enabled and auth_mode and auth_mode.lower() == "none" and source_registry_auth_id:
306+
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.')
307+
298308
source_registry_credentials = None
309+
# Need to differentiate between the string "None" and the None value for auth_mode
299310
if auth_mode:
300311
source_registry_credentials = SourceRegistryCredentials(
301-
login_mode=auth_mode)
312+
login_mode=auth_mode, identity=source_registry_auth_id)
313+
elif source_registry_auth_id: # auth_mode is None at this point
314+
source_registry_credentials = SourceRegistryCredentials(identity=source_registry_auth_id)
302315

303316
custom_registries = None
304317
if login_server:
@@ -603,3 +616,7 @@ def get_task_details_by_name(cli_ctx, resource_group_name, registry_name, task_n
603616
from ._client_factory import cf_acr_tasks
604617
client = cf_acr_tasks(cli_ctx)
605618
return client.get_details(resource_group_name, registry_name, task_name)
619+
620+
def check_auth_mode_for_abac(registry_abac_enabled, auth_mode):
621+
if registry_abac_enabled and auth_mode is not None:
622+
logger.warning("The --auth-mode flag is deprecated for specifying access to an ABAC-enabled source registry. Please use --source-registry-auth-id to specify an Entra identity for use in accessing an ABAC-enabled source registry." )

src/azure-cli/azure/cli/command_modules/acr/build.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from knack.util import CLIError
1414
from azure.cli.core.commands import LongRunningOperation
1515

16-
from ._utils import validate_managed_registry, get_validate_platform, get_custom_registry_credentials
16+
from ._utils import check_auth_mode_for_abac, validate_managed_registry, get_validate_platform, get_custom_registry_credentials
1717
from ._stream_utils import stream_logs
1818
from ._archive_utils import upload_source_code, check_remote_source_code
1919

@@ -40,8 +40,9 @@ def acr_build(cmd, # pylint: disable=too-many-locals
4040
platform=None,
4141
target=None,
4242
auth_mode=None,
43-
log_template=None):
44-
_, resource_group_name = validate_managed_registry(
43+
log_template=None,
44+
source_registry_auth_id=None):
45+
registry, resource_group_name = validate_managed_registry(
4546
cmd, registry_name, resource_group_name, BUILD_NOT_SUPPORTED)
4647

4748
from ._client_factory import cf_acr_registries_tasks
@@ -97,10 +98,14 @@ def acr_build(cmd, # pylint: disable=too-many-locals
9798

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

100-
DockerBuildRequest, PlatformProperties = cmd.get_models(
101+
DockerBuildRequest, PlatformProperties, RoleAssignmentMode = cmd.get_models(
101102
'DockerBuildRequest',
102103
'PlatformProperties',
104+
'RoleAssignmentMode',
103105
operation_group='runs')
106+
107+
registry_abac_enabled = registry.role_assignment_mode == RoleAssignmentMode.ABAC_REPOSITORY_PERMISSIONS
108+
check_auth_mode_for_abac(registry_abac_enabled, auth_mode)
104109

105110
docker_build_request = DockerBuildRequest(
106111
agent_pool_name=agent_pool_name,
@@ -118,15 +123,17 @@ def acr_build(cmd, # pylint: disable=too-many-locals
118123
target=target,
119124
credentials=get_custom_registry_credentials(
120125
cmd=cmd,
121-
auth_mode=auth_mode
126+
auth_mode=auth_mode,
127+
source_registry_auth_id=source_registry_auth_id,
128+
registry_abac_enabled=registry_abac_enabled
122129
),
123130
log_template=log_template
124131
)
125132

126-
queued = LongRunningOperation(cmd.cli_ctx)(client_registries.begin_schedule_run(
133+
queued = client_registries.schedule_run(
127134
resource_group_name=resource_group_name,
128135
registry_name=registry_name,
129-
run_request=docker_build_request))
136+
run_request=docker_build_request)
130137

131138
run_id = queued.run_id
132139
logger.warning("Queued a build with ID: %s", run_id)

src/azure-cli/azure/cli/command_modules/acr/pack.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ def acr_pack_build(cmd, # pylint: disable=too-many-locals
9595
agent_pool_name=agent_pool_name
9696
)
9797

98-
queued = LongRunningOperation(cmd.cli_ctx)(client_registries.begin_schedule_run(
98+
queued = client_registries.schedule_run(
9999
resource_group_name=resource_group_name,
100100
registry_name=registry_name,
101-
run_request=request))
101+
run_request=request)
102102

103103
run_id = queued.run_id
104104
logger.warning('Queued a run with ID: %s', run_id)

src/azure-cli/azure/cli/command_modules/acr/run.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from ._constants import ACR_TASK_YAML_DEFAULT_NAME
1111
from ._stream_utils import stream_logs
1212
from ._utils import (
13+
check_auth_mode_for_abac,
1314
validate_managed_registry,
1415
get_validate_platform,
1516
get_custom_registry_credentials,
@@ -40,9 +41,10 @@ def acr_run(cmd, # pylint: disable=too-many-locals
4041
resource_group_name=None,
4142
platform=None,
4243
auth_mode=None,
43-
log_template=None):
44+
log_template=None,
45+
source_registry_auth_id=None):
4446

45-
_, resource_group_name = validate_managed_registry(
47+
registry, resource_group_name = validate_managed_registry(
4648
cmd, registry_name, resource_group_name, RUN_NOT_SUPPORTED)
4749

4850
if cmd_value and file:
@@ -57,8 +59,11 @@ def acr_run(cmd, # pylint: disable=too-many-locals
5759

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

60-
EncodedTaskRunRequest, FileTaskRunRequest, PlatformProperties = cmd.get_models(
61-
'EncodedTaskRunRequest', 'FileTaskRunRequest', 'PlatformProperties', operation_group='runs')
62+
EncodedTaskRunRequest, FileTaskRunRequest, PlatformProperties, RoleAssignmentMode = cmd.get_models(
63+
'EncodedTaskRunRequest', 'FileTaskRunRequest', 'PlatformProperties', 'RoleAssignmentMode', operation_group='runs')
64+
65+
registry_abac_enabled = registry.role_assignment_mode == RoleAssignmentMode.ABAC_REPOSITORY_PERMISSIONS
66+
check_auth_mode_for_abac(registry_abac_enabled, auth_mode)
6267

6368
if source_location:
6469
request = FileTaskRunRequest(
@@ -74,7 +79,9 @@ def acr_run(cmd, # pylint: disable=too-many-locals
7479
),
7580
credentials=get_custom_registry_credentials(
7681
cmd=cmd,
77-
auth_mode=auth_mode
82+
auth_mode=auth_mode,
83+
source_registry_auth_id=source_registry_auth_id,
84+
registry_abac_enabled=registry_abac_enabled
7885
),
7986
agent_pool_name=agent_pool_name,
8087
log_template=log_template
@@ -94,16 +101,18 @@ def acr_run(cmd, # pylint: disable=too-many-locals
94101
),
95102
credentials=get_custom_registry_credentials(
96103
cmd=cmd,
97-
auth_mode=auth_mode
104+
auth_mode=auth_mode,
105+
source_registry_auth_id=source_registry_auth_id,
106+
registry_abac_enabled=registry_abac_enabled
98107
),
99108
agent_pool_name=agent_pool_name,
100109
log_template=log_template
101110
)
102111

103-
queued = LongRunningOperation(cmd.cli_ctx)(client_registries.begin_schedule_run(
112+
queued = client_registries.schedule_run(
104113
resource_group_name=resource_group_name,
105114
registry_name=registry_name,
106-
run_request=request))
115+
run_request=request)
107116

108117
run_id = queued.run_id
109118
logger.warning("Queued a run with ID: %s", run_id)

0 commit comments

Comments
 (0)