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

In part one I talked about Prerequisites and Key Vault

In part two I talked about ARM Template and JsonADDomainExtension details

In this part we are doing a deployment for domain join, a summary and pointing out some “flaws”.

So now we have everything in place.

  • OU path
  • Key Vault that is enabled for ARM deployment
  • Secrets in the key vault for the domain join account and password
  • MainTemplate
  • Storage account and container blob
  • LinkedTemplate in Storage Blob and SaS key protected

Run the template

Let’s run the MainTemplate, for demo sake lets run it in the Portal and custom template deployment.

Paste in the complete content from the MainTemplate.json.

Edit the variables – See part-2 for details

“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”

Save, klick I agree to purchase, and off to deployment.

If you check at Deployment in the Resource group, you can see that there are 2 templates being deployed.

After 5-10 minutes you will get a successful deployment, note the JsonADDomanExtension

This indicates that the domainjoin has been done, we can check more detail on the server and the extension itself.

That’s nice, let’s check the AD also.

So, there it is, domain join the VM with ARM templates and Key Vault.

The linked Template is secured with SaS token, the credentials are secured with Key Vault and the VM is domain joined as expected.


As you might have noticed there is some work to be done to get this working, mostly on the Linked Template setup.

So, lets deploy another VM and domain join it, but this time, by “mistake” I will switch place for Key Vault ref for Account and Password.

“domainPassword”: “Accountfordomainjoin”,

“domainUsername”: “Passfordomainjoin”,

In my Key Vault I have the secrets like this, under TYPE I have added the actual secret just for showcase this event.

Deploy the template again with the credential and password switched.

The VM will be provisioned with a failed state on the extension.

Digging into the failed message it tells me  the VM reported and failure for “joindomain”

Now to the interesting part, as I mentioned in Part 1, when an extension fails you get more info on the extension and also logging into the VM and looking at the logs.

So, going to the extension I can see Provisioning failed.

If I, then dig into the extension I get a little bit more info about error when joining domain.

But then looking at the detailed status is getting more info, and I can see that it is complaining on the user. BUT I also get returned from the Key Vault the secret in plain text. Since I Switched the username and password reference the error message tells me the username name is wrong.



“code”: “ComponentStatus/JoinDomainException for Option 3 meaning ‘User Specified’/failed/1”,

“level”: “Error”,

“displayStatus”: “Provisioning failed”,

“message”: “ERROR – Failed to join domain=’’, ou=’OU=AzureVM,DC=modernit,DC=com’, user=’\\Passinplaintext99!‘, option=’NetSetupJoinDomain, NetSetupAcctCreate’ (#3 meaning ‘User Specified’). Error code 1326″



“code”: “ComponentStatus/JoinDomainException for Option 1 meaning ‘User Specified without NetSetupAcctCreate’/failed/1”,

“level”: “Error”,

“displayStatus”: “Provisioning failed”,

“message”: “ERROR – Failed to join domain=’’, ou=’OU=AzureVM,DC=modernit,DC=com’, user=’\\Passinplaintext99!’, option=’NetSetupJoinDomain’ (#1 meaning ‘User Specified without NetSetupAcctCreate’). Error code 1326″



This is really per design, Key Vault hands over the secret on request, the JsonADDomainExtension tells you what is wrong.

My reflection here is that doing all the extra setup for domainjoin with key vault loses its charm here.

But not really anyone’s fault, you could just wish that the JsonADDomainExtension would be better documented and that you could request this to be masked in the error message.


Writing this series, I realized it might make this seem more complex than intended, it’s kind of happened due to trying to give details.

I have made a small Repo with a short guide to setup a test environment.

You find it here and can go back to this series for details if needed.

Leave a Comment