Search code examples
azureazure-active-directoryazure-virtual-machineazure-virtual-network

Azure VM: AzureADJoined is "No" even after enabling the AADLoginForWindows extension


I have created a VM in Azure as mentioned below

resource "azurerm_windows_virtual_machine" "virtual_machine_hub" {
  name                = "vm-hub"
  resource_group_name = azurerm_resource_group.ipz12-dat-np-connection-rg.name
  location            = azurerm_resource_group.ipz12-dat-np-connection-rg.location
  size                = "Standard_B8ms"
  admin_username      = "xxxxx"
  admin_password      = "xxxxx"
  network_interface_ids = [
    azurerm_network_interface.virtual_machine_hub_nic.id
  ]

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsDesktop"
    offer     = "Windows-10"
    sku       = "21h1-pro"
    version   = "latest"
  }

  depends_on = [
    azurerm_network_interface.virtual_machine_hub_nic
  ]    
}

and enabled the AADLoginForWindows extension

resource "azurerm_virtual_machine_extension" "virtual_machine_hub_ext" {
  name                 = "AADLoginForWindows"
  virtual_machine_id   = azurerm_windows_virtual_machine.virtual_machine_hub.id
  type                       = "AADLoginForWindows"
  type_handler_version       = "1.0"
  auto_upgrade_minor_version = true
  publisher                  = "Microsoft.Azure.ActiveDirectory"

  depends_on = [
    azurerm_windows_virtual_machine.virtual_machine_hub
  ] }

however "dsregcmd /status" command shows that it is not connected with Azure AD domain like AzureADJoined is "No"

enter image description here

In order to register the VM in Azure AD, I don't feel that I have the appropriate permissions. If that's the case, what level of permission is required? and what am I missing?

Note: I have manually joined Azure AD like mentioned below

enter image description here

enter image description here


Solution

  • As discussed here, it is described that aadj private extension also to be created for a virtual machine under path:

    HKLM\SOFTWARE\Microsoft\RDInfraAgent\AADJPrivate

    So, another Custom script extension was added to add the key AADJPRIVATE for the VM.

    Followed this template given by @Ansuman Bal in SO thread for Azure AD VM join and made a few changes to achieve the expected results as per your requirements.

    vi main.tf:

    terraform {
      required_providers {
        azurerm = {
          source = "hashicorp/azurerm"
          version = "3.37.0"
        }
      }
    }
    provider "azurerm" {
      features {}
    }
    provider "azuread" {}
    
    data "azuread_group" "aad_group" {
      display_name = "<ADGroup>"
      security_enabled = true
    }
    data "azurerm_role_definition" "vm_user_login" {
      name = "<VM User Login>"
    }
    resource "azurerm_role_assignment" "vm_user_role" {
      scope              = azurerm_resource_group.rg-xxx.id
      role_definition_id = data.azurerm_role_definition.vm_user_login.id
      principal_id       = data.azuread_group.aad_group.id
    }
    
    data "azurerm_role_definition" "desktop_user" { 
      name = "xxxxxx User"
    }
    
    resource "azurerm_role_assignment" "desktop_role" {
      scope              = azurerm_virtual_desktop_application_group.desktopapp.id
      role_definition_id = data.azurerm_role_definition.desktop_user.id
      principal_id       = data.azuread_group.aad_group.id
    }
    
    resource "azurerm_resource_group" "eg-RG" {
      name     = "xxxxxtest"
      location = "West Europe"
    }
    
    resource "azurerm_virtual_network" "vnet" {
      name                = "xxxx-vnet"
      location            = azurerm_resource_group.eg-RG.location
      resource_group_name = azurerm_resource_group.eg-RG.name
      address_space       = ["10.0.0.0/16"]
    }
    
    resource "azurerm_subnet" "xxxxdefaultSubnet" {
      name           = "xxxxxsubnet"
      resource_group_name = azurerm_resource_group.eg-RG.name
      virtual_network_name = azurerm_virtual_network.vnet.name
      address_prefixes = ["10.0.0.0/24"]
    }
    
    resource "azurerm_network_security_group" "nsg" {
      name                = "xxxx-nsg"
      location            = azurerm_resource_group.eg-RG.location
      resource_group_name = azurerm_resource_group.eg-RG.name
      security_rule {
        name                       = "allow-rdp"
        priority                   = 100
        direction                  = "Inbound"
        access                     = "Allow"
        protocol                   = "Tcp"
        source_port_range          = "*"
        destination_port_range     = 3389
        source_address_prefix      = "*"
        destination_address_prefix = "*"
      }
    }
    
    resource "azurerm_subnet_network_security_group_association" "nsg_association" {
      subnet_id                 = azurerm_subnet.xxxxdefaultSubnet.id
      network_security_group_ID = azurerm_network_security_group.<nsg>.id
    }
    
    resource "time_rotating" Gen_token" {
      rotation_days = 30
    }
    
    resource "azurerm_virtual_desktop_host_pool" "new-hp" {
      location            = azurerm_resource_group.eg-RG.location
      resource_group_name = azurerm_resource_group.eg-RG.name
    
      name                     = "xxxxxhostpool"
      friendly_name            = "samplepool"
      validate_environment     = true
      start_vm_on_connect      = true
      custom_rdp_properties    = "audiocapturemode:i:1;audiomode:i:0;targetisaadjoined:i:1;"
      description              = "host-poool demo"
      type                     = "Pooled"
      maximum_sessions_allowed = 10
      load_balancer_type       = "DepthFirst"
     }
    resource "azurerm_virtual_desktop_host_pool_registration_info" "reginfo" {
      hostpool_id     = azurerm_virtual_desktop_host_pool.new-hp.id
      expiration_date = time_rotating.avd_token.rotation_rfc3339
    }
      
    resource "azurerm_virtual_desktop_application_group" "desktopapp" {
      name                = "xxxx-Desktop"
      location            = azurerm_resource_group.eg-RG.location
      resource_group_name = azurerm_resource_group.eg-RG.name
      type          = "Desktop"
      host_pool_id  = azurerm_virtual_desktop_host_pool.new-hp.id
      friendly_name = "xxxxx"
      description   = "xxxxapplications"
    }
    
    resource "azurerm_virtual_desktop_workspace" "workspace" {
      name                = "xxxxxx-WORKSPACE"
      location            = azurerm_resource_group.eg-RG.location
      resource_group_name = azurerm_resource_group.eg-RG.name
      friendly_name = "xxxxxxx"
      description   = "Purporse"
    }
    
    resource "azurerm_virtual_desktop_workspace_application_group_association" "sampleworkspaceremoteapp" {
      workspace_id         = azurerm_virtual_desktop_workspace.workspace.id
      application_group_id = azurerm_virtual_desktop_application_group.desktopapp.id
    }
    
    
    resource "azurerm_network_interface" "xxxx_nic" {
        count=2
      name                = "xxxx-${count.index}"
      location            = azurerm_resource_group.eg-RG.location
      resource_group_name = azurerm_resource_group.eg-RG.name
    
      ip_configuration {
        name                          = "internal"
        subnet_id                     = azurerm_subnet.xxxxxdefaultSubnet.id
        private_ip_address_allocation = "Dynamic"
      }
    }
    
    resource "azurerm_windows_virtual_machine" "sessionhost" {
      depends_on = [
          azurerm_network_interface.sessionhost_nic
      ]
      count=2
      name                = "xxxvm-${count.index}"
      resource_group_name = azurerm_resource_group.eg-RG.name
      location            = azurerm_resource_group.eg-RG.location
      size                = "Standard_B2MS"
      admin_username      = "useradmin"
      admin_password      = "<Password>"
      provision_vm_agent = true
      
      network_interface_ids = [azurerm_network_interface.sessionhost_nic.*.id[count.index]]
    
      identity {
        type  = "SystemAssigned"
      }
      
      os_disk {
        caching              = "ReadWrite"
        storage_account_type = "Premium_LRS"
      }
    
     source_image_reference {
        publisher = "MicrosoftWindowsDesktop"
        offer     = "Windows-10"
        sku       = "20h2-evd"
        version   = "latest"
      }
    }
    
    locals {
      registration_token = "${azurerm_virtual_desktop_host_pool_registration_info.reginfo.token}"
      shutdown_command     = "shutdown -r -t 10"
      exit_code_hack       = "exit 0"
      commandtorun         = "New-Item -Path HKLM:/SOFTWARE/Microsoft/RDInfraAgent/AADJPrivate"
      powershell_command   = "${local.commandtorun}; ${local.shutdown_command}; ${local.exit_code_hack}"
    }
    
    resource "azurerm_virtual_machine_extension" "xxxAVDModule" {
      depends_on = [
          azurerm_windows_virtual_machine.xxx_sessionhost
      ]
      count = 2
      name                 = "Microsoft.PowerShell.DSC"
      virtual_machine_id   = azurerm_windows_virtual_machine.avd_sessionhost.*.id[count.index]
      publisher            = "Microsoft.Powershell"
      type                 = "DSC"
      type_handler_version = "2.73"
      settings = <<-SETTINGS
        {
            "modulesUrl": "https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_11-22-2021.zip",
            "ConfigurationFunction": "Configuration.ps1\\AddSessionHost",
            "Properties" : {
              "hostPoolName" : "${azurerm_virtual_desktop_host_pool.new-hp.name}",
              "aadJoin": true
            }
        }
    SETTINGS
    
      protected_settings = <<PROTECTED_SETTINGS
      {
        "properties": {
          "registrationInfoToken": "${azurerm_virtual_desktop_host_pool_registration_info.reginfo.token}"
        }
      }
    PROTECTED_SETTINGS
    
    }
    resource "azurerm_virtual_machine_extension" "AADLoginForWindows" {
      depends_on = [
          azurerm_windows_virtual_machine.xxxx_sessionhost,
            azurerm_virtual_machine_extension.AVDModule
      ]
      count = 2
      name                 = "AADLoginForWindows"
      virtual_machine_id   = azurerm_windows_virtual_machine.avd_sessionhost.*.id[count.index]
      publisher            = "Microsoft.Azure.ActiveDirectory"
      type                 = "AADLoginForWindows"
      type_handler_version = "1.0"
      auto_upgrade_minor_version = true
    }
    resource "azurerm_virtual_machine_extension" "xxxxaadjprivate" {
        depends_on = [
          azurerm_virtual_machine_extension.AADLoginForWindows
        ]
        count = 2
      name                 = "AADJPRIVATE"
      virtual_machine_id =    azurerm_windows_virtual_machine.avd_sessionhost.*.id[count.index]
      publisher            = "Microsoft.Compute"
      type                 = "CustomScriptExtension"
      type_handler_version = "1.9"
      settings = <<SETTINGS
        {
            "commandToExecute": "powershell.exe -Command \"${local.powershell_command}\""
        }
    SETTINGS
    }
    

    terraform plan:

    enter image description here

    terraform apply:

    enter image description here

    VM Joined in AD after deployment:

    enter image description here

    Virtual Machine Overview:

    enter image description here