We recently found ourselves needing to copy over every single secret (name and value) from one Azure KeyVault to a newly created one. I found ways to restore the secrets from a backup, but we didn't have a backup. Is there a Powershell script that can just loop through every name/value combo in a source vault and copy it to a destination vault?
7 Answers
this is just too triggering (no offense), here's a more "powershelly" version:
Param(
[Parameter(Mandatory)]
[string]$sourceVaultName,
[Parameter(Mandatory)]
[string]$destVaultName
)
Connect-AzAccount
$secretNames = (Get-AzKeyVaultSecret -VaultName $sourceVaultName).Name
$secretNames.foreach{
Set-AzKeyVaultSecret -VaultName $destVaultName -Name $_ `
-SecretValue (Get-AzKeyVaultSecret -VaultName $sourceVaultName -Name $_).SecretValue
}
Just to sum it up:
Parameters are mandatory with this change and you can tab complete them, so you dont have to remember which one is first.
Using foreach
is a bit cleaner than using do\while
(certainly less cognitive effort).
You dont have to cast values to text and encrypt it back, you can just use encrypted value to assign it to new secret

- 69,186
- 6
- 100
- 141
-
1cool that looks way neater--i don't write a lot of powershell (if you couldn't tell) so thanks for cleaning it up! – Tom Warner Apr 10 '19 at 17:25
The script can be translated to the new coolness of az.cli
Param(
[Parameter(Mandatory)]
[string]$sourceVaultName,
[Parameter(Mandatory=$false)]
[string]$sourceSubscription,
[Parameter(Mandatory)]
[string]$destVaultName,
[Parameter(Mandatory=$false)]
[string]$descriptionSubscription
)
# az login
if($sourceSubscription){
az account set --subscription $sourceSubscription
}
Write-Host 'Reading secrets ids from' $sourceVaultName
$secretNames = az keyvault secret list --vault-name $sourceVaultName -o json --query "[].name" | ConvertFrom-Json
Write-Host 'Reading secrets values'
$secrets = $secretNames | % {
$secret = az keyvault secret show --name $_ --vault-name $sourceVaultName -o json | ConvertFrom-Json
[PSCustomObject]@{
name = $_;
value = $secret.value;
}
}
Write-Host 'writing secrets'
if($descriptionSubscription){
az account set --subscription $descriptionSubscription
}
$secrets.foreach{
az keyvault secret set --vault-name $destVaultName --name $_.name --value $_.value
}

- 169
- 1
- 3
-
Did you test this? The name parameter is not outputted in the LIST command. I could not get this to work. https://github.com/Azure/azure-cli/issues/3499 – markwilde Dec 04 '20 at 11:10
-
yup, @markwilde, I test it by saving in a .ps1 file and running the script supplying the parameters. It run correctly and cloned my KVs – Florin Grigoriu Dec 12 '20 at 01:16
-
1This DOES not work, it fails silently as the name is not exported. The fix is reasonably simple though ``` $secretNames = az keyvault secret list --vault-name $sourceVaultName -o json --query "[].id" | ConvertFrom-Json | ForEach-Object { $_.Substring($_.LastIndexOf('/') +1 ) } – GavinB Jun 03 '21 at 22:14
This is for those who came here looking for a python solution:
from azure.keyvault.secrets import SecretClient # pip install azure-keyvault-secrets
from azure.identity import DefaultAzureCredential # pip install azure-identity
source_vault_url = "https://sourcevault.vault.azure.net"
destination_vault_url = "https://destvault.vault.azure.net/"
credential = DefaultAzureCredential(
exclude_cli_credential=False
, exclude_environment_credential=True
, exclude_managed_identity_credential=True
, exclude_visual_studio_code_credential=True
, exclude_shared_token_cache_credential=True
, exclude_interactive_browser_credential=True
)
source_client = SecretClient(vault_url=source_vault_url, credential=credential)
destination_client = SecretClient(vault_url=destination_vault_url, credential=credential)
key_list = ['key1', 'key2', 'key3']
# Get secrets from the source key vault
credentials = {}
for key in key_list :
credentials[key] = source_client.get_secret(key).value
# Set secrets in the destination key vault
for key, value in credentials.items():
print(f"Creating a secret called '{key}' with the value '{value}' ...")
destination_client.set_secret(key, value)

- 969
- 3
- 12
- 10
-
-
1Script works well for me. Ensure that you fill out the `key_list` list with the keys you'd like to transfer. Also a good note is that this is for two key vaults within the _same_ Azure subscription! – cody.codes May 30 '21 at 22:46
There is now!
import-module AzureRM.keyvault
$sourceVaultName = $args[0]
$destVaultName = $args[1]
Connect-AzureRmAccount
#unfortunately you can only access secret values one at a time, by name. so this gets the names first
$names = (Get-AzureKeyVaultSecret -VaultName $sourceVaultName | select Name)
$i=0
do {
$rawSecret = (Get-AzureKeyVaultSecret -VaultName $sourceVaultName -Name $names[$i].Name).SecretValueText
$AKVsecret = ConvertTo-SecureString $rawSecret -AsPlainText -Force
Set-AzureKeyVaultSecret -VaultName $destVaultName -Name $names[$i].Name -SecretValue $AKVsecret
$i++
} while($i -lt $names.length)
You can call it using
script.ps1 source-keyvault-name dest-keyvault-name

- 3,193
- 3
- 17
- 24
I started with @4c74356b41's answer, but made a couple changes. I didn't want to copy the certificates across since that doesn't really work well (it copies them as secrets, and they don't work at all cross-region), and I wanted to preserve the ContentType properties as well:
$sourceVaultName = "<source vault>"
$destVaultName = "<dest vault>"
$secrets = Get-AzKeyVaultSecret -VaultName $sourceVaultName
$secrets | ? { $_.ContentType -ne "application/x-pkcs12" } | % {
$secret = Get-AzKeyVaultSecret -VaultName $sourceVaultName -Name $_.Name
Set-AzKeyVaultSecret -VaultName $destVaultName -Name $secret.Name -SecretValue $secret.SecretValue -ContentType $secret.ContentType
}

- 1,251
- 13
- 26
I wrote a set of powershell scripts to clone key vaults from one subscription to another. Hope it make some help.

- 385
- 2
- 18
-
Correct me if I'm wrong, but aren't you just copying all secrets from a keyvault and rewriting them in the very same keyvault? – Sartigan Mar 02 '21 at 19:29
-
@Sartigan No, I am making this dynamic so you can specify name of the new key vault and the script will copy it there – HSharma Mar 03 '21 at 04:03
Very simple script to copy secret from one vault to another:
Param(
[Parameter(Mandatory)]
[string]$oldKeyVault,
[Parameter(Mandatory)]
[string]$newKeyVault
)
#Display Secrets in New Key Vault
Write-Host 'Secrets in New Key Vault BEFORE Sync'
$newSecrets = Get-AzKeyVaultSecret -VaultName $newKeyVault
foreach($newSecret in $newSecrets)
{
$newSecretDetails = Get-AzKeyVaultSecret -VaultName $newKeyVault -Name $newSecret.Name
Write-Host 'New Secret: Name='$newSecretDetails.Name
#Uncomment below if you need to check values
#Write-Host 'New Secret: Name='$newSecretDetails.Name ': Value=' $newSecretDetails.SecretValueText
}
Write-Host ''
Write-Host ''
#Display Secrets in New Old Vault
Write-Host 'Secrets in Old Key Vault before Sync'
$oldSecrets = Get-AzKeyVaultSecret -VaultName $oldKeyVault
foreach($oldSecret in $oldSecrets)
{
$oldSecretDetails = Get-AzKeyVaultSecret -VaultName $oldKeyVault -Name $oldSecret.Name
Write-Host 'Old Secret: Name='$oldSecretDetails.Name
#Uncomment below if you need to check values
#Write-Host 'Old Secret: Name='$oldSecretDetails.Name ': Value=' $oldSecretDetails.SecretValueText
}
Write-Host ''
Write-Host ''
#Sync Key Vault
Write-Host 'Syncing Vaults'
$oldSecrets = Get-AzKeyVaultSecret -VaultName $oldKeyVault
foreach($oldSecret in $oldSecrets)
{
$oldSecretDetails = Get-AzKeyVaultSecret -VaultName $oldKeyVault -Name $oldSecret.Name
$secureStringKey = ConvertTo-SecureString -String $oldSecretDetails.SecretValueText -AsPlainText -Force
Set-AzKeyVaultSecret -VaultName $newKeyVault -Name $oldSecretDetails.Name -SecretValue $secureStringKey
Write-Host 'Secret Copied to New Key Vault: Name = ' $oldSecretDetails.Name
}
Write-Host ''
Write-Host ''
#Display Secrets in New Key Vault
Write-Host 'Secrets in New Key Vault AFTER Sync'
$newSecrets = Get-AzKeyVaultSecret -VaultName $newKeyVault
foreach($newSecret in $newSecrets)
{
$newSecretDetails = Get-AzKeyVaultSecret -VaultName $newKeyVault -Name $newSecret.Name
Write-Host 'New Secret: Name='$newSecretDetails.Name
#Uncomment below if you need to check values
#Write-Host 'New Secret: Name='$newSecretDetails.Name ': Value=' $newSecretDetails.SecretValueText
}

- 53
- 5