Domain join Azure VMs from ARM template with Key Vault secured credentials – Part 2

3 Part blog series

In part one I talked about Prerequisites and Key Vault

In this part we are looking at the linked/nested template.

Prerequisites for this part, some a repeat from part one.

  • AD Domain to join the VM to
  • OU Path
  • Key Vault with “enabledForTemplateDeployment” set
  • Secrets in the Key Vault
  • Blob storage with SaS key and linked template uploaded
  • Main template and a linked template

AD Domain to join the VM to

You need to have a domain to join the VMs to, if you just want a test playground you can run this:

OU Path

As mentioned in part 1 the standard OU Computers cannot be used by the JsonADDomainExtension so you need to create a new one. In my example have create a AzureVM OU.

Main template and a linked template

As I mentioned in Part-1 the Key Vault secrets needs to be passed in to template, this can be done from parameter file but sometimes that does not work in some scenarios.

Like when you have a 3 party portal or ITSM tool, then you need to execute a template without a parameter file. This could be scripted and initiated from the portal or tools but many times that is not an option either.

So, you need a MainTemplate and a LinkedTemplate.

Find Samples here:

These samples are aligned with the QuickStart Template for new AD Forest, Domain and DC.

Meaning you can deploy this template first and the use the samples and deploy to same resource group to test the domain joining with linked template.


In the MainTemplate fill in the variables, here are the secrets from key vault that will be passed to the linked template.

 “variables”: {

        “templatelink”: “enter the template URL to the blob storage”,

        “keyVaultRef”: “enter the key vault resource id”,

        “domainPassword”: “key vault reference for the domain password”,

        “domainUsername”: “key vault reference for the domain account name”,

        “artifactsLocationSasToken”: “SaS token for the blob storage”

The Linked Template is to be places somewhere you can reach it when running the main template. For this purpose, I used a Blob storage container with a SaS key (“artifactsLocationSasToken”).

The keyVaultRef is the key vault resource ID.

The domainpassword and domainusername are the secrets in the key vault.


Fill in some variables in the LinkedTemplate before uploading to the Blob

“variables”: {

        “location”: “westeurope”,

        “virtualNetworkName”: “adVNET”,

        “vnetId”: “[resourceId(‘ADTestForrest’,’Microsoft.Network/virtualNetworks’, variables(‘virtualNetworkName’))]”,

        “subnetRef”: “[concat(variables(‘vnetId’), ‘/subnets/’, parameters(‘subnetName’))]”,

        “adminUsername”: “Insert desired local admin”,

        “adminPassword”: “Chose a password”,

        “blobStorageEndpoint”: “”,

        “sizeOfDataDisksInGB”: 32,

        “networkInterfaceName”: “[concat(parameters(‘virtualMachineName’), ‘-nic1’)]”,

        “osDiskName”: “[concat(parameters(‘virtualMachineName’), ‘-osDisk’)]”,

        “domainToJoin”: “”,

        “domainOuPath”: “OU=enter the ou path,DC=yourdomain,DC=com”,

        “domainJoinOptions”: 3


I complained a bit about the lack of documentation in part one.

This extension exists currently in 3 versions.

I cannot find documentation that explains what differs in these versions.

Also the domainJoinOptions would be nice to have some documentation on, currently this is the closest thing to documentation I can find.

I recall finding something more about last time but cannot find it now…

But keep the option to 3 and you will be fine 🙂

“domainJoinOptions”: 3

“properties”: {

                “publisher”: “Microsoft.Compute”,

                “type”: “JsonADDomainExtension”,

                “typeHandlerVersion”: “1.3”,

                “autoUpgradeMinorVersion”: true,

                “settings”: {

                    “Name”: “[variables(‘domainToJoin’)]”,

                    “OUPath”: “[variables(‘DomainOuPath’)]”,

                    “User”: “[concat(variables(‘domainToJoin’), ‘\\’, parameters(‘domainUsername’))]”,

                    “Restart”: “true”,

                    “Options”: “[variables(‘domainJoinOptions’)]”


                “protectedSettings”: {

                    “Password”: “[parameters(‘domainPassword’)]”


When you deploy the MainTemplate you will see 2 deployments in the resource group.

One for the MainTemplate and one for the linked template.

Additional Links to read

Useful Extensions for VSCode when authoring ARM Templates

Leave a Comment