Skip to content
Closed
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
4 changes: 4 additions & 0 deletions src/acrtransfer/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Release History
===============
1.1.1b1
+++++++
* Add: Breaking change announcement for new required parameter --storage-access-mode for acr export-pipeline create and acr import-pipeline create commands.

1.1.0
++++++
* Add: New command acr pipeline-run clean - Bulk deletes failed pipeline-runs.
Expand Down
1 change: 1 addition & 0 deletions src/acrtransfer/azext_acrtransfer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from azure.cli.core import AzCommandsLoader
from azext_acrtransfer._help import helps # pylint: disable=unused-import
import azext_acrtransfer._breaking_change # pylint: disable=unused-import


class AcrtransferCommandsLoader(AzCommandsLoader):
Expand Down
17 changes: 17 additions & 0 deletions src/acrtransfer/azext_acrtransfer/_breaking_change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core.breaking_change import register_logic_breaking_change

register_logic_breaking_change('acr export-pipeline create', 'Add required parameter --storage-access-mode',
detail='A new required parameter `--storage-access-mode` will be added. '
'Allowed values: `entra-mi-auth`, `storage-sas-token`.',
doc_link="https://aka.ms/acr/transfer")


register_logic_breaking_change('acr import-pipeline create', 'Add required parameter --storage-access-mode',
detail='A new required parameter `--storage-access-mode` will be added. '
'Allowed values: `entra-mi-auth`, `storage-sas-token`.',
doc_link="https://aka.ms/acr/transfer")
2 changes: 1 addition & 1 deletion src/acrtransfer/azext_acrtransfer/_client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@

def cf_acrtransfer(cli_ctx, *_):
from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azext_acrtransfer.vendored_sdks.containerregistry.v2019_12_01_preview._container_registry_management_client import ContainerRegistryManagementClient
from azext_acrtransfer.vendored_sdks.containerregistry.v2025_06_01_preview._container_registry_management_client import ContainerRegistryManagementClient
return get_mgmt_service_client(cli_ctx, ContainerRegistryManagementClient)
24 changes: 16 additions & 8 deletions src/acrtransfer/azext_acrtransfer/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@
type: command
short-summary: Create an import pipeline.
examples:
- name: Create an import pipeline.
text: az acr import-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --secret-uri https://$MyKV.vault.azure.net/secrets/$MySecret --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer
- name: Create an import pipeline with a user-assigned identity, all available options, and source trigger disabled.
text: az acr import-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --secret-uri https://$MyKV.vault.azure.net/secrets/$MySecret --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer --options DeleteSourceBlobOnSuccess OverwriteTags ContinueOnErrors --assign-identity /subscriptions/$MySubID/resourceGroups/$MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$MyIdentity --source-trigger-enabled False
- name: Create an import pipeline with SAS token authentication.
text: az acr import-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --storage-access-mode storage-sas-token --secret-uri https://$MyKV.vault.azure.net/secrets/$MySecret --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer
- name: Create an import pipeline with system-assigned managed identity (automatic provisioning).
text: az acr import-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --storage-access-mode entra-mi-auth --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer
- name: Create an import pipeline with explicit system-assigned managed identity.
text: az acr import-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --storage-access-mode entra-mi-auth --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer --assign-identity [system]
- name: Create an import pipeline with user-assigned managed identity and all available options.
text: az acr import-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --storage-access-mode entra-mi-auth --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer --options DeleteSourceBlobOnSuccess OverwriteTags ContinueOnErrors --assign-identity /subscriptions/$MySubID/resourceGroups/$MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$MyIdentity --source-trigger-enabled False
"""

helps['acr import-pipeline list'] = """
Expand Down Expand Up @@ -60,10 +64,14 @@
type: command
short-summary: Create an export pipeline.
examples:
- name: Create an export pipeline.
text: az acr export-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --secret-uri https://$MyKV.vault.azure.net/secrets/$MySecret --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer
- name: Create an export pipeline with a user-assigned identity and all available options.
text: az acr export-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --secret-uri https://$MyKV.vault.azure.net/secrets/$MySecret --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer --options OverwriteBlobs ContinueOnErrors --assign-identity /subscriptions/$MySubID/resourceGroups/$MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$MyIdentity
- name: Create an export pipeline with SAS token authentication.
text: az acr export-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --storage-access-mode storage-sas-token --secret-uri https://$MyKV.vault.azure.net/secrets/$MySecret --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer
- name: Create an export pipeline with system-assigned managed identity (automatic provisioning).
text: az acr export-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --storage-access-mode entra-mi-auth --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer
- name: Create an export pipeline with explicit system-assigned managed identity.
text: az acr export-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --storage-access-mode entra-mi-auth --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer --assign-identity [system]
- name: Create an export pipeline with user-assigned managed identity and all available options.
text: az acr export-pipeline create --resource-group $MyRG --registry $MyReg --name $MyPipeline --storage-access-mode entra-mi-auth --storage-container-uri https://$MyStorage.blob.core.windows.net/$MyContainer --options OverwriteBlobs ContinueOnErrors --assign-identity /subscriptions/$MySubID/resourceGroups/$MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$MyIdentity
"""

helps['acr export-pipeline list'] = """
Expand Down
7 changes: 4 additions & 3 deletions src/acrtransfer/azext_acrtransfer/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# --------------------------------------------------------------------------------------------
# pylint: disable=line-too-long

from ._validators import validate_export_options, validate_import_options, validate_keyvault_secret_uri, validate_pipeline_type, validate_storage_account_container_uri, validate_user_assigned_identity_resource_id, validate_top
from ._validators import validate_export_options, validate_import_options, validate_keyvault_secret_uri, validate_pipeline_type, validate_storage_access_mode_and_secret_uri, validate_storage_account_container_uri, validate_user_assigned_identity_resource_id, validate_top


def load_arguments(self, _):
Expand All @@ -16,8 +16,9 @@ def load_arguments(self, _):
c.argument('location', validator=get_default_location_from_resource_group)
c.argument('registry_name', options_list=['--registry', '-r'], help='Name of registry.')
c.argument('storage_account_container_uri', options_list=['--storage-container-uri', '-c'], validator=validate_storage_account_container_uri, help='Storage account container URI of the source or target storage account container of the form https://$MyStorageAccount.blob.core.windows.net/$MyContainer. Note that the URI may be different outside of AzureCloud.')
c.argument('keyvault_secret_uri', options_list=['--secret-uri', '-s'], validator=validate_keyvault_secret_uri, help='Keyvault secret URI containing a valid SAS token to the associated storage account of the form https://$MyKeyvault.vault.azure.net/secrets/$MySecret. Note that the URI may be different outside of AzureCloud.')
c.argument('user_assigned_identity_resource_id', options_list=['--assign-identity', '-i'], validator=validate_user_assigned_identity_resource_id, help='User assigned identity resource ID of the form /subscriptions/$MySubID/resourceGroups/$MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$MyIdentity.')
c.argument('storage_access_mode', options_list=['--storage-access-mode', '-m'], validator=validate_storage_access_mode_and_secret_uri, help='Storage account access mode. Allowed values: entra-mi-auth, storage-sas-token. When using entra-mi-auth, a managed identity is required (use --assign-identity).')
c.argument('keyvault_secret_uri', options_list=['--secret-uri', '-s'], validator=validate_keyvault_secret_uri, help='Keyvault secret URI containing a valid SAS token to the associated storage account of the form https://$MyKeyvault.vault.azure.net/secrets/$MySecret. Note that the URI may be different outside of AzureCloud. Required when --storage-access-mode is storage-sas-token.')
c.argument('user_assigned_identity_resource_id', options_list=['--assign-identity', '-i'], validator=validate_user_assigned_identity_resource_id, help='Managed identity for the pipeline. Provide a user-assigned identity resource ID of the form /subscriptions/$MySubID/resourceGroups/$MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$MyIdentity, or use [system] to provision a system-assigned identity. When using --storage-access-mode entra-mi-auth, if not specified or if [system] is used, a system-assigned managed identity will be automatically provisioned.')

with self.argument_context('acr import-pipeline') as c:
c.argument('options', options_list=['--options', '-z'], nargs='+', validator=validate_import_options, help='Space-separated list of options. May only contain the following options: DeleteSourceBlobOnSuccess,OverwriteTags,ContinueOnErrors,DisableSourceTrigger.')
Expand Down
32 changes: 31 additions & 1 deletion src/acrtransfer/azext_acrtransfer/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,55 @@ def validate_keyvault_secret_uri(namespace):
uri = namespace.keyvault_secret_uri
valid = True

if uri is None:
return

if "https://" not in uri or "/secrets/" not in uri:
valid = False

if not valid:
logger.warning("Invalid keyvault secret URI. Please provide a keyvault secret URI of the form https://$MyKeyvault.vault.azure.net/secrets/$MySecret. Note - The exact URI form may be different outside of AzureCloud.")


def validate_storage_access_mode_and_secret_uri(namespace):
storage_access_mode = namespace.storage_access_mode
secret_uri = namespace.keyvault_secret_uri

allowed_modes = ["entra-mi-auth", "storage-sas-token"]

if storage_access_mode not in allowed_modes:
raise InvalidArgumentValueError(f"Invalid storage access mode '{storage_access_mode}'. Allowed values: {', '.join(allowed_modes)}")

# Convert CLI values to API values
if storage_access_mode == "entra-mi-auth":
namespace.storage_access_mode = "ManagedIdentity"
# Reject secret-uri when using Managed Identity mode
if secret_uri is not None:
raise InvalidArgumentValueError("The '--secret-uri' flag cannot be supplied when 'entra-mi-auth' is chosen for the flag '--storage-access-mode'.")
elif storage_access_mode == "storage-sas-token":
namespace.storage_access_mode = "SasToken"
# Require secret-uri when using SasToken mode
if secret_uri is None:
raise InvalidArgumentValueError("--secret-uri is required when --storage-access-mode is storage-sas-token")


def validate_user_assigned_identity_resource_id(namespace):
identity_id = namespace.user_assigned_identity_resource_id
valid = True

if identity_id is None:
return

# Handle [system] keyword for system-assigned identity
if identity_id.lower() == "[system]":
namespace.user_assigned_identity_resource_id = None
return

if "/providers/Microsoft.ManagedIdentity/userAssignedIdentities/" not in identity_id:
valid = False

if not valid:
logger.warning("Invalid user assigned identity resource ID. Please provide a user assigned identity resource ID of the form /subscriptions/$MySubID/resourceGroups/$MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$MyIdentity.")
logger.warning("Invalid user assigned identity resource ID. Please provide a user assigned identity resource ID of the form /subscriptions/$MySubID/resourceGroups/$MyRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$MyIdentity or use [system] for system-assigned identity.")


def validate_import_options(namespace):
Expand Down
12 changes: 6 additions & 6 deletions src/acrtransfer/azext_acrtransfer/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@

def load_command_table(self, _):
importpipeline_sdk = CliCommandType(
operations_tmpl='azext_acrtransfer.vendored_sdks.containerregistry.v2019_12_01_preview.operations#ImportPipelinesOperations.{}',
operations_tmpl='azext_acrtransfer.vendored_sdks.containerregistry.v2025_06_01_preview.operations#ImportPipelinesOperations.{}',
client_factory=cf_acrtransfer,
min_api='2019-12-01-preview'
min_api='2025-06-01-preview'
)

exportpipeline_sdk = CliCommandType(
operations_tmpl='azext_acrtransfer.vendored_sdks.containerregistry.v2019_12_01_preview.operations#ExportPipelinesOperations.{}',
operations_tmpl='azext_acrtransfer.vendored_sdks.containerregistry.v2025_06_01_preview.operations#ExportPipelinesOperations.{}',
client_factory=cf_acrtransfer,
min_api='2019-12-01-preview'
min_api='2025-06-01-preview'
)

pipelinerun_sdk = CliCommandType(
operations_tmpl='azext_acrtransfer.vendored_sdks.containerregistry.v2019_12_01_preview.operations#PipelineRunsOperations.{}',
operations_tmpl='azext_acrtransfer.vendored_sdks.containerregistry.v2025_06_01_preview.operations#PipelineRunsOperations.{}',
client_factory=cf_acrtransfer,
min_api='2019-12-01-preview'
min_api='2025-06-01-preview'
)

with self.command_group('acr import-pipeline', importpipeline_sdk, table_transformer=import_pipeline_output_format, is_preview=True) as g:
Expand Down
Loading
Loading