How To Copy Certificates and Secrets Between Key Vaults - Azure Bicep
Certificates and secrets are often necessary for correct functioning of an application or service. In cases, when the application/service is distributed across multiple regions or geos, we might need an ability to copy certificates/secrets between the regions where our application is deployed.
Azure Bicep (and ARM templates) allows copying secrets and certificates between key vaults by retrieving secret values from the source key vault and passing them as parameters into a template. Then the passed values can be used to create or update entries in the destination key vault.
In this post, we will cover not only how to copy individual certs and secrets but also discuss how to organize our template and parameter files to make it extensible for handling an arbitrary number of certificates or secrets. Stay tuned.
Contents:
- Overview
- Copy Secret From One Key Vault To Another
- Copy Certificate Between Key Vaults
- Copy Multiple Certificates/Secrets Between Different Key Vaults
- Related Posts
- Useful Links
Overview
Azure Key Vault is a service in Azure which is commonly used for storing keys, secrets, and certificates. Almost every infrastructure or service deployment involves Key Vault with some secrets.
For better understanding of Azure Key Vault, consider reading Key Vault & Secrets Management With Azure Bicep - Create, Reference, Output Examples.
In this post, we will talk about handling secrets and certificates, these are child resources of Key Vault. You can read more about Child Resources In Azure Bicep - 3 Ways To Declare, Loops, Conditions.
We start with exploring how to copy a secret from one key vault to another. There we look at two ways of passing a secret value, either using parameter file or module inputs.
Next, we reuse the same idea as for secrets and apply it to copying a certificate between vaults. Luckily, the code is almost identical, so it is easy to understand after reading the section about copying a secret.
At the end, we cover how to handle a more advanced use case where we want to copy many secrets and/or certificates between different key vaults, perhaps in a case of multi-region deployment.
Copy Secret From One Key Vault To Another
To clone a secret between key vaults, we need to perform two steps:
- Retrieve/export the secret value from the source key vault.
- Import this value into the destination key vault.
For step #1, we are going to pass the value of the secret stored in the source key vault through a parameter. It can be done both for template and module parameters. This approach is described in detail in Using Key Vault Secrets As Secure Parameters In Azure Bicep - Template & Module Inputs.
For step #2, we will use Microsoft.KeyVault/vaults/secrets resource type to create or update the secret. You can read more about secrets in Key Vault & Secrets Management With Azure Bicep.
[Step #2] Template To Create Or Update Secret
Below, we define a Bicep template which creates or updates the secret based on the values passed via parameters.
NOTE: In the template below, we assume that the destKeyVaultName
Key Vault already exists in the same resource group where the template is deployed. So, we just work with the secret, a child resource of that key vault.
Also, on line 5 pay attention to the @secure()
decorator for secretValue
parameter. This is needed to properly handle sensitive values. Read more in Parameter With @secure() Decorator.
File create-or-update-secret.bicep:
1
2
3
4
5
6
7
8
9
10
11
12
13
// ========== create-or-update-secret.bicep ==========
param destKeyVaultName string
param secretName string
@secure()
param secretValue string
resource secret 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = {
name: '${destKeyVaultName}/${secretName}'
properties: {
value: secretValue
}
}
[Step #1] Passing Secret Value To The Template
Given we already have a template to create or update the secret, now the only thing left is to extract the value from the source key vault and pass it to the template. We will cover two ways of doing that: using a parameter file and modules functionality.
In both cases, under the hood the underlying infrastructure (ARM) retrieves the secret value from the source key vault and passes it as a parameter during deployment. This is what makes it seamless for us, users.
IMPORTANT: Key vault must be enabled for template deployment, otherwise, deployment will fail. See how to enable it in this note.
[Option A] Through Parameter File
While parameter file is familiar to anyone who worked with ARM templates, it also made its way into Azure Bicep without any modifications. To make use of a parameter file to pass the key vault secret value, we need the following:
- create-or-update-secret.bicep - the template defined in the previous section which creates or updates our destination secret.
- create-or-update-secret.parameters.json - the parameter file with a reference to the source secret, the code is listed below.
NOTE: This approach of using a parameter file is explained in Key Vault Secret Through Parameter File.
Notes about the parameters in the code snippet below:
destKeyVaultName
(line 5) - the key vault we are copying secret to.secretName
(line 8) - the name of the secret resource which will hold our secret value in the destination key vault.secretValue
(line 11) - this parameter value is defined through a reference where:id
(line 14) - the full identifier of the source key vault (including subscription and resource group) from where we are copying the secret.secretName
(line 16) - the name of the secret resource in the source key vault; note that there’s an optionalsecretVersion
property if we want to pass a specific version of the secret.
File create-or-update-secret.parameters.json:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"destKeyVaultName": {
"value": "kv-contoso-dest"
},
"secretName": {
"value": "destSecret"
},
"secretValue": {
"reference": {
"keyVault": {
"id": "/subscriptions/ec53095c-1445-4402-a7b7-f7c0d021e8fc/resourceGroups/rg-contoso/providers/Microsoft.KeyVault/vaults/kv-contoso-src"
},
"secretName": "srcSecret"
}
}
}
}
[Option B] Through Module Inputs
Great thing about Bicep files is that any file can be used as a module template. This means that we can deploy our create-or-update-secret.bicep file as a module inside of another template. Also, feel free to read about Modules In Azure Bicep if needed.
And during the module deployment, we can use getSecret function to specify which key vault secret we want to pass as a value for the corresponding parameter.
Read more about secrets and module inputs in another post Passing Key Vault Secret into Module.
The following code snippet is our parent file: main-copy-secret.bicep. A few notes about the code:
- Lines 3-6 - source key vault and secret name, destination key vault and secret name. We specify default values here for convenience, but we could provide values through a parameter file as well.
- Line 8 - a symbolic name for an existing source key vault, read more about how to Reference New Or Existing Resource In Azure Bicep.
- Line 12 - deploying a module which uses the template defined in step #2.
- Line 17 - passing secret value as a parameter using getSecret() function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ========== main-copy-secret.bicep ==========
param srcKeyVaultName string = 'kv-contoso-src'
param srcSecretName string = 'srcSecret'
param destKeyVaultName string = 'kv-contoso-dest'
param destSecretName string = 'destSecret'
resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = {
name: srcKeyVaultName
}
module secret 'create-or-update-secret.bicep' = {
name: '${srcSecretName}-deployment' // Arbitrary name for the module deployment
params: {
destKeyVaultName: destKeyVaultName
secretName: destSecretName
secretValue: keyVault.getSecret(srcSecretName)
}
}
Copy Certificate Between Key Vaults
The cool thing is that copying certificates between key vaults is literally the same as copying secrets. The reason is that any certificate in a key vault is also exposed through secrets API.
We can treat certificates as secrets because retrieving a certificate through secrets API returns the certificate along with its private key (if present). This greatly simplifies our task, however, it has a downside.
Important notes about this approach:
- Certificate will be imported as a secret in the destination key vault, and won’t be shown in the “Certificates” tab in Azure Portal. So, it won’t be that easy to view certificate’s properties in the portal.
- Certificate will be available for download in a pfx format, so we will be able to download and do whatever we want, for example, install or view properties.
- Certificate will be available for programmatic access, for example, to install on virtual machines.
- We will specify contentType to be
application/x-pkcs12
to give Key Vault a hint about the content.
NOTE: If you want to import certificate as a proper certificate (and not secret), then consider using Azure PowerShell or Azure CLI to perform export/import operations. However, this is beyond the scope of this blog post.
Bicep Template and Parameter File
As already mentioned, the copying a certificate between key vaults requires the same template as copying secrets. The only difference is that we will specify content type for the secret resource:
- Line 12 (in the first code snippet) - contentType is set to
application/x-pkcs12
to specify that it is a certificate (this is one of multiple MIME types related to public certificates, so the value might differ based on your certificate format, please double check).
The rest of the template and parameter file are identical to the secret’s case, only parameter names are different to reflect that we are dealing with a certificate.
NOTE: We can also use module deployment and getSecret function as discussed in Option B of section about copying secrets. In this section, we use a parameter file for simplicity.
By deploying the template and parameter file below, we will copy srcCert
certificate from kv-contoso-src
into destCert
secret in kv-contoso-dest
.
File create-or-update-certificate.bicep:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ========== create-or-update-certificate.bicep ==========
param destKeyVaultName string
param certName string
@secure()
param certValue string
resource cert 'Microsoft.KeyVault/vaults/secrets@2019-09-01' = {
name: '${destKeyVaultName}/${certName}'
properties: {
value: certValue
contentType: 'application/x-pkcs12'
}
}
File create-or-update-certificate.parameters.json:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"destKeyVaultName": {
"value": "kv-contoso-dest"
},
"certName": {
"value": "destCert"
},
"certValue": {
"reference": {
"keyVault": {
"id": "/subscriptions/ec53095c-1445-4402-a7b7-f7c0d021e8fc/resourceGroups/rg-contoso/providers/Microsoft.KeyVault/vaults/kv-contoso-src"
},
"secretName": "srcCert"
}
}
}
}
Copy Multiple Certificates/Secrets Between Different Key Vaults
In the previous sections, we discussed how to copy one certificate or one secret between two different key vaults. However, it is unlikely that we will create this automation to handle only one secret or certificate, since in this case we can just copy it manually.
NOTE: In this section, examples are for the certificates use case, but dealing with secrets is very similar, please refer to sections related to secrets and certificates to see the differences.
Now, imagine that we want to automate copying many certificates/secrets during an infrastructure deployment, for example, between key vaults in different Azure regions. Here is an approach we can take:
- Accept a configuration array which specifies what certs to copy between which key vaults.
- Use for-loop to process the incoming collection.
- Use create-or-update-certificate.bicep, module deployments, and getSecret function to copy certificates based on the supplied config.
In this section, we make extensive use of modules, you can read more about them in the post Learn Modules In Azure Bicep - Basics To Advanced, How It Works, Nested Modules, Outputs, Scopes.
Next, let’s look at how to implement step by step the approach described above.
1. Copy Certs Between Two Key Vaults
As a first step, let’s introduce a new Bicep file which will copy multiple certificates from one key vault to another. This template leverages create-or-update-certificate.bicep which we defined previously.
A few notes about the following copy-multiple-certificates-same-keyvaults.bicep template:
certsToCopy
(line 5) - an array of certificate names which we want to copy from source to destination key vault, note that it reuses the same name when creating secret in the destination key vault.- Line 7 - we create a symbolic name for the existing source key vault, it is required for retrieving certificates, also see Reference New Or Existing Resource In Azure Bicep.
@batchSize(1)
(line 11) - this instructs Bicep to execute modules in the loop one by one, and not in parallel; we might get an error when making changes within one KV simultaneously.- Line 12 - processing the list of certificates in a loop, copying each of them using our existing create-or-update-certificate.bicep template.
srckeyVault.getSecret(certName)
(line 17) - using getSecret function to retrieve the certificate secret value and pass it into the nested deployment (module).
NOTE: The template will import the certificate into the destination key vault under the same name as it is in the source key vault. However, you can easily extend the template to import cert under different name if needed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ========== copy-multiple-certificates-same-keyvaults.bicep ==========
param srcKeyVaultName string
param destKeyVaultName string
param certsToCopy array
resource srckeyVault 'Microsoft.KeyVault/vaults@2019-09-01' existing = {
name: srcKeyVaultName
}
@batchSize(1)
module certificates 'create-or-update-certificate.bicep' = [for certName in certsToCopy: {
name: '${destKeyVaultName}-${certName}'
params: {
destKeyVaultName: destKeyVaultName
certName: certName
certValue: srckeyVault.getSecret(certName)
}
}]
2. Copy Certs Between Many Key Vaults
By going one step further, we can leverage step #1 and extend it to copy certificates between multiple source-destination key vault pairs.
For this, we are going to add a new template copy-multiple-certificates-different-keyvaults.bicep which will reuse a template from the previous section:
- Line 3 - the template accepts a config array where each entry describes what certificates to copy from where to where. So, one entry in the array defines certificates to copy between a particular key vault pair.
- Line 22 - iterate over the config array and for each entry invoke copy-multiple-certificates-same-keyvaults.bicep from the last section.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ========== copy-multiple-certificates-different-keyvaults.bicep ==========
param copyConfig array = [
{
srcKeyVaultName: 'kv-contoso-src-1'
destKeyVaultName: 'kv-contoso-dest-1'
certificates: [
'certA'
'certB'
]
}
{
srcKeyVaultName: 'kv-contoso-src-2'
destKeyVaultName: 'kv-contoso-dest-2'
certificates: [
'certC'
'certD'
]
}
]
module kvLevelCertCopy 'copy-multiple-certificates-same-keyVaults.bicep' = [for config in copyConfig: {
name: '${config.srcKeyVaultName}-${config.destKeyVaultName}'
params: {
srcKeyVaultName: config.srcKeyVaultName
destKeyVaultName: config.destKeyVaultName
certsToCopy: config.certificates
}
}]
Summary
In the previous subsections, we used three Bicep files to handle copying of multiple certificates between different key vaults. To summarize, we have the following template files:
- copy-multiple-certificates-different-keyvaults.bicep - the main file, this is the template that we submit for deployment. In turn, it uses the next file in this list.
- copy-multiple-certificates-same-keyvaults.bicep - copies multiple certificates from a specific source key vault to a specific destination key vault. In turn, it uses the next file in this list.
- create-or-update-certificate.bicep - sets the value for one particular certificate based on the values provided through parameters.
Related Posts
- Key Vault & Secrets Management With Azure Bicep - Create, Reference, Output Examples
- Using Key Vault Secrets As Secure Parameters In Azure Bicep - Template & Module Inputs
- Storage Account SAS Tokens, Access Keys, And Connection Strings In Azure Bicep
- Learn Modules In Azure Bicep - Basics To Advanced, How It Works, Nested Modules, Outputs, Scopes
- Reference New Or Existing Resource In Azure Bicep
- Parameters In Azure Bicep - Ultimate Guide With Examples
- Child Resources In Azure Bicep - 3 Ways To Declare, Loops, Conditions
- 5 Ways To Deploy Bicep File With Parameters - Azure DevOps, PowerShell, CLI, Portal, Cloud Shell