Azure ARM: VM Domain Join to Active Directory Domain with "JoinDomain" Extension

It is a pretty common scenario to provision a Virtual Machine (VM) in Azure and join it to an existing Active Directory (AD) Domain, either extended from on-premises via hybrid connections, or natively deployed in the cloud installing Domain Controllers (DCs) into Azure VMs. Using legacy Azure Service Management API (ASM), a nice PowerShell cmdlet called “Add-AzureProvisioningConfig” is available, with “-JoinDomain” switch to automatically join the VM to an AD domain as in example below:


Unfortunately, there is no more evidence of domain join functionality in the newest Azure Resource Manager (ARM) API, then how to gain back that nice feature previously available in Azure ASM? There are essentially two ways, both based on a new type of ARM “Extension” called “JsonADDomainExtension”:

  • Using Azure ARM Templates (“declarative mode”).
  • Using Azure ARM PowerShell Cmdlet (“imperative mode”).

The first approach is very simple; you can find a template sample for automatic VM joining to an AD domain at the link below in GitHub:


As you can see, this new extension type can take all the necessary parameters to join a VM to an existing AD domain, and then finally reboot the VM once the join operation is complete. If you try to search using Bing about this extension, you will not find (at least at the time of writing this post) any public reference nor documentation. Now, what happen if you don’t or want to use an ARM template and instead you have/want to use “imperative” PowerShell? What I have done below is reverse-engineering the template mentioned above and test a very simple script. First of all, you need to prepare two JSON strings as in the example below:


The first one contains all the necessary parameters/options for the domain join operation, please note that there is no documentation on the possible values for “Options”, value (3) is recommended in the GitHub template mentioned above. The second one contains protected info that the extension mechanism will encrypt transparently to guarantee security of passwords. After that, simply execute the PowerShell cmdlet below:

Execution of “Set-AzureRmVMExtension” cmdlet will require some time; you can check the status using the “Get-AzureRmVMExtension” cmdlet as in the example below (output sample included):

From the Azure Portal, you can see almost the same level of details going into the “Extension” tab of your Virtual Machine:


That’s all I wanted to share with you, hope you will find this content useful and interesting, feel free to post your comments and feedbacks here. Remember that you can also follow me on Twitter (@igorpag). Best Regards.





Comments (15)

  1. JJ says:

    Awesome! thank you

  2. E says:

    Very Useful. Thanks.

  3. veggi says:

    Hi, thank you for your excellent description of this subject. I am having trouble joining using the ARM template on certain VHD files I have made. Some work, some don't. Is there a setting on the image that needs to be set right for this to work?

  4. Hi Veggi,

    No, there is nothing special in the image to set, but based on my experience failure is 99% times originated from VM not being able to contact Active Directory domain controllers, due to DNS or account or network issue in Azure VNET.


  5. Hedge1 says:

    Worked like a charm. I ran the VM extensions after I created the VM, where Add-AzureProvisioningConfig is run prior to creating the VM.

  6. Vishal says:


    We are deployig VMs using JSON which is working fine. In the existing JSON, I have added the extension to add the VM to the Domain, while deploying the VM, I am getting an error for adding it to the Domain, it say not getting the “‘The template variable ‘apiVersion’ is not found”. Please suggest.

  7. Raj says:

    Hi Igor,
    Nice article, thanks. Although when I try to use 201 domain join template, it fails with a joindomain extension conflict error, have you run into this issue at all.?

    1. Conflict error on using the ARM template? Sorry no, never heard about that.

  8. Sandeep says:

    I am getting below error using Github template code
    “apiVersion”: “2015-06-15”,
    “type”: “Microsoft.Compute/virtualMachines/extensions”,
    “name”: “[concat(parameters(‘vmName’),’/joindomain’)]”,
    “location”: “[resourceGroup().location]”,
    “dependsOn”: [
    “[concat(‘Microsoft.Compute/virtualMachines/’, parameters(‘vmName’))]”
    “properties”: {
    “publisher”: “Microsoft.Compute”,
    “type”: “JsonADDomainExtension”,
    “typeHandlerVersion”: “1.3”,
    “autoUpgradeMinorVersion”: true,
    “settings”: {
    “Name”: “[parameters(‘domainToJoin’)]”,
    “OUPath”: “[parameters(‘ouPath’)]”,
    “User”: “[concat(parameters(‘domainToJoin’), ‘\’, parameters(‘domainUsername’))]”,
    “Restart”: “true”,
    “Options”: “[parameters(‘domainJoinOptions’)]”
    “protectedsettings”: {
    “Password”: “[parameters(‘domainPassword’)]”

    New-AzureRmResourceGroupDeployment : 11:20:11 AM – Handler ‘Microsoft.Compute.JsonADDomainExtension’ has reported failure

    for VM Extension ‘joindomain’ with terminal error code ‘1009’ and error message: ‘Enable failed
    11:20:11 – [ERROR] for plugin (name: Microsoft.Compute.JsonADDomainExtension, version 1.3) with exception Command C:\Packages\Plugins\Microsoft.Compute.JsonADDomainExtension\1.3\enable.cmd of Microsoft.Compute.JsonADDomainExtension has
    11:20:11 – [ERROR] not exited on time! Killing it…’

    1. Rony Vargas says:

      Hello I just found the reason for my error on the ARM template deployment,
      You may want to look your specific case under: %windir%\debug\netsetup.log
      In my case it was failing because of incorrect referenced username in the template.

      wrong definittion on the template I had:
      “User”: “parameters(‘domainUsername’))”
      It has missing the enclosing [ ], and the log says:

      12/20/2016 22:45:20:804 HostName: testVM
      12/20/2016 22:45:20:804 NetbiosName: testVM
      12/20/2016 22:45:20:804 Domain:
      12/20/2016 22:45:20:804 MachineAccountOU: OU=Servers,DC=example,DC=com
      12/20/2016 22:45:20:804 Account: parameters(‘domainUsername’)) ******** notice the parameter was not passed correctly
      12/20/2016 22:45:20:804 Options: 0x3


  9. Mike Bergin says:

    Sorry for this question as it is a reflection on my lack of powershell knowledge and not a problem with your script. I actually used it perfectly and LOVE it! I want to add it to my own script and am trying to use variables for the domain join info. for example: rather than having the domain name hard coded in, I have if statements to determine the domain to use. here is an example:
    IF ($SubName -eq “ProdSubcription”) {$DomainName = ‘’ }
    IF ($DomainName -eq “”) { $DomainUser = ‘Administrator’ }
    So then i want to have the script you wrote look like this:
    “Name”: “$DomainName”
    “User” : “DomainUser”

    Problem is when it runs it doesn’t see the variable, rather thinks the domain is called $DomainName…. any ideas?

    1. Edward Douse says:

      @Mike Bergin, If you’re replacing information in the script example in this post, you need to change the quotes from single to double.

      So it would start $string1 = “{ …

      Single quotes are literal strings, double quotes allow for variable replacement.


  10. Amol says:

    Hey Igor,

    Great script. It works.

  11. Dyanesh says:

    It works , but getting this error
    “Handler ‘Microsoft.Compute.JsonADDomainExtension’ has reported failure for VM Extension ‘joindomain’ with terminal error code ‘1009’ and error message: ‘Enable failed for pluginmpute.JsonADDomainExtension’ has reported failure for VM Extension ‘joindomain’ with terminal error code ‘1009’ and
    error message: ‘Enable failed for plugin (name: Microsoft.Compute.JsonADDomainExtension, version 1.3) with exception Command
    C:\\Packages\\Plugins\\Microsoft.Compute.JsonADDomainExtension\\1.3\\enable.cmd of Microsoft.Compute.JsonADDomainExtension has not exited on time! Killing
    is there any settings for timeout ?

  12. Brian Ehlert says:

    I have to ask. Does this now support joining Azure Active Directory?

Skip to main content