Presently sponsored by: ScriptRunner - Get your free PowerShell Security e-Book!

Backing up your data and sensitive settings are crucial for any disaster recovery processes. Whether working in a big or small organization, backup and copy is a topic we come back to a lot.

If you're here for the Azure CLI PowerShell script to copy secrets between Azure Key Vaults, scroll to the end.

In this post we will take a look at these things:

  • Differences between a backup, and a downloaded object.
  • How to backup objects from the Azure Portal.
  • How to backup objects using the Azure CLI.
  • How to backup objects into another Azure Key Vault, in a different subscription and directory.

Tag along, and as always, a friendly comment is always appreciated!

What's the difference between Backup and Download?

There's a key difference between backing up your objects, and downloading them.

When you use the backup functionality - either command line or from the Azure Portal - the objects can only be restored into a vault in the same subscription.

When you instead download your objects, they are plain-text and you can do with them as you please.

This is important to understand when you might need to backup objects from your production vaults into different subscriptions, in different directories altogether. You may want to do this to safe-keep the backups, away from the same access-paths. It may also be part of the requirements for a large micro-service architecture that spans across many subscriptions, but need use the same secrets - copying them across is perhaps a good idea then, to stay in sync.

Why copy the same secrets to multiple vaults?

We'll take a look at how we can backup our secrets. But also how we can copy them from one vault to another. Let's just reason a bit around this before we tackle the how-to later.

Imagine you make billions of cloud transactions every month; hundreds of millions requests to key vaults and storage accounts, and other services. The cross-region transaction costs are a lot higher than making transactions in the same region. That's one reason.

Another reason is data sovereignty and legal requirements for data flows. Some organizations require all persisted data, and all data transactions to happen in a specific location or region. In those cases, we can't cross-request data or secrets from another region.

A final reason that comes to mind is that Azure Key Vault simply isn't performing well enough to handle the load at a single point. If we were to target all our guns toward a single instance of the Key Vault, it quickly becomes throttled and a blocker for our running services.

In this post I will review both how to make a Backup using the built-in backup functionality, and how to download the secrets and send them into another vault. I saw myself coming back to this use case many times over, so I wanted to share.

How to: Backup objects from the Azure Portal

The easy option is to backup objects directly from the Azure Portal. However, as mentioned in the introduction above, this will encrypt your secrets and you can only restore them into the same subscription.

Using the built-in backup download capability from the Azure Portal. It can only be restored into the same subscription.

Downloading this file will give you a text file with the name of the secret, and an encrypted version of the secret value.

The filename for any backup-object will be:

  • Secrets: vaultName-secretName-datestamp.secretbackup
  • Keys: vaultName-keyName-datestamp.keybackup
  • Certificates: vaultName-certificateName-datestamp.certificatebackup

Here's an example of a secret backup that contains my connection string value, encrypted specifically for my subscription. I have cropped the string for brevity, to make it fit into this post.

)AzureSignalRServiceV1.microsoft.comeyJraWQiOiJiM2IwZjgwYy0yZGZkLTRiOTktODUxMS1jYTdkM2JlYTE5M2EiLCJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOjU2Q0JDLUhTNTEyIn0.MQxoXWAdzvBhuhbIUEvaGnmpQSYQq00Jk5R12CB0g25nEFhHIHQG1IEurT0K7AB5wWQImUzSuZrjdPhCD3FzvBNzw6o4v3Q8qLa-EZPZIdWkXYNaxCQxoQy-LYWrrI9WMc1WQttv6lRsucV98FpwO4JnrhnstM7f_QDJEzp5ED5HjL4vR5MSclg64a7JAFmdCa9PEaGrCGg0GC1MXerYQPogjptT3UG66OMljypHFy7U8rILs5JBKcp2I-osEOhYrDLB6rblw9Yq--03OT5Qi3J6S3enXtS3BgY912_66dz1Xzk7GtbrsqXgO_FDErIFevUvF1ymkz-HNOUGqOaHBmP0XNDWKjjAByJS8xhN_mH63dxyLBDwkSbW-Jefba6Ozbn7W9FSS4pPPxE0e7U9s8rjgwnmGi8Q2U87nA8OUEWPhCdoe6ayyiOZInRxMDx64RBa90RI-mrg7olVuUfqav4imgjlRLtW_VrqtvDzkWRm0Z1ttWd1_jWWXmHuFqOF_bIoiCw5yPJNDdCklz3KWiqrLrqMdb5VZoww0pDhU3_-G5IcEOow5Hvd0AyjxwglKL2IGEaKis_P8JShD5Q15lqlgE1IaKRB2v8MFWaiNHowi6TYpR9f2DTx8k8DNEijsbY0ZdIIYFIo8T60wDWUrln_s738K0dIjx-ivtV_aR4CokCC8B4-
......

<redacted for brevity>

At this point you've got a file containing your encrypted value of your key, secret, or certificate. This can only be restored into the same subscription, all from within the Azure Portal.

Restoring a Secret, Key or Certificate in the Azure Portal.

This is a manual and fairly tedious task if you have a lot of data, and a lot of vaults. To automate it, we can for example use the Azure CLI or Azure PowerShell. Let's take a look at both next.

How to: Backup objects using Azure CLI

Please note. We're making backups of these objects, which mean they can only be restored into the same subscription.

We use the az keyvault commands to make backups, or perform other tasks on an Azure Key Vault.

az keyvault secret backup --file "AzureSignalRService.secretbackup" --vault-name "myVault1" -n "AzureSignalRService"

The results is a file with encrypted data, which can only be restored in the same subscription:

)AzureKeyVaultSecretBackupV1.microsoft.comeyJraWQiOiJiM2IwZjgwYy0yZGZkLTRiOTktODUxMS1jYTdkM2JlYTE5M2EiLCJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.Kn0NrmmTh1h....
<cropped for brevity>

Download the secret, and don't encrypt it.

Alternatively, if you don't want to get the encrypted data, you can download the secret instead of making a backup:

az keyvault secret download --file "AzureSignalRService.secret" --vault-name "myVault1" -n "AzureSignalRService"

This will give you the file in clear text, with the actual value of the secret.

Endpoint=https://my-signal-r-service.service.signalr.net;AccessKey=FiAXokluIlB84CnVvx39dlo6AHGIpP+_+t+Uo=;Version=1.0;

How to: Backup objects into another vault in another subscription

In this section we'll take a look at getting the secret values, and saving them into another vault directly. We want to do this without touching any files on disk.

With this approach, we're simply fetching all secrets from Vault1 in Subscription1, and saving them to Vault2 in Subscription2. However, remember that the secrets we fetch now are not encrypted to your subscription, hence it's not a good idea to persist them in memory, sessions or disk. Please modify the below script as applicable to your needs.

I am using PowerShell and the Azure CLI here. Feel free to modify it as you see fit.

The script has been stripped of custom logic and unique use cases, and can serve as a starting point for your adventures into the land of Azure Key Vault backups and data copy.

Here's the CopySecretsToAnotherVault.ps1 script:

Param(
    [parameter(mandatory)] [string] $originVault,
    [parameter(mandatory)] [string] $originSubscriptionId,
    [parameter(mandatory)] [string] $destinationVault,
    [parameter(mandatory)] [string] $destinationSubscriptionId,
    [string] $disableDestinationSecrets = $true
)

# 1. Set the source subscription id. 
Write-Host "Setting origin subscription to: $($originSubscriptionId)..."
az account set -s $originSubscriptionId

# 1.1 Get all secrets
Write-Host "Listing all origin secrets from vault: $($originVault)"
$originSecretKeys = az keyvault secret list --vault-name $originVault  -o json --query "[].name"  | ConvertFrom-Json

# 1.3 Loop the secrets, and push the value to the destination vault without instantiating new variables.
$originSecretKeys | ForEach-Object {
    $secretName = $_
    Write-Host " - Getting '$($secretName)' from origin, and setting in destination..."
    az keyvault secret set --name $secretName --vault-name $destinationVault -o none --value(az keyvault secret show --name $secretName --vault-name $originVault -o json --query "value")
}

Write-Host "Finished."

In the script above, I'm avoiding using any local objects and variables; We pipe the results of the secrets directly into new secrets in the destination vault.

For a more explicit script, but with the risk of having in-memory variables, you could opt to do it like this. We should generally try to avoid having any variables that contain sensitive information, and this is perhaps better suited for backup scenarios where you would encrypt your secrets. For transparency and clarity, I wanted to share this version, too. Both versions are available on GitHub (check the history of the file, link at the end of the post).

Param(
    [parameter(mandatory)] [string] $originVault,
    [parameter(mandatory)] [string] $originSubscriptionId,
    [parameter(mandatory)] [string] $destinationVault,
    [parameter(mandatory)] [string] $destinationSubscriptionId,
    [string] $disableDestinationSecrets = $true
)

# 1. Set the source subscription id. 
Write-Host "Setting origin subscription to: $($originSubscriptionId)..."
az account set -s $originSubscriptionId

# 1.1 Get all secrets
Write-Host "Listing all origin secrets from vault: $($originVault)"
$originSecretKeys = az keyvault secret list --vault-name $originVault  -o json --query "[].name"  | ConvertFrom-Json

# 1.3 Loop secrets into PSCustomObjects, making it easy to work with later.
$secretObjects = $originSecretKeys | ForEach-Object {
    Write-Host " - Getting secret value for '$($_)'"
    $secret = az keyvault secret show --name $_ --vault-name $originVault -o json | ConvertFrom-Json
    
    [PSCustomObject]@{
        secretName  = $_;
        secretValue = $secret.value;
    }#endcustomobject.

}#endforeach.

Write-Host "Done fetching secrets..."

# 2. Set the destination subscription id.
Write-Host "Setting destination subscription to: $($destinationSubscriptionId)..."
az account set -s $destinationSubscriptionId

# 2.2 Loop secrets objects, and set secrets in destination vault
Write-Host "Writing all destination secrets to vault: $($destinationVault)"
$secretObjects | ForEach-Object {
    Write-Host " - Setting secret value for '$($_.secretName)'"
    az keyvault secret set --vault-name $destinationVault --name "$($_.secretName)" --value  "$($_.secretValue)" --disabled $disableDestinationSecrets -o none
}

# 3. Clean up
Write-Host "Cleaning up and exiting."
Remove-Variable secretObjects
Remove-Variable originSecretKeys

Write-Host "Finished."

Here's how you call the script:

.\CopySecretsToAnotherVault.ps1 -originVault "vault1-name" -originSubscriptionId "SUBSCRIPTION GUID" -destinationVault "vault2-name" 
-destinationSubscriptionId "SUBSCRIPTION GUID"

Get it on GitHub

This post was also a part of the Azure Spring Clean 2021 series. Check out more content from other Azure contributors.

Have fun!