2
votes

I regularly use key vault references in my template parameter files in ARM to securely pass-in secrets.

Now, instead of having singular key-vaulted parameters, I'm trying to pass in an array of keyvault-referenced objects. This does not work unfortunately. For background info: I'm trying to deploy/derive a secondary keyvault from a master keyvault, and transfer/copy some entries.

For doing this deploy the following resources in my template:

{
            "type": "Microsoft.KeyVault/vaults",
            "name": "my-new-sub-vault",
            "apiVersion": "2015-06-01",
            "location": "[resourceGroup().location]",
            "properties": {
                "enabledForDeployment": "false",
                "enabledForTemplateDeployment": "false",
                "enabledForVolumeEncryption": "false",
                "tenantId": "[subscription().tenantId]",
                "accessPolicies": [
                    {
                        "tenantId": "[subscription().tenantId]",
                        "objectId": "[parameters('msiObjectId')]",
                        "permissions": {
                            "keys": ["get", "list"],
                            "secrets": ["get", "list"]
                        }
                    }
                ],
                "sku": {
                    "name": "Standard",
                    "family": "A"
                }
            }
        },
        {
            "type": "Microsoft.KeyVault/vaults/secrets",
            "name": "[concat('my-new-sub-vault', '/', parameters('secretsObject').secrets[copyIndex()].secretName)]",
            "apiVersion": "2015-06-01",
            "properties": {
                "value": "[parameters('secretsObject').secrets[copyIndex()].secretValue]"
            },
            "dependsOn": [
                "[concat('Microsoft.KeyVault/vaults/my-new-sub-vault')]"
            ],
            "copy": {
                "name": "secretsCopy",
                "count": "[length(parameters('secretsObject').secrets)]"
            }
        }

Is there an error in my syntax? Or is this not possible?

The error that is thrown is

New-AzResourceGroupDeployment : 16:25:54 - Resource Microsoft.KeyVault/vaults/secrets 'my-new-sub-vault/my-secret' failed with message '{ "error": { "code": "BadRequest", "message": "The secret is missing" } }'

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "secretsObject": {
            "value": {
                "secrets": [						
                    ...,
                    {
                        "secretName": "my-secret",
                        "secretValue": {
                            "reference": {
                              "keyVault": {
                                "id": "/subscriptions/subId/resourceGroups/main/providers/Microsoft.KeyVault/vaults/master-vault"
                              },
                              "secretName": "my-secret"
                            }
                        }
                    }
                ]
            }
        }
    }
}

Cheers

2

2 Answers

1
votes

Another option would be to store the secrets into a single secret in KeyVault, formatted as a JSON object. Then you can write it with a single resource and read it with a single reference. You can use the string() and json() functions to convert it from and to an object that you can use during deployment.

To create the "single secret":

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "keyVaultName": {
      "type": "string",
      "metadata": {
        "description": "Name of the vault"
      }
    },
    "secretName": {
      "type": "string",
      "metadata": {
        "description": "Name of the secret to store in the vault"
      }
    },
    "secretValue": {
      "type": "secureObject",
      "metadata": {
        "description": "Value of the secret to store in the vault"
      }
    }
  },
  "variables": { },
  "resources": [
    {
      "type": "Microsoft.KeyVault/vaults/secrets",
      "name": "[concat(parameters('keyVaultName'), '/', parameters('secretName'))]",
      "apiVersion": "2018-02-14",
      "tags": {
        "displayName": "secret"
      },
      "properties": {
        "value": "[string(parameters('secretValue'))]"
      }
    }
  ]
}

Use this to consume the secrets:

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "secretValue": {
      "type": "securestring"
    }
  },
  "variables": {
    "toJSON": "[json(parameters('secretValue'))]"
  },
  "resources": [ ],
  "outputs": {
    "secretValue": {
      "type": "string",
      "value": "[parameters('secretValue')]"
    },
    "singleObj": {
      "type": "object",
      "value": "[variables('toJSON')]"
    },
    "singleProperty": {
      "type": "string",
      "value": "[variables('toJSON').two]"
    }
  }
}

The parameter file will contain a json object with your secrets when creating them and then you reference using the reference parameter syntax as you have in the OP.

0
votes

that wont work unfortunately, you can, however, work around that with a loop of nested templates. something like this:

{
    "apiVersion": "2017-05-10",
    "name": "[concat('kvReference-', copyIndex())]",
    "type": "Microsoft.Resources/deployments",
    "copy": {
        "name": "kvReference",
        "count": 2
    },
    "properties": {
        "mode": "Incremental",
        "templateLink": {
            "uri": "[uri(deployment().properties.templateLink.uri, 'nested-kv-reference.json')]"
        },
        "parameters": {
            "parameter": {
                "reference": {
                    "keyVault": {
                        "id": "[variables('kvUri')]"
                    },
                    "secretName": "secretName"
                }
            }
        }
    }
},

and you nested template have to just output them as is then you can reference those later in the template:

reference('kvReference-', copyIndex()).outputs.valueName.value

or you can just use them as parameters in the nested template. you can make them secure string type so the values are not exposed in the portal\api.