Create an Azure Key Vault using an ARM template (api version 2015-06-01) and Azure PowerShell v1.0.4

We live in a world where data and security is not to be taken lightly. Having to create or maintain a system that enable you to keep secrets and/or certificates safe is a challenge in itself. In this particular article we'll see how to create an Azure Key Vault resource using the resource provider Microsoft.KeyVault and API version 2015-06-01 in an ARM template.

Prerequisites

  • Azure PowerShell cmdlets v1.0.4 or later

What Is Azure Key Vault?

Key Vault help you safeguard cryptographic keys and other secrets used by your applications whenever they are On-Premise or in the cloud. More and more services on Azure are now integrating Azure Key Vault as their secret/key source for things like deployments, data or even disk encryption.

Assumptions This article assumes you have basic knowledge and experience with ARM templates. That you know how to configure a resource type in the Resource section of a template. If you don't and need more information about ARM templates, please take a look here before going further: Authoring Azure Resource Manager templates

Microsoft.KeyVault/vaults properties

The type Microsoft.KeyVault/vaults is not that complex but we'll take a look at some the most important properties together.

enabledForDeployment - Specifies if the vault is enabled for deployment from tools and resources providers in Azure or from command lines likes PowerShell or Azure CLI. An example of this is when you create a virtual machine, the Microsoft.Compute resource provider could retrieve secrets from this key vault when the key vault is referenced in resource creation.

enabledForTemplateDeployment - Specifies if the vault is enabled for ARM template deployments. You need to enable this if you want the ARM template engine to retrieve secrets (on behalf of the user/service principal performing the deployment) from this key vault when the key vault is referenced in ARM deployment template or parameter files.

enabledForVolumeEncryption - Specifies if the vault is enabled for volume encryption. The Azure disk encryption solution lets you encrypt your IaaS virtual machine disks, including boot and data disks. The solution is integrated with Key Vault to help you control and manage the disk encryption keys in your Key Vault subscription.

tenantId - Specifies the Azure AD (tenant) unique identifier (GUID) associated with the subscription where the vault will be created.

accessPolicies - Specifies one or many permission on the vault.

If you use the vault for deployment, the person or service principal launching the deployment will need permission on that vault. In the following example I'm going to give myself permission to the vault since I'll be performing future deployments. I'll need to know the tenantId (unique identifier of the Azure Active Directory that owns my account) and my objectId (unique identifier of my user). We can get those values using Azure PowerShell cmdlets and then pass them in as parameters to the template.

To get the tenantId of the subscription, we'll use Azure PowerShell cmdlets v1.0.4 or later. Remember, we want the tenantId for the subscription our vault will reside in. The Get-AzureRmSubscription cmdlet will list one or more subscription if you have access to many. Simply pick the one you want like in this example :

PS C:\> Get-AzureRmSubscription

SubscriptionName : Visual Studio Enterprise with MSDN  
SubscriptionId   : edf192ff-xxxx-xxxx-xxxx-xxxxxxxxxxxx  
TenantId         : a87b073b-xxxx-xxxx-xxxx-xxxxxxxxxxxx  

Get TenantId using Get-AzureRmSubscription

There is also a tenantId property on the accessPolicies for the vault. This is the unique identifier of the Azure Active Directory tenant owning the desired user you want to give access. Speaking of that user, we also need the objectId which represents the unique identifier of the user account in that tenant. In our example, I can do something like...

PS C:\> Get-AzureRmADUser -Mail

DisplayName                    Type                           ObjectId  
-----------                    ----                           --------                                                                                                                                                                             
Stephane Lapointe                                             5eaecd00-xxxx-xxxx-xxxx-xxxxxxxxxxxx  

Getting the current user objectId using Get-AzureRmADUser

Note that if you have a lot of users in your tenant or if you are looking for the objectId of someone else, you can also do the following:

PS C:\> Get-AzureRmADUser -Mail '{TheUserEmailAddress}'  

The next step is to set the permission on the vault that allows Azure Resource Manager to access the vault on our behalf. This is done by setting the enabledForTemlateDeployment property on the resource.

Parameters and template files

Here are the json files we'll need for our deployment.

KeyVaultSetup.parameters.json

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "keyVaultName": {
            "value": "MyUniqueKeyVaultName"
        },
        "tenantId": {
            "value": "a87b073b-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        },
        "objectId": {
            "value": "5eaecd00-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        },
        "secretName": {
            "value": "MyAdminPassword"
        },        
        "enabledForTemplateDeployment": {
            "value": true
        }
    }
}

And the template itself:

KeyVaultSetup.json

{
  "$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"
      }
    },
    "tenantId": {
      "type": "string",
      "metadata": {
        "description": "Tenant Id for the subscription and use assigned access to the vault. Available from the Get-AzureRMSubscription PowerShell cmdlet"
      }
    },
    "objectId": {
      "type": "string",
      "metadata": {
        "description": "Object Id of the AAD user or service principal that will have access to the vault. Available from the Get-AzureRMADUser or the Get-AzureRMADServicePrincipal cmdlets"
      }
    },
    "keysPermissions": {
      "type": "array",
      "defaultValue": [ "all" ],
      "metadata": {
        "description": "Permissions to grant user to keys in the vault. Valid values are: all, create, import, update, get, list, delete, backup, restore, encrypt, decrypt, wrapkey, unwrapkey, sign, and verify."
      }
    },
    "secretsPermissions": {
      "type": "array",
      "defaultValue": [ "all" ],
      "metadata": {
        "description": "Permissions to grant user to secrets in the vault. Valid values are: all, get, set, list, and delete."
      }
    },
    "vaultSku": {
      "type": "string",
      "defaultValue": "Standard",
      "allowedValues": [
        "Standard",
        "Premium"
      ],
      "metadata": {
        "description": "SKU for the vault"
      }
    },
    "enabledForDeployment": {
      "type": "bool",
      "defaultValue": false,
      "metadata": {
        "description": "Specifies if the vault is enabled for VM or Service Fabric deployment"
      }
    },
    "enabledForTemplateDeployment": {
      "type": "bool",
      "defaultValue": false,
      "metadata": {
        "description": "Specifies if the vault is enabled for ARM template deployment"
      }
    },
    "enableVaultForVolumeEncryption": {
      "type": "bool",
      "defaultValue": false,
      "metadata": {
        "description": "Specifies if the vault is enabled for volume encryption"
      }
    },
    "secretName": {
      "type": "string",
      "metadata": {
        "description": "Name of the secret to store in the vault"
      }
    },
    "secretValue": {
      "type": "securestring",
      "metadata": {
        "description": "Value of the secret to store in the vault"
      }
    }
  },
  "resources": [
    {
      "type": "Microsoft.KeyVault/vaults",
      "name": "[parameters('keyVaultName')]",
      "apiVersion": "2015-06-01",
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "KeyVault"
      },
      "properties": {
        "enabledForDeployment": "[parameters('enabledForDeployment')]",
        "enabledForTemplateDeployment": "[parameters('enabledForTemplateDeployment')]",
        "enabledForVolumeEncryption": "[parameters('enableVaultForVolumeEncryption')]",
        "tenantId": "[parameters('tenantId')]",
        "accessPolicies": [
          {
            "tenantId": "[parameters('tenantId')]",
            "objectId": "[parameters('objectId')]",
            "permissions": {
              "keys": "[parameters('keysPermissions')]",
              "secrets": "[parameters('secretsPermissions')]"
            }
          }
        ],
        "sku": {
          "name": "[parameters('vaultSku')]",
          "family": "A"
        }
      },
      "resources": [
        {
          "type": "secrets",
          "name": "[parameters('secretName')]",
          "apiVersion": "2015-06-01",
          "tags": { "displayName": "secret" },
          "properties": {
            "value": "[parameters('secretValue')]"
          },
          "dependsOn": [
            "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
          ]
        }
      ]
    }
  ]
}

What about a little secret?

A vault without any secret is not really useful. Let's take a closer look at the child resource Microsoft.KeyVault/vaults/secrets that will help us with that. Since it is a child resource of our vault, we can use secrets for the type name and ARM will automatically resolve the full type name for us.

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  ...
  "resources": [
    {
      "type": "Microsoft.KeyVault/vaults",
      "name": "[parameters('keyVaultName')]",
      ...
      },
      "properties": {
        ...
      },
      "resources": [
        {
          "type": "secrets",
          "name": "[parameters('secretName')]",
          "apiVersion": "2015-06-01",
          "tags": {
            "displayName": "secret"
          },
          "properties": {
            "value": "[parameters('secretValue')]"
          },
          "dependsOn": [
            "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
          ]
        }
      ]
    }
  ]
}

Since it's an array, we can declare one or many secrets. In our template we only create one secret and it uses the secretName and secretValue template parameters.

Time to Create That Vault

Ensure you replace the values with your own in the parameter file listed in this article. To perform our deployment, let's open a new PowerShell session and execute the following commands...

Note: As you did with the parameter file values, replace the -SubscriptionId value with yours below.

Login-AzureRmAccount  
Select-AzureRmSubscription -SubscriptionId 'edf192ff-xxxx-xxxx-xxxx-xxxxxxxxxxxx'  
New-AzureRmResourceGroup -Name 'AzureKeyVaultDemo' -Location 'West US' -Force  
New-AzureRmResourceGroupDeployment -ResourceGroupName 'AzureKeyVaultDemo' -TemplateFile .\KeyVaultSetup.json -TemplateParameterFile .\KeyVaultSetup.parameters.json -Force -Verbose  

Since we have omitted the secretValue parameter in our parameters file, the New-AzureRmResourceGroupDeployment cmdlet will detect that it's missing and prompt us to enter it. Enter any the value you want for this.

At the end you should have a Succeeded provisioning state if you provided a unique name for your Key Vault and valid info for your tenant & object Ids in your parameter file.
Successful Azure Key Vault deployment

In this series

References

I am the proud father of two little gems. A beer & wine enthusiasm. For everything else, I work and play with Azure at day, and I am an Azure MVP & Advisor at night.
Montreal, Canada