Skip to content

Conversation

@jcassanji-southworks
Copy link
Contributor

Related command
az keyvault secret copy

Description
This PR introduces a new command az keyvault secret copy to simplify the process of copying secrets from one Key Vault to another. It supports copying individual secrets or all secrets in bulk, with options to control overwrite behavior and preserve metadata.

Motivation and Benefits
Currently, users needing to migrate or replicate secrets between Key Vaults (e.g., promoting from Dev to Prod, or replicating for DR) must write complex scripts. This new command standardizes this workflow into a single CLI operation.

  • Productivity: Reduces multi-step scripting to a single command.
  • Accuracy: Ensures all metadata (tags, content-type) is preserved.
  • Safety: Built-in protection against accidental overwrites and automatic filtering of Azure-managed secrets.

Implementation Details

  • Registered copy command under keyvault secret group.
  • Implemented core logic using azure-keyvault-secrets track2 SDK.
  • Added comprehensive unit tests covering single copy, bulk copy, overwrite protection, and metadata preservation.

Testing Guide

  1. Copy a single secret:

    az keyvault secret copy --source-vault <SourceKV> --destination-vault <DestKV> --name <SecretName>
  2. Copy all secrets:

    az keyvault secret copy --source-vault <SourceKV> --destination-vault <DestKV> --all
  3. Force copy (overwrite existing):

    az keyvault secret copy --source-vault <SourceKV> --destination-vault <DestKV> --name <SecretName> --overwrite

History Notes
[KeyVault] az keyvault secret copy: Add new command to copy secrets between Key Vaults


This checklist is used to make sure that common guidelines for a pull request are followed.

jcassanji-southworks and others added 24 commits February 4, 2026 19:24
…test_keyvault_commands.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…test_keyvault_commands.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 5, 2026 22:05
@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Feb 5, 2026

❌AzureCLI-FullTest
️✔️acr
️✔️latest
️✔️3.12
️✔️3.13
️✔️acs
️✔️latest
️✔️3.12
️✔️3.13
️✔️advisor
️✔️latest
️✔️3.12
️✔️3.13
️✔️ams
️✔️latest
️✔️3.12
️✔️3.13
️✔️apim
️✔️latest
️✔️3.12
️✔️3.13
️✔️appconfig
️✔️latest
️✔️3.12
️✔️3.13
️✔️appservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️aro
️✔️latest
️✔️3.12
️✔️3.13
️✔️backup
️✔️latest
️✔️3.12
️✔️3.13
️✔️batch
️✔️latest
️✔️3.12
️✔️3.13
️✔️batchai
️✔️latest
️✔️3.12
️✔️3.13
️✔️billing
️✔️latest
️✔️3.12
️✔️3.13
️✔️botservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️cdn
️✔️latest
️✔️3.12
️✔️3.13
️✔️cloud
️✔️latest
️✔️3.12
️✔️3.13
️✔️cognitiveservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️compute_recommender
️✔️latest
️✔️3.12
️✔️3.13
️✔️computefleet
️✔️latest
️✔️3.12
️✔️3.13
️✔️config
️✔️latest
️✔️3.12
️✔️3.13
️✔️configure
️✔️latest
️✔️3.12
️✔️3.13
️✔️consumption
️✔️latest
️✔️3.12
️✔️3.13
️✔️container
️✔️latest
️✔️3.12
️✔️3.13
️✔️containerapp
️✔️latest
️✔️3.12
️✔️3.13
️✔️core
️✔️latest
️✔️3.12
️✔️3.13
️✔️cosmosdb
️✔️latest
️✔️3.12
️✔️3.13
️✔️databoxedge
️✔️latest
️✔️3.12
️✔️3.13
️✔️dls
️✔️latest
️✔️3.12
️✔️3.13
️✔️dms
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventgrid
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventhubs
️✔️latest
️✔️3.12
️✔️3.13
️✔️feedback
️✔️latest
️✔️3.12
️✔️3.13
️✔️find
️✔️latest
️✔️3.12
️✔️3.13
️✔️hdinsight
️✔️latest
️✔️3.12
️✔️3.13
️✔️identity
️✔️latest
️✔️3.12
️✔️3.13
️✔️iot
️✔️latest
️✔️3.12
️✔️3.13
❌keyvault
❌latest
❌3.12
Type Test Case Error Message Line
Failed test_keyvault_secret_copy self = <azure.cli.testsdk.base.ExecutionResult object at 0x7f0defa09d00>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7f0df5276030>
command = 'group create --location westus --name cli_test_keyvault_copy2pkat2oz7meyaluj4jxweykflxgf5ltkmwogmzy3zw5cttjbkd2kz --tag product=azurecli cause=automation test date=2026-02-05T22:44:58Z test=test_keyvault_secret_copy module=keyvault'
expect_failure = False

    def in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
>           self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli-testsdk/azure/cli/testsdk/base.py:303: 
                                        
env/lib/python3.12/site-packages/knack/cli.py:245: in invoke
    exit_code = self.exception_handler(ex)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/init.py:135: in exception_handler
    return handle_exception(ex)
           ^^^^^^^^^^^^^^^^^^^^
                                        

ex = CLIError("Please run 'az login' to setup account."), args = (), kwargs = {}

    def handle_main_exception(ex, *args, **kwargs):  # pylint: disable=unused-argument
        if isinstance(ex, CannotOverwriteExistingCassetteException):
            # This exception usually caused by a no match HTTP request. This is a product error
            # that is caused by change of SDK invocation.
            raise ex
    
>       raise CliExecutionError(ex)
E       azure.cli.testsdk.exceptions.CliExecutionError: The CLI throws exception CLIError during execution and fails the command.

src/azure-cli-testsdk/azure/cli/testsdk/patches.py:35: CliExecutionError

During handling of the above exception, another exception occurred:
src/azure-cli-testsdk/azure/cli/testsdk/scenario_tests/preparers.py:39: in preparer_wrapper
    parameter_update = self.create_resource(
src/azure-cli-testsdk/azure/cli/testsdk/preparers.py:99: in create_resource
    self.live_only_execute(self.cli_ctx, template.format(self.location, name))
src/azure-cli-testsdk/azure/cli/testsdk/preparers.py:39: in live_only_execute
    return self.raw_execute(cli_ctx, command, expect_failure)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:252: in init
    self.in_process_execute(cli_ctx, command, expect_failure=expect_failure)
src/azure-cli-testsdk/azure/cli/testsdk/base.py:315: in in_process_execute
    raise ex.exception
env/lib/python3.12/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:669: in execute
    raise ex
src/azure-cli-core/azure/cli/core/commands/init.py:737: in run_jobs_serially
    results.append(self.run_job(expanded_arg, cmd_copy))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:706: in run_job
    result = cmd_copy(params)
             ^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:336: in call
    return self.handler(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/command_operation.py:120: in handler
    return op(**command_args)
           ^^^^^^^^^^^^^^^^^^
src/azure-cli/azure/cli/command_modules/resource/custom.py:1619: in create_resource_group
    rcf = resource_client_factory(cmd.cli_ctx)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli/azure/cli/command_modules/resource/client_factory.py:10: in resource_client_factory
    return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/client_factory.py:83: in get_mgmt_service_client
    client, 
 = get_mgmt_service_client(cli_ctx, client_type, subscription_id=subscription_id,
src/azure-cli-core/azure/cli/core/commands/client_factory.py:234: in get_mgmt_service_client
    credential, subscription_id, 
 = profile.get_login_credentials(
src/azure-cli-core/azure/cli/core/profile.py:308: in get_login_credentials
    account = self.get_subscription(subscription_id)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
 
 
 
 
 
 
 
 
 
 
                           _ 

self = <azure.cli.core._profile.Profile object at 0x7f0def763470>
subscription = None

    def get_subscription(self, subscription=None):  # take id or name
        subscriptions = self.load_cached_subscriptions()
        if not subscriptions:
>           raise CLIError(_AZ_LOGIN_MESSAGE)
E           knack.util.CLIError: Please run 'az login' to setup account.

src/azure-cli-core/azure/cli/core/_profile.py:558: CLIError
azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py:2784
Failed test_copy_all_secrets cmd = <MagicMock id='139697828533392'>
client = <MagicMock id='139697826636112'>
destination_vault = 'https://dest-kv.vault.azure.net/',&nbsp;name&nbsp;=&nbsp;None
all_secrets = True, overwrite = False

    def copy_secret(cmd, client, destination_vault, name=None, all_secrets=None, overwrite=False):
        from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
        from azure.keyvault.secrets import SecretClient
        from azure.cli.core.profile import Profile
        from azure.cli.core.commands.client_factory import prepare_client_kwargs_track2
    
        # If neither a specific secret name nor --all is provided, default to copying all secrets.
        if not name and not all_secrets:
            all_secrets = True
    
        # A specific secret name and --all are mutually exclusive.
        if name and all_secrets:
            raise MutuallyExclusiveArgumentError("Specify either a secret name or --all, but not both.")
        # Validation
        if client.vault_url.rstrip('/') == destination_vault.rstrip('/'):
            raise CLIError("Source and destination Key Vaults cannot be the same.")
    
        profile = Profile(cli_ctx=cmd.cli_ctx)
        credential, 
 = profile.get_login_credentials(subscription_id=cmd.cli_ctx.data.get('subscription_id'))
    
        # Use standard client kwargs for consistent logging/telemetry
        client_kwargs = prepare_client_kwargs_track2(cmd.cli_ctx)
        # KeyVault clients handle this internally or differently sometimes, mimicking client_factory
        client_kwargs.pop('http_logging_policy', None)
    
        dest_client = SecretClient(
            vault_url=destination_vault,
            credential=credential,
            api_version='7.4',
            verify_challenge_resource=False,
            **client_kwargs
        )
    
        # Fail fast if destination vault is not accessible or does not exist
        try:
            # Perform a lightweight call to validate vault accessibility.
            # A 404 for a dummy secret name means the vault is reachable but the secret does not exist.
>           dest_client.get_secret("azure-cli-validation-dummy")

src/azure-cli/azure/cli/command_modules/keyvault/custom.py:2626: 
 
                                       
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/unittest/mock.py:1139: in call
    return self.mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/unittest/mock.py:1143: in mock_call
    return self.execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
                                      

self = <MagicMock name='SecretClient().get_secret' id='139697829718992'>
args = ('azure-cli-validation-dummy',), kwargs = {}
effect = <list_iterator object at 0x7f0def5ccdc0>
result = HttpResponseError('Not Found')

    def execute_mock_call(self, /, *args, **kwargs):
        # separate from increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if is_exception(effect):
                raise effect
            elif not callable(effect):
                result = next(effect)
                if is_exception(result):
>                   raise result
E                   azure.core.exceptions.HttpResponseError: Not Found

/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/unittest/mock.py:1202: HttpResponseError

During handling of the above exception, another exception occurred:

self = <azure.cli.command_modules.keyvault.tests.latest.test_keyvault_unit.KeyVaultCopySecretTest testMethod=test_copy_all_secrets>

    def test_copy_all_secrets(self):
        # Setup
        destination_vault = "https://dest-kv.vault.azure.net/"
    
        # Dummy check 404
        not_found_error = HttpResponseError(message="Not Found")
        not_found_error.status_code = 404
        # We have 2 secrets. For each, we check existence (fails -> copy).
        # Side effect sequence: DummyCheck -> Check(sec1) -> Check(sec2)
        self.dest_client.get_secret.side_effect = [
            not_found_error,
            ResourceNotFoundError,
            ResourceNotFoundError
        ]
    
        # List secrets source
        s1 = mock.Mock(); s1.name = "sec1"; s1.managed = False
        s2 = mock.Mock(); s2.name = "sec2"; s2.managed = False
        s3 = mock.Mock(); s3.name = "mgd1"; s3.managed = True # Should be skipped
        self.source_client.list_properties_of_secrets.return_value = [s1, s2, s3]
    
        # Get secret details
        def get_secret_side_effect(name):
            m = mock.Mock()
            m.name = name
            m.value = "val"
            m.properties.content_type = None
            m.properties.tags = None
            m.properties.enabled = True
            m.properties.not_before = None
            m.properties.expires_on = None
            return m
        self.source_client.get_secret.side_effect = get_secret_side_effect
    
        new_secret = mock.Mock()
        new_secret.name = "sec"
        new_secret.id = "id"
        self.dest_client.set_secret.return_value = new_secret
    
        # Act
>       result = copy_secret(self.cmd, self.source_client, destination_vault, all_secrets=True)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:181: 
 
 
 
 
 
                                   

cmd = <MagicMock id='139697828533392'>
client = <MagicMock id='139697826636112'>
destination_vault = 'https://dest-kv.vault.azure.net/',&nbsp;name&nbsp;=&nbsp;None
all_secrets = True, overwrite = False

    def copy_secret(cmd, client, destination_vault, name=None, all_secrets=None, overwrite=False):
        from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
        from azure.keyvault.secrets import SecretClient
        from azure.cli.core.profile import Profile
        from azure.cli.core.commands.client_factory import prepare_client_kwargs_track2
    
        # If neither a specific secret name nor --all is provided, default to copying all secrets.
        if not name and not all_secrets:
            all_secrets = True
    
        # A specific secret name and --all are mutually exclusive.
        if name and all_secrets:
            raise MutuallyExclusiveArgumentError("Specify either a secret name or --all, but not both.")
        # Validation
        if client.vault_url.rstrip('/') == destination_vault.rstrip('/'):
            raise CLIError("Source and destination Key Vaults cannot be the same.")
    
        profile = Profile(cli_ctx=cmd.cli_ctx)
        credential, 
, _ = profile.get_login_credentials(subscription_id=cmd.cli_ctx.data.get('subscription_id'))
    
        # Use standard client kwargs for consistent logging/telemetry
        client_kwargs = prepare_client_kwargs_track2(cmd.cli_ctx)
        # KeyVault clients handle this internally or differently sometimes, mimicking _client_factory
        client_kwargs.pop('http_logging_policy', None)
    
        dest_client = SecretClient(
            vault_url=destination_vault,
            credential=credential,
            api_version='7.4',
            verify_challenge_resource=False,
            **client_kwargs
        )
    
        # Fail fast if destination vault is not accessible or does not exist
        try:
            # Perform a lightweight call to validate vault accessibility.
            # A 404 for a dummy secret name means the vault is reachable but the secret does not exist.
            dest_client.get_secret("azure-cli-validation-dummy")
        except ResourceNotFoundError:
            # Vault is accessible but the dummy secret does not exist, which is expected.
            pass
        except HttpResponseError as e:
>           raise CLIError(f"Failed to access destination Key Vault '{destination_vault}': {str(e)}")
E           knack.util.CLIError: Failed to access destination Key Vault 'https://dest-kv.vault.azure.net/':&nbsp;Not&nbsp;Found

src/azure-cli/azure/cli/command_modules/keyvault/custom.py:2631: CLIError
azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:140
Failed test_copy_secret_already_exists_no_overwrite cmd = <MagicMock id='139697828533392'>
client = <MagicMock id='139697827046528'>
destination_vault = 'https://dest-kv.vault.azure.net/',&nbsp;name&nbsp;=&nbsp;'mysecret'
all_secrets = None, overwrite = False

    def copy_secret(cmd, client, destination_vault, name=None, all_secrets=None, overwrite=False):
        from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
        from azure.keyvault.secrets import SecretClient
        from azure.cli.core.profile import Profile
        from azure.cli.core.commands.client_factory import prepare_client_kwargs_track2
    
        # If neither a specific secret name nor --all is provided, default to copying all secrets.
        if not name and not all_secrets:
            all_secrets = True
    
        # A specific secret name and --all are mutually exclusive.
        if name and all_secrets:
            raise MutuallyExclusiveArgumentError("Specify either a secret name or --all, but not both.")
        # Validation
        if client.vault_url.rstrip('/') == destination_vault.rstrip('/'):
            raise CLIError("Source and destination Key Vaults cannot be the same.")
    
        profile = Profile(cli_ctx=cmd.cli_ctx)
        credential, 
 = profile.get_login_credentials(subscription_id=cmd.cli_ctx.data.get('subscription_id'))
    
        # Use standard client kwargs for consistent logging/telemetry
        client_kwargs = prepare_client_kwargs_track2(cmd.cli_ctx)
        # KeyVault clients handle this internally or differently sometimes, mimicking client_factory
        client_kwargs.pop('http_logging_policy', None)
    
        dest_client = SecretClient(
            vault_url=destination_vault,
            credential=credential,
            api_version='7.4',
            verify_challenge_resource=False,
            **client_kwargs
        )
    
        # Fail fast if destination vault is not accessible or does not exist
        try:
            # Perform a lightweight call to validate vault accessibility.
            # A 404 for a dummy secret name means the vault is reachable but the secret does not exist.
>           dest_client.get_secret("azure-cli-validation-dummy")

src/azure-cli/azure/cli/command_modules/keyvault/custom.py:2626: 
 
                                       
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/unittest/mock.py:1139: in call
    return self.mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/unittest/mock.py:1143: in mock_call
    return self.execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
                                      

self = <MagicMock name='SecretClient().get_secret' id='139697829718992'>
args = ('azure-cli-validation-dummy',), kwargs = {}
effect = <list_iterator object at 0x7f0def763700>
result = HttpResponseError('Not Found')

    def execute_mock_call(self, /, *args, **kwargs):
        # separate from increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if is_exception(effect):
                raise effect
            elif not callable(effect):
                result = next(effect)
                if is_exception(result):
>                   raise result
E                   azure.core.exceptions.HttpResponseError: Not Found

/opt/hostedtoolcache/Python/3.12.12/x64/lib/python3.12/unittest/mock.py:1202: HttpResponseError

During handling of the above exception, another exception occurred:

self = <azure.cli.command_modules.keyvault.tests.latest.test_keyvault_unit.KeyVaultCopySecretTest testMethod=test_copy_secret_already_exists_no_overwrite>

    def test_copy_secret_already_exists_no_overwrite(self):
        # Setup
        secret_name = "mysecret"
        destination_vault = "https://dest-kv.vault.azure.net/"
    
        # Dummy check 404
        not_found_error = HttpResponseError(message="Not Found")
        not_found_error.status_code = 404
    
        # Pre-check existence returns Success (means it exists)
        self.dest_client.get_secret.side_effect = [not_found_error, mock.Mock()]
    
        # Act
>       result = copy_secret(self.cmd, self.source_client, destination_vault, name=secret_name, overwrite=False)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:102: 
 
 
 
 
 
                                   

cmd = <MagicMock id='139697828533392'>
client = <MagicMock id='139697827046528'>
destination_vault = 'https://dest-kv.vault.azure.net/',&nbsp;name&nbsp;=&nbsp;'mysecret'
all_secrets = None, overwrite = False

    def copy_secret(cmd, client, destination_vault, name=None, all_secrets=None, overwrite=False):
        from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
        from azure.keyvault.secrets import SecretClient
        from azure.cli.core.profile import Profile
        from azure.cli.core.commands.client_factory import prepare_client_kwargs_track2
    
        # If neither a specific secret name nor --all is provided, default to copying all secrets.
        if not name and not all_secrets:
            all_secrets = True
    
        # A specific secret name and --all are mutually exclusive.
        if name and all_secrets:
            raise MutuallyExclusiveArgumentError("Specify either a secret name or --all, but not both.")
        # Validation
        if client.vault_url.rstrip('/') == destination_vault.rstrip('/'):
            raise CLIError("Source and destination Key Vaults cannot be the same.")
    
        profile = Profile(cli_ctx=cmd.cli_ctx)
        credential, 
, _ = profile.get_login_credentials(subscription_id=cmd.cli_ctx.data.get('subscription_id'))
    
        # Use standard client kwargs for consistent logging/telemetry
        client_kwargs = prepare_client_kwargs_track2(cmd.cli_ctx)
        # KeyVault clients handle this internally or differently sometimes, mimicking _client_factory
        client_kwargs.pop('http_logging_policy', None)
    
        dest_client = SecretClient(
            vault_url=destination_vault,
            credential=credential,
            api_version='7.4',
            verify_challenge_resource=False,
            **client_kwargs
        )
    
        # Fail fast if destination vault is not accessible or does not exist
        try:
            # Perform a lightweight call to validate vault accessibility.
            # A 404 for a dummy secret name means the vault is reachable but the secret does not exist.
            dest_client.get_secret("azure-cli-validation-dummy")
        except ResourceNotFoundError:
            # Vault is accessible but the dummy secret does not exist, which is expected.
            pass
        except HttpResponseError as e:
>           raise CLIError(f"Failed to access destination Key Vault '{destination_vault}': {str(e)}")
E           knack.util.CLIError: Failed to access destination Key Vault 'https://dest-kv.vault.azure.net/':&nbsp;Not&nbsp;Found

src/azure-cli/azure/cli/command_modules/keyvault/custom.py:2631: CLIError
azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:88
Failed test_copy_secret_already_exists_with_overwrite The error message is too long, please check the pipeline log for details. azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:107
Failed test_copy_single_secret_success The error message is too long, please check the pipeline log for details. azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:47
❌3.13
Type Test Case Error Message Line
Failed test_keyvault_secret_copy self = <azure.cli.testsdk.base.ExecutionResult object at 0x7fdc96d10c20>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7fdc9ca2efd0>
command = 'group create --location westus --name cli_test_keyvault_copy5dwxykvoe2usqd6id3ohkkciqgp2e2nzxexukm7dzddf5j6f4gm5n --tag product=azurecli cause=automation test date=2026-02-05T22:44:32Z test=test_keyvault_secret_copy module=keyvault'
expect_failure = False

    def in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
>           self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli-testsdk/azure/cli/testsdk/base.py:303: 
                                        
env/lib/python3.13/site-packages/knack/cli.py:245: in invoke
    exit_code = self.exception_handler(ex)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/init.py:135: in exception_handler
    return handle_exception(ex)
           ^^^^^^^^^^^^^^^^^^^^
                                        

ex = CLIError("Please run 'az login' to setup account."), args = (), kwargs = {}

    def handle_main_exception(ex, *args, **kwargs):  # pylint: disable=unused-argument
        if isinstance(ex, CannotOverwriteExistingCassetteException):
            # This exception usually caused by a no match HTTP request. This is a product error
            # that is caused by change of SDK invocation.
            raise ex
    
>       raise CliExecutionError(ex)
E       azure.cli.testsdk.exceptions.CliExecutionError: The CLI throws exception CLIError during execution and fails the command.

src/azure-cli-testsdk/azure/cli/testsdk/patches.py:35: CliExecutionError

During handling of the above exception, another exception occurred:
src/azure-cli-testsdk/azure/cli/testsdk/scenario_tests/preparers.py:39: in preparer_wrapper
    parameter_update = self.create_resource(
src/azure-cli-testsdk/azure/cli/testsdk/preparers.py:99: in create_resource
    self.live_only_execute(self.cli_ctx, template.format(self.location, name))
src/azure-cli-testsdk/azure/cli/testsdk/preparers.py:39: in live_only_execute
    return self.raw_execute(cli_ctx, command, expect_failure)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:252: in init
    self.in_process_execute(cli_ctx, command, expect_failure=expect_failure)
src/azure-cli-testsdk/azure/cli/testsdk/base.py:315: in in_process_execute
    raise ex.exception
env/lib/python3.13/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:669: in execute
    raise ex
src/azure-cli-core/azure/cli/core/commands/init.py:737: in run_jobs_serially
    results.append(self.run_job(expanded_arg, cmd_copy))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:706: in run_job
    result = cmd_copy(params)
             ^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:336: in call
    return self.handler(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/command_operation.py:120: in handler
    return op(**command_args)
           ^^^^^^^^^^^^^^^^^^
src/azure-cli/azure/cli/command_modules/resource/custom.py:1619: in create_resource_group
    rcf = resource_client_factory(cmd.cli_ctx)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli/azure/cli/command_modules/resource/client_factory.py:10: in resource_client_factory
    return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/client_factory.py:83: in get_mgmt_service_client
    client, 
 = get_mgmt_service_client(cli_ctx, client_type, subscription_id=subscription_id,
src/azure-cli-core/azure/cli/core/commands/client_factory.py:234: in get_mgmt_service_client
    credential, subscription_id, 
 = profile.get_login_credentials(
src/azure-cli-core/azure/cli/core/profile.py:308: in get_login_credentials
    account = self.get_subscription(subscription_id)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
 
 
 
 
 
 
 
 
 
 
                           _ 

self = <azure.cli.core._profile.Profile object at 0x7fdc9ed96cf0>
subscription = None

    def get_subscription(self, subscription=None):  # take id or name
        subscriptions = self.load_cached_subscriptions()
        if not subscriptions:
>           raise CLIError(_AZ_LOGIN_MESSAGE)
E           knack.util.CLIError: Please run 'az login' to setup account.

src/azure-cli-core/azure/cli/core/_profile.py:558: CLIError
azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py:2784
Failed test_copy_all_secrets cmd = <MagicMock id='140585399812848'>
client = <MagicMock id='140585398963568'>
destination_vault = 'https://dest-kv.vault.azure.net/',&nbsp;name&nbsp;=&nbsp;None
all_secrets = True, overwrite = False

    def copy_secret(cmd, client, destination_vault, name=None, all_secrets=None, overwrite=False):
        from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
        from azure.keyvault.secrets import SecretClient
        from azure.cli.core.profile import Profile
        from azure.cli.core.commands.client_factory import prepare_client_kwargs_track2
    
        # If neither a specific secret name nor --all is provided, default to copying all secrets.
        if not name and not all_secrets:
            all_secrets = True
    
        # A specific secret name and --all are mutually exclusive.
        if name and all_secrets:
            raise MutuallyExclusiveArgumentError("Specify either a secret name or --all, but not both.")
        # Validation
        if client.vault_url.rstrip('/') == destination_vault.rstrip('/'):
            raise CLIError("Source and destination Key Vaults cannot be the same.")
    
        profile = Profile(cli_ctx=cmd.cli_ctx)
        credential, 
 = profile.get_login_credentials(subscription_id=cmd.cli_ctx.data.get('subscription_id'))
    
        # Use standard client kwargs for consistent logging/telemetry
        client_kwargs = prepare_client_kwargs_track2(cmd.cli_ctx)
        # KeyVault clients handle this internally or differently sometimes, mimicking client_factory
        client_kwargs.pop('http_logging_policy', None)
    
        dest_client = SecretClient(
            vault_url=destination_vault,
            credential=credential,
            api_version='7.4',
            verify_challenge_resource=False,
            **client_kwargs
        )
    
        # Fail fast if destination vault is not accessible or does not exist
        try:
            # Perform a lightweight call to validate vault accessibility.
            # A 404 for a dummy secret name means the vault is reachable but the secret does not exist.
>           dest_client.get_secret("azure-cli-validation-dummy")

src/azure-cli/azure/cli/command_modules/keyvault/custom.py:2626: 
 
                                       
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/unittest/mock.py:1169: in call
    return self.mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/unittest/mock.py:1173: in mock_call
    return self.execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
                                      

self = <MagicMock name='SecretClient().get_secret' id='140585398964240'>
args = ('azure-cli-validation-dummy',), kwargs = {}
effect = <list_iterator object at 0x7fdc971f5c30>
result = HttpResponseError('Not Found')

    def execute_mock_call(self, /, *args, **kwargs):
        # separate from increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if is_exception(effect):
                raise effect
            elif not callable(effect):
                result = next(effect)
                if is_exception(result):
>                   raise result
E                   azure.core.exceptions.HttpResponseError: Not Found

/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/unittest/mock.py:1232: HttpResponseError

During handling of the above exception, another exception occurred:

self = <azure.cli.command_modules.keyvault.tests.latest.test_keyvault_unit.KeyVaultCopySecretTest testMethod=test_copy_all_secrets>

    def test_copy_all_secrets(self):
        # Setup
        destination_vault = "https://dest-kv.vault.azure.net/"
    
        # Dummy check 404
        not_found_error = HttpResponseError(message="Not Found")
        not_found_error.status_code = 404
        # We have 2 secrets. For each, we check existence (fails -> copy).
        # Side effect sequence: DummyCheck -> Check(sec1) -> Check(sec2)
        self.dest_client.get_secret.side_effect = [
            not_found_error,
            ResourceNotFoundError,
            ResourceNotFoundError
        ]
    
        # List secrets source
        s1 = mock.Mock(); s1.name = "sec1"; s1.managed = False
        s2 = mock.Mock(); s2.name = "sec2"; s2.managed = False
        s3 = mock.Mock(); s3.name = "mgd1"; s3.managed = True # Should be skipped
        self.source_client.list_properties_of_secrets.return_value = [s1, s2, s3]
    
        # Get secret details
        def get_secret_side_effect(name):
            m = mock.Mock()
            m.name = name
            m.value = "val"
            m.properties.content_type = None
            m.properties.tags = None
            m.properties.enabled = True
            m.properties.not_before = None
            m.properties.expires_on = None
            return m
        self.source_client.get_secret.side_effect = get_secret_side_effect
    
        new_secret = mock.Mock()
        new_secret.name = "sec"
        new_secret.id = "id"
        self.dest_client.set_secret.return_value = new_secret
    
        # Act
>       result = copy_secret(self.cmd, self.source_client, destination_vault, all_secrets=True)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:181: 
 
 
 
 
 
                                   

cmd = <MagicMock id='140585399812848'>
client = <MagicMock id='140585398963568'>
destination_vault = 'https://dest-kv.vault.azure.net/',&nbsp;name&nbsp;=&nbsp;None
all_secrets = True, overwrite = False

    def copy_secret(cmd, client, destination_vault, name=None, all_secrets=None, overwrite=False):
        from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
        from azure.keyvault.secrets import SecretClient
        from azure.cli.core.profile import Profile
        from azure.cli.core.commands.client_factory import prepare_client_kwargs_track2
    
        # If neither a specific secret name nor --all is provided, default to copying all secrets.
        if not name and not all_secrets:
            all_secrets = True
    
        # A specific secret name and --all are mutually exclusive.
        if name and all_secrets:
            raise MutuallyExclusiveArgumentError("Specify either a secret name or --all, but not both.")
        # Validation
        if client.vault_url.rstrip('/') == destination_vault.rstrip('/'):
            raise CLIError("Source and destination Key Vaults cannot be the same.")
    
        profile = Profile(cli_ctx=cmd.cli_ctx)
        credential, 
, _ = profile.get_login_credentials(subscription_id=cmd.cli_ctx.data.get('subscription_id'))
    
        # Use standard client kwargs for consistent logging/telemetry
        client_kwargs = prepare_client_kwargs_track2(cmd.cli_ctx)
        # KeyVault clients handle this internally or differently sometimes, mimicking _client_factory
        client_kwargs.pop('http_logging_policy', None)
    
        dest_client = SecretClient(
            vault_url=destination_vault,
            credential=credential,
            api_version='7.4',
            verify_challenge_resource=False,
            **client_kwargs
        )
    
        # Fail fast if destination vault is not accessible or does not exist
        try:
            # Perform a lightweight call to validate vault accessibility.
            # A 404 for a dummy secret name means the vault is reachable but the secret does not exist.
            dest_client.get_secret("azure-cli-validation-dummy")
        except ResourceNotFoundError:
            # Vault is accessible but the dummy secret does not exist, which is expected.
            pass
        except HttpResponseError as e:
>           raise CLIError(f"Failed to access destination Key Vault '{destination_vault}': {str(e)}")
E           knack.util.CLIError: Failed to access destination Key Vault 'https://dest-kv.vault.azure.net/':&nbsp;Not&nbsp;Found

src/azure-cli/azure/cli/command_modules/keyvault/custom.py:2631: CLIError
azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:140
Failed test_copy_secret_already_exists_no_overwrite cmd = <MagicMock id='140585399812848'>
client = <MagicMock id='140585398963568'>
destination_vault = 'https://dest-kv.vault.azure.net/',&nbsp;name&nbsp;=&nbsp;'mysecret'
all_secrets = None, overwrite = False

    def copy_secret(cmd, client, destination_vault, name=None, all_secrets=None, overwrite=False):
        from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
        from azure.keyvault.secrets import SecretClient
        from azure.cli.core.profile import Profile
        from azure.cli.core.commands.client_factory import prepare_client_kwargs_track2
    
        # If neither a specific secret name nor --all is provided, default to copying all secrets.
        if not name and not all_secrets:
            all_secrets = True
    
        # A specific secret name and --all are mutually exclusive.
        if name and all_secrets:
            raise MutuallyExclusiveArgumentError("Specify either a secret name or --all, but not both.")
        # Validation
        if client.vault_url.rstrip('/') == destination_vault.rstrip('/'):
            raise CLIError("Source and destination Key Vaults cannot be the same.")
    
        profile = Profile(cli_ctx=cmd.cli_ctx)
        credential, 
 = profile.get_login_credentials(subscription_id=cmd.cli_ctx.data.get('subscription_id'))
    
        # Use standard client kwargs for consistent logging/telemetry
        client_kwargs = prepare_client_kwargs_track2(cmd.cli_ctx)
        # KeyVault clients handle this internally or differently sometimes, mimicking client_factory
        client_kwargs.pop('http_logging_policy', None)
    
        dest_client = SecretClient(
            vault_url=destination_vault,
            credential=credential,
            api_version='7.4',
            verify_challenge_resource=False,
            **client_kwargs
        )
    
        # Fail fast if destination vault is not accessible or does not exist
        try:
            # Perform a lightweight call to validate vault accessibility.
            # A 404 for a dummy secret name means the vault is reachable but the secret does not exist.
>           dest_client.get_secret("azure-cli-validation-dummy")

src/azure-cli/azure/cli/command_modules/keyvault/custom.py:2626: 
 
                                       
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/unittest/mock.py:1169: in call
    return self.mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/unittest/mock.py:1173: in mock_call
    return self.execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 
                                      

self = <MagicMock name='SecretClient().get_secret' id='140585398964576'>
args = ('azure-cli-validation-dummy',), kwargs = {}
effect = <list_iterator object at 0x7fdc970ffbb0>
result = HttpResponseError('Not Found')

    def execute_mock_call(self, /, *args, **kwargs):
        # separate from increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if is_exception(effect):
                raise effect
            elif not callable(effect):
                result = next(effect)
                if is_exception(result):
>                   raise result
E                   azure.core.exceptions.HttpResponseError: Not Found

/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/unittest/mock.py:1232: HttpResponseError

During handling of the above exception, another exception occurred:

self = <azure.cli.command_modules.keyvault.tests.latest.test_keyvault_unit.KeyVaultCopySecretTest testMethod=test_copy_secret_already_exists_no_overwrite>

    def test_copy_secret_already_exists_no_overwrite(self):
        # Setup
        secret_name = "mysecret"
        destination_vault = "https://dest-kv.vault.azure.net/"
    
        # Dummy check 404
        not_found_error = HttpResponseError(message="Not Found")
        not_found_error.status_code = 404
    
        # Pre-check existence returns Success (means it exists)
        self.dest_client.get_secret.side_effect = [not_found_error, mock.Mock()]
    
        # Act
>       result = copy_secret(self.cmd, self.source_client, destination_vault, name=secret_name, overwrite=False)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:102: 
 
 
 
 
 
                                   

cmd = <MagicMock id='140585399812848'>
client = <MagicMock id='140585398963568'>
destination_vault = 'https://dest-kv.vault.azure.net/',&nbsp;name&nbsp;=&nbsp;'mysecret'
all_secrets = None, overwrite = False

    def copy_secret(cmd, client, destination_vault, name=None, all_secrets=None, overwrite=False):
        from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
        from azure.keyvault.secrets import SecretClient
        from azure.cli.core.profile import Profile
        from azure.cli.core.commands.client_factory import prepare_client_kwargs_track2
    
        # If neither a specific secret name nor --all is provided, default to copying all secrets.
        if not name and not all_secrets:
            all_secrets = True
    
        # A specific secret name and --all are mutually exclusive.
        if name and all_secrets:
            raise MutuallyExclusiveArgumentError("Specify either a secret name or --all, but not both.")
        # Validation
        if client.vault_url.rstrip('/') == destination_vault.rstrip('/'):
            raise CLIError("Source and destination Key Vaults cannot be the same.")
    
        profile = Profile(cli_ctx=cmd.cli_ctx)
        credential, 
, _ = profile.get_login_credentials(subscription_id=cmd.cli_ctx.data.get('subscription_id'))
    
        # Use standard client kwargs for consistent logging/telemetry
        client_kwargs = prepare_client_kwargs_track2(cmd.cli_ctx)
        # KeyVault clients handle this internally or differently sometimes, mimicking _client_factory
        client_kwargs.pop('http_logging_policy', None)
    
        dest_client = SecretClient(
            vault_url=destination_vault,
            credential=credential,
            api_version='7.4',
            verify_challenge_resource=False,
            **client_kwargs
        )
    
        # Fail fast if destination vault is not accessible or does not exist
        try:
            # Perform a lightweight call to validate vault accessibility.
            # A 404 for a dummy secret name means the vault is reachable but the secret does not exist.
            dest_client.get_secret("azure-cli-validation-dummy")
        except ResourceNotFoundError:
            # Vault is accessible but the dummy secret does not exist, which is expected.
            pass
        except HttpResponseError as e:
>           raise CLIError(f"Failed to access destination Key Vault '{destination_vault}': {str(e)}")
E           knack.util.CLIError: Failed to access destination Key Vault 'https://dest-kv.vault.azure.net/':&nbsp;Not&nbsp;Found

src/azure-cli/azure/cli/command_modules/keyvault/custom.py:2631: CLIError
azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:88
Failed test_copy_secret_already_exists_with_overwrite The error message is too long, please check the pipeline log for details. azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:107
Failed test_copy_single_secret_success The error message is too long, please check the pipeline log for details. azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py:47
️✔️lab
️✔️latest
️✔️3.12
️✔️3.13
️✔️managedservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️maps
️✔️latest
️✔️3.12
️✔️3.13
️✔️marketplaceordering
️✔️latest
️✔️3.12
️✔️3.13
️✔️monitor
️✔️latest
️✔️3.12
️✔️3.13
️✔️mysql
️✔️latest
️✔️3.12
️✔️3.13
️✔️netappfiles
️✔️latest
️✔️3.12
️✔️3.13
️✔️network
️✔️latest
️✔️3.12
️✔️3.13
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.13
️✔️postgresql
️✔️latest
️✔️3.12
️✔️3.13
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.13
️✔️profile
️✔️latest
️✔️3.12
️✔️3.13
️✔️rdbms
️✔️latest
️✔️3.12
️✔️3.13
️✔️redis
️✔️latest
️✔️3.12
️✔️3.13
️✔️relay
️✔️latest
️✔️3.12
️✔️3.13
️✔️resource
️✔️latest
️✔️3.12
️✔️3.13
️✔️role
️✔️latest
️✔️3.12
️✔️3.13
️✔️search
️✔️latest
️✔️3.12
️✔️3.13
️✔️security
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicebus
️✔️latest
️✔️3.12
️✔️3.13
️✔️serviceconnector
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicefabric
️✔️latest
️✔️3.12
️✔️3.13
️✔️signalr
️✔️latest
️✔️3.12
️✔️3.13
️✔️sql
️✔️latest
️✔️3.12
️✔️3.13
️✔️sqlvm
️✔️latest
️✔️3.12
️✔️3.13
️✔️storage
️✔️latest
️✔️3.12
️✔️3.13
️✔️synapse
️✔️latest
️✔️3.12
️✔️3.13
️✔️telemetry
️✔️latest
️✔️3.12
️✔️3.13
️✔️util
️✔️latest
️✔️3.12
️✔️3.13
️✔️vm
️✔️latest
️✔️3.12
️✔️3.13

@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Feb 5, 2026

⚠️AzureCLI-BreakingChangeTest
⚠️keyvault
rule cmd_name rule_message suggest_message
⚠️ 1001 - CmdAdd keyvault secret copy cmd keyvault secret copy added

@yonzhan
Copy link
Collaborator

yonzhan commented Feb 5, 2026

Thank you for your contribution! We will review the pull request and get back to you soon.

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

@microsoft-github-policy-service
Copy link
Contributor

Thank you for your contribution @jcassanji-southworks! We will review the pull request and get back to you soon.

@microsoft-github-policy-service microsoft-github-policy-service bot added customer-reported Issues that are reported by GitHub users external to the Azure organization. Auto-Assign Auto assign by bot labels Feb 5, 2026
@microsoft-github-policy-service microsoft-github-policy-service bot added the Reservations az reservations label Feb 5, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new az keyvault secret copy command that enables users to copy secrets between Azure Key Vaults. The command supports copying individual secrets or bulk copying all secrets, with built-in protection against accidental overwrites and automatic filtering of Azure-managed secrets.

Changes:

  • Added copy_secret command function with helper _copy_single_secret to handle the copy logic
  • Registered the new copy command in the keyvault secret command group
  • Added comprehensive parameter definitions for source vault, destination vault, secret selection, and overwrite behavior
  • Implemented unit tests covering various scenarios (single copy, bulk copy, overwrite protection)
  • Implemented integration tests validating end-to-end functionality
  • Added help documentation with usage examples

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/azure-cli/azure/cli/command_modules/keyvault/custom.py Core implementation of copy_secret and _copy_single_secret functions with error handling and logging
src/azure-cli/azure/cli/command_modules/keyvault/commands.py Command registration for the new copy command
src/azure-cli/azure/cli/command_modules/keyvault/_params.py Parameter definitions for source-vault, destination-vault, name, all, and overwrite options
src/azure-cli/azure/cli/command_modules/keyvault/_help.py Help text and usage examples for the copy command
src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_unit.py Unit tests for copy functionality covering various scenarios
src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py Integration tests validating end-to-end command behavior

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@yonzhan yonzhan assigned notyashhh and unassigned evelyn-ys and kairu-ms Feb 5, 2026
@yonzhan yonzhan removed the Reservations az reservations label Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto-Assign Auto assign by bot customer-reported Issues that are reported by GitHub users external to the Azure organization. KeyVault az keyvault

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants