Azure KeyVault Secrets Migration Commands
🧱 Migration Inputs
Azure Key Vault Secrets Migration
1. Verify Azure CLI Installation
Run:
az --versionIf you see a message like:
You have 2 update(s) available. Consider updating your CLI installation with 'az upgrade'Then run:
az upgrade2. Select the Correct Azure Subscription
List subscriptions:
az account list -o tableSet your subscription:
az account set --subscription "a4042c0e-f555-440f-8a16-6e0170d10c98"3. Confirm Source and Destination Vault Names
This page uses an effective environment name:
- If
envis local or dev, the effective env is dev - For all other environments, the effective env is
{env}
Source Vault:
dev-ts-keyvaultDestination Vault:
dev-se-keyvault4. Verify the Destination Vault Exists
Run:
az keyvault show -g dev-se-resource-group -n dev-se-keyvault -o tableYou should see the Key Vault details in the output.
5. Inspect a Known Secret in the Source Vault
If you already know an existing secret name (example: dev-aws-admin-log-ui-app-client-id), fetch its value from the source vault:
az keyvault secret show --vault-name dev-ts-keyvault --name "dev-aws-admin-log-ui-app-client-id" --query "value" -o tsvYou should get a secret value (example: xxxxxxxx).
6. List All Secret Names (Source Vault)
List secret names in the source vault:
az keyvault secret list --vault-name dev-ts-keyvault --query "[].name" -o tsvThis should print a list of secret names.
7. Copy Secrets from Source Vault to Destination Vault (PowerShell)
Run this PowerShell script to copy all secrets (including attributes and tags):
$SrcVault = "dev-ts-keyvault"
$DstVault = "dev-se-keyvault"
# Get all secret names from source
$names = az keyvault secret list --vault-name $SrcVault --query "[].name" -o tsv
if (-not $names) {
Write-Host "No secrets found in source vault."
return
}
foreach ($name in $names) {
Write-Host "Copying: $name"
$json = az keyvault secret show --vault-name $SrcVault --name $name -o json | ConvertFrom-Json
if (-not $json.value) {
Write-Host " Skipping $name (no value returned)"
continue
}
$value = $json.value
$contentType = $json.contentType
$enabled = $json.attributes.enabled
$notBefore = $json.attributes.notBefore
$expires = $json.attributes.expires
$tags = $json.tags
# Build argument list
$args = @(
"keyvault","secret","set",
"--vault-name", $DstVault,
"--name", $name,
"--value", $value
)
if ($contentType) { $args += @("--content-type", $contentType) }
if ($notBefore) { $args += @("--not-before", $notBefore) }
if ($expires) { $args += @("--expires", $expires) }
if ($enabled -eq $false) { $args += @("--disabled") }
# Add tags if present: --tags k=v k2=v2
if ($tags) {
$args += "--tags"
foreach ($p in $tags.PSObject.Properties) {
$args += "$($p.Name)=$($p.Value)"
}
}
# Execute as: az <args...>
& az @args | Out-Null
}
Write-Host "Done. Secrets copied from $SrcVault to $DstVault."After the script completes, all secrets should be copied from the source vault to the destination vault.
8. Validate the Secret Counts Match
Count secrets in the source vault:
az keyvault secret list --vault-name dev-ts-keyvault -o tsv | Measure-Object -LineCount secrets in the destination vault:
az keyvault secret list --vault-name dev-se-keyvault -o tsv | Measure-Object -LineThe output should show the same line count for both vaults.
Rename Secrets from Source Prefix to Destination Prefix
This section renames secrets inside the destination vault:
local-ts-→local-se-dev-ts-→dev-se-
9. Step 1 — Configure Variables
Set variables (first run should be dry-run):
$VaultName = "dev-se-keyvault"
# Prefix mappings (old → new)
$PrefixMap = @{
"local-ts-" = "local-se-"
"dev-ts-" = "dev-se-"
}
# Safety switches
$DryRun = $true # ← SET TO $false to actually rename
$DeleteOld = $false # ← SET TO $true only AFTER verificationImportant (first run):
$DryRun = $true
$DeleteOld = $false10. Step 2 — Dry Run Rename Script
Run this script:
Write-Host "Vault: $VaultName"
Write-Host "DryRun: $DryRun"
Write-Host "DeleteOld: $DeleteOld"
Write-Host "---------------------------"
# Get all secret names
$secretNames = az keyvault secret list `
--vault-name $VaultName `
--query "[].name" -o tsv
foreach ($oldName in $secretNames) {
foreach ($oldPrefix in $PrefixMap.Keys) {
if ($oldName.StartsWith($oldPrefix)) {
$newPrefix = $PrefixMap[$oldPrefix]
$newName = $oldName -replace "^$oldPrefix", $newPrefix
Write-Host "Renaming:"
Write-Host " OLD → $oldName"
Write-Host " NEW → $newName"
# Fetch source secret
$json = az keyvault secret show `
--vault-name $VaultName `
--name $oldName -o json | ConvertFrom-Json
if (-not $json.value) {
Write-Host " ⚠ Skipped (no value)"
continue
}
# Build args
$args = @(
"keyvault","secret","set",
"--vault-name", $VaultName,
"--name", $newName,
"--value", $json.value
)
if ($json.contentType) {
$args += @("--content-type", $json.contentType)
}
if ($json.attributes.notBefore) {
$args += @("--not-before", $json.attributes.notBefore)
}
if ($json.attributes.expires) {
$args += @("--expires", $json.attributes.expires)
}
if ($json.attributes.enabled -eq $false) {
$args += "--disabled"
}
if ($json.tags) {
$args += "--tags"
foreach ($p in $json.tags.PSObject.Properties) {
$args += "$($p.Name)=$($p.Value)"
}
}
if ($DryRun) {
Write-Host " 🟡 DRY-RUN: would create $newName"
}
else {
& az @args | Out-Null
Write-Host " ✅ Created $newName"
if ($DeleteOld) {
az keyvault secret delete `
--vault-name $VaultName `
--name $oldName -o none
Write-Host " 🗑 Deleted $oldName"
}
}
Write-Host ""
}
}
}
Write-Host "Done."You should see output like:
Renaming:
OLD dev-ts-conf-db-database-name
NEW dev-se-conf-db-database-name
DRY-RUN: would create dev-se-conf-db-database-name11. Step 3 — Set DryRun to False (Create New Names)
Update variables:
$DryRun = $false
$DeleteOld = $falseRun the Step 2 script again.
12. Step 4 — Delete Old Names (After Verification)
After verifying your services are reading the new secret names, update variables:
$DryRun = $false
$DeleteOld = $trueRun the Step 2 script again to delete old secret names.