When you create a VM in Azure as IaaS, the domain context depends on several factors:
By default, Azure VMs are not joined to any domain. They are standalone machines in a workgroup configuration. Each VM exists independently without Active Directory integration.
Your VM does get:
vmname.internal.cloudapp.net that works within your Azure virtual networkmyvm.eastus.cloudapp.azure.com - this is just a DNS pointer, not a domain membershipIf you want your Azure VM to be part of a domain, you have several options:
1. Azure AD Domain Services (Managed)
contoso.onmicrosoft.com)2. Self-Managed AD Domain
contoso.local or corp.contoso.com)3. Hybrid Connection
4. Azure AD Join (for Windows 10/11)
The key point: Azure doesn't automatically put your IaaS VMs into any domain. You start with standalone VMs and must explicitly configure domain membership based on your identity and management needs.
Great question! You're asking about joining your Azure VM to the Azure AD (Microsoft Entra ID) tenant that contains your subscription. Here's how this works:
Your Azure subscription exists within an Azure AD tenant (now called Microsoft Entra ID). This is your organization's cloud identity directory. When you want your VM to use this same identity system, you have two main approaches:
This is the modern cloud-native approach - your VM joins directly to your Azure AD tenant without needing a traditional domain.
Best for: Windows 10/11 VMs, cloud-first scenarios, users signing in with Azure AD accounts
How to do it:
What you get:
This provides a managed traditional Active Directory domain that's synchronized with your Azure AD tenant.
Best for: Windows Server VMs, legacy apps requiring traditional AD, Group Policy needs
How to do it:
yourtenant.onmicrosoft.com or a custom domainWhat you get:
| Feature | Azure AD Join | Azure AD Domain Services |
|---|---|---|
| Setup complexity | Easy | Moderate (requires Azure AD DS setup) |
| Cost | VM cost only | VM cost + Azure AD DS cost (~$100-300/month) |
| Use case | Modern Windows 10/11 | Windows Server, legacy apps |
| Authentication | Cloud-native Azure AD | Traditional AD protocols |
| Group Policy | No (use Intune instead) | Yes |
| Sign-in | user@tenant.com | DOMAIN\user or user@domain.com |
Both approaches connect your VM to your tenant's identity system, so users in your Azure AD tenant can access the VM. The choice depends on whether you need modern cloud authentication or traditional domain services.
Would you like detailed steps for either approach?
Great idea! Let me expand the comparison table to include Azure AD Connect synchronization methods (PTA, Password Hash Sync, WriteBack). This is important context because these features are relevant when you have hybrid scenarios - connecting on-premises Active Directory to Azure AD.
Before the expanded table, let's clarify what these are:
Azure AD Connect is the tool that synchronizes your on-premises Active Directory with your Azure AD tenant. It has different authentication and sync methods:
These are relevant when you have both on-premises AD infrastructure and Azure AD, and you're deciding how to connect your Azure VMs to your identity infrastructure.
| Feature | Azure AD Join | Azure AD Domain Services | Hybrid AD Join (with PHS) | Hybrid AD Join (with PTA) |
|---|---|---|---|---|
| Setup complexity | Easy | Moderate | Complex | Complex |
| Infrastructure required | Azure AD only | Azure AD + Azure AD DS | On-prem AD + Azure AD Connect | On-prem AD + Azure AD Connect + PTA agents |
| Monthly cost estimate | VM only | VM + Azure AD DS ($100-300) | VM + on-prem infrastructure | VM + on-prem infrastructure |
| Best for | Cloud-native Windows 10/11 | Legacy apps needing traditional AD in cloud | Hybrid orgs with on-prem AD | Hybrid orgs, high security requirements |
| Authentication location | Cloud (Azure AD) | Cloud (Managed AD DS) | Cloud (Azure AD with synced hashes) | On-premises (passwords stay on-prem) |
| User sign-in format | user@tenant.com | DOMAIN\user or UPN | DOMAIN\user or UPN | DOMAIN\user or UPN |
| Requires on-prem AD | ❌ No | ❌ No | ✅ Yes | ✅ Yes |
| Requires Azure AD Connect | ❌ No | ❌ No | ✅ Yes | ✅ Yes |
| Password Hash Sync | N/A | N/A (separate sync) | ✅ Required | ❌ Not used |
| Pass-Through Authentication | N/A | N/A | ❌ Not used | ✅ Required (3+ agents) |
| Federation (ADFS) | Optional | Optional | Alternative to PHS/PTA | Alternative to PHS/PTA |
| Password WriteBack | N/A | ✅ Supported (Azure AD → Azure AD DS) | ✅ Supported (Azure AD → on-prem AD) | ✅ Supported (Azure AD → on-prem AD) |
| Device WriteBack | N/A | ❌ Not applicable | ✅ Supported | ✅ Supported |
| Group WriteBack | N/A | ❌ Not applicable | ✅ Supported (M365 groups) | ✅ Supported (M365 groups) |
| Group Policy support | ❌ No (use Intune/MDM) | ✅ Yes (full GPO) | ✅ Yes (full GPO) | ✅ Yes (full GPO) |
| LDAP support | ❌ No | ✅ Yes (LDAP/LDAPS) | ✅ Yes (via VPN to on-prem) | ✅ Yes (via VPN to on-prem) |
| Kerberos/NTLM | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes |
| SSO experience | ✅ Seamless (cloud) | ✅ Yes (via Azure AD) | ✅ Seamless (cloud + on-prem) | ✅ Seamless (cloud + on-prem) |
| Works offline | Limited (cached creds) | Yes (cached creds) | Yes (cached creds) | Yes (cached creds) |
| Conditional Access | ✅ Full support | ✅ Supported | ✅ Full support | ✅ Full support |
| MFA support | ✅ Native Azure MFA | ✅ Azure MFA | ✅ Azure MFA | ✅ Azure MFA |
| Network dependency | Internet to Azure AD | VNet to Azure AD DS | VPN/ExpressRoute to on-prem (for some features) | VPN/ExpressRoute to on-prem (for auth) |
| High availability | Azure AD (built-in) | Multiple replicas (extra cost) | Multiple DCs + Azure AD Connect | Multiple DCs + multiple PTA agents |
| Password expiry | Azure AD policy | Managed domain policy | On-prem AD policy | On-prem AD policy |
| Self-service password reset | ✅ Yes | ✅ Yes (with WriteBack to Azure AD DS) | ✅ Yes (with WriteBack to on-prem) | ✅ Yes (with WriteBack to on-prem) |
Context: You have no on-premises infrastructure, everything is cloud-native.
Context: You need traditional AD features but don't want to manage infrastructure.
Context: You have on-premises AD + Azure AD, want cloud authentication.
Context: You have on-premises AD + Azure AD, passwords must stay on-prem.
| WriteBack Type | Azure AD Join | Azure AD DS | Hybrid (PHS/PTA) |
|---|---|---|---|
| Password WriteBack | N/A (no on-prem) | Azure AD → Azure AD DS | Azure AD → On-prem AD |
| Device WriteBack | N/A | N/A | Hybrid joined device objects → On-prem AD |
| Group WriteBack | N/A | N/A | M365 Groups → On-prem AD as security groups |
| User WriteBack | ❌ Not supported | ❌ Not supported | ❌ Not supported (one-way: on-prem → cloud) |
Azure AD Join:
User → Azure AD → VM
(Direct cloud authentication)Azure AD Domain Services:
User → Azure AD → Managed Domain Controllers → VM
(Cloud-based traditional domain auth)Hybrid with Password Hash Sync:
User → Azure AD (validates with synced hash) → VM
(Cloud auth with synced credentials)Hybrid with Pass-Through Authentication:
User → Azure AD → PTA Agent → On-prem AD → Response → VM
(Cloud requests, on-prem validation)Does this help clarify how these pieces fit together for your Azure VM tenant integration scenario?
Excellent question! Let me detail all domain-related considerations for these three migration scenarios. Domain membership and identity integration are critical during VM migrations because they affect user access, application functionality, security policies, and management.
When migrating VMs, the domain relationship determines:
Questions to answer:
- Is the VM domain-joined? (Check: System Properties → Computer Name)
- Which domain? (domain.local, corp.contoso.com, etc.)
- Which OU (Organizational Unit) is it in?
- What GPOs apply to this VM?
- Does it have any domain-specific applications?
- Are service accounts domain accounts?Check for applications that use:
When: VM needs to stay domain-joined, you have on-prem AD infrastructure
Architecture:
On-Premises Azure
┌─────────────────┐ ┌──────────────────┐
│ Active Directory│◄──VPN or ───┤ Migrated VM │
│ Domain │ ExpressRoute│ (Domain-joined) │
│ Controllers │ └──────────────────┘
└─────────────────┘Steps:
nltest /dsgetdc:domain.comgpresult /rDomain Considerations:
When: Better performance needed, reducing on-prem dependencies
Architecture:
On-Premises Azure
┌─────────────────┐ ┌──────────────────┐
│ Active Directory│◄──Sync─────┤ Azure AD DCs │
│ Domain (Primary)│ │ (Replica) │
└─────────────────┘ └────────┬─────────┘
│
▼
┌──────────────────┐
│ Migrated VM │
│ (Domain-joined) │
└──────────────────┘Steps:
Domain Considerations:
When: Moving away from on-prem AD, want managed AD in cloud
Steps:
Domain Considerations:
When: Modern Windows 10/11 VM, moving to cloud-native identity
Domain Considerations:
Case A: VM in AWS Managed Microsoft AD
AWS Account
┌────────────────────────────┐
│ AWS Managed Microsoft AD │
│ (Directory Service) │
│ ├─ Directory ID: d-xxxxx │
│ └─ DNS: corp.example.com │
└────────┬───────────────────┘
│
▼
┌────────┐
│ EC2 VM │ (Domain-joined)
└────────┘Case B: VM in AWS Simple AD
AWS Account
┌────────────────────────────┐
│ AWS Simple AD │
│ (Samba-based, standalone) │
└────────┬───────────────────┘
│
▼
┌────────┐
│ EC2 VM │ (Domain-joined)
└────────┘Case C: VM joined to on-premises AD via AWS Direct Connect/VPN
On-Premises AD ◄─── Direct Connect/VPN ───► AWS EC2 VMCase D: VM in standalone/workgroup mode
EC2 VM (No domain)When: Want equivalent managed AD service in Azure
Architecture:
AWS Azure
┌──────────────────┐ ┌────────────────────┐
│ AWS Managed AD │ │ Azure AD DS │
│ corp.example.com │ ───► │ corp.example.com │
└────────┬─────────┘ Migrate └──────┬─────────────┘
│ │
┌────▼────┐ ┌────▼────┐
│ EC2 VM │ ──► │ AzureVM │
└─────────┘ └─────────┘Steps:
Domain Considerations:
When: VM needs to join your corporate on-prem AD
Architecture:
On-Premises AD
(Central Authority)
▲ ▲
│ │
┌────────────────┘ └───────────────┐
│ │
AWS VPN Azure VPN
│ │
┌───▼──┐ ┌───▼──┐
│EC2 VM│ ─────── Migrate ─────► │Azure │
└──────┘ │ VM │
└──────┘Steps:
Reset-ComputerMachinePasswordnltest /dsgetdc:domain.comDomain Considerations:
When: Migrating from Samba-based Simple AD to proper AD
Domain Considerations:
When: VM not domain-joined in AWS
Options:
Domain Considerations:
If your AWS Managed AD has a trust with on-prem:
On-Prem AD ◄─── Trust ───► AWS Managed AD ◄─── Joined ─── EC2 VMPost-migration:
If using AWS Directory Connector (proxy to on-prem):
Key Challenge: Scale Sets are designed for stateless, ephemeral VMs that can be created/destroyed dynamically. Traditional domain membership assumes stateful, persistent VMs.
Scale Set Event │ VM Lifecycle │ Domain Impact
───────────────────┼────────────────────────┼──────────────────────
Scale Out │ New VM created │ Needs domain join
Scale In │ VM deleted │ Orphaned AD account
Update/Reimage │ VM recreated │ Lost domain membership
Auto-healing │ VM replaced │ New computer accountConsider alternatives:
Domain join VMSS only if:
Current Azure VM Status:
- Domain-joined? Which domain?
- Domain-dependent applications?
- Service accounts in use?
- GPOs applied?
- Static or dynamic IP?
- Computer name format?When: Need traditional domain join in scale set
Architecture:
Azure Scale Set
├─ VM Instance 0 ──┐
├─ VM Instance 1 ──┼─► Custom Script Extension
├─ VM Instance 2 ──┤ └─ Auto-domain join script
└─ VM Instance N ──┘
│
▼
Domain Controllers
(Azure AD DS or Hybrid AD)Implementation:
# domain-join.ps1
param(
[string]$domainName = "corp.contoso.com",
[string]$ouPath = "OU=ScaleSetVMs,DC=corp,DC=contoso,DC=com"
)
# Get domain join credentials from Key Vault
$username = "domain-joiner@corp.contoso.com"
$password = (Get-AzKeyVaultSecret -VaultName "myvault" -Name "domain-join-password").SecretValueText | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($username, $password)
# Generate unique computer name (scale set + instance ID)
$vmssName = (Invoke-RestMethod -Uri 'http://169.254.169.254/metadata/instance?api-version=2021-02-01' -Headers @{"Metadata"="true"} -Method GET).compute.vmScaleSetName
$instanceId = (Invoke-RestMethod -Uri 'http://169.254.169.254/metadata/instance?api-version=2021-02-01' -Headers @{"Metadata"="true"} -Method GET).compute.name
$computerName = "$vmssName-$instanceId" # e.g., "webvmss-0"
# Rename computer if needed
if ($env:COMPUTERNAME -ne $computerName) {
Rename-Computer -NewName $computerName -Force
}
# Join domain
Add-Computer -DomainName $domainName -OUPath $ouPath -Credential $credential -Force -Restart{
"type": "Microsoft.Compute/virtualMachineScaleSets",
"properties": {
"virtualMachineProfile": {
"extensionProfile": {
"extensions": [
{
"name": "DomainJoinExtension",
"properties": {
"publisher": "Microsoft.Compute",
"type": "CustomScriptExtension",
"typeHandlerVersion": "1.10",
"autoUpgradeMinorVersion": true,
"settings": {
"fileUris": ["https://mystorageaccount.blob.core.windows.net/scripts/domain-join.ps1"]
},
"protectedSettings": {
"commandToExecute": "powershell -ExecutionPolicy Unrestricted -File domain-join.ps1"
}
}
}
]
}
}
}
}Domain Considerations:
When: Simpler configuration, Windows VMs only
Implementation:
{
"type": "Microsoft.Compute/virtualMachineScaleSets",
"properties": {
"virtualMachineProfile": {
"extensionProfile": {
"extensions": [
{
"name": "JsonADDomainExtension",
"properties": {
"publisher": "Microsoft.Compute",
"type": "JsonADDomainExtension",
"typeHandlerVersion": "1.3",
"autoUpgradeMinorVersion": true,
"settings": {
"Name": "corp.contoso.com",
"OUPath": "OU=ScaleSetVMs,DC=corp,DC=contoso,DC=com",
"User": "corp.contoso.com\\domain-joiner",
"Restart": "true",
"Options": "3"
},
"protectedSettings": {
"Password": "[parameters('domainJoinPassword')]"
}
}
}
]
}
}
}
}Domain Considerations:
When: Windows 10/11 VMs in scale set, cloud-native approach
Implementation:
{
"type": "Microsoft.Compute/virtualMachineScaleSets",
"properties": {
"virtualMachineProfile": {
"extensionProfile": {
"extensions": [
{
"name": "AADLoginForWindows",
"properties": {
"publisher": "Microsoft.Azure.ActiveDirectory",
"type": "AADLoginForWindows",
"typeHandlerVersion": "1.0",
"autoUpgradeMinorVersion": true
}
}
]
}
},
"identity": {
"type": "SystemAssigned"
}
}
}
```
**Domain Considerations:**
- ✅ **No orphaned computer accounts** - Azure AD manages identity
- ✅ **No domain join credentials** needed
- ✅ **Modern authentication** - Azure AD, MFA, Conditional Access
- ✅ **Automatic cleanup** when VMs scale in
- ❌ **No Group Policy** (use Intune instead)
- ❌ **No traditional domain services** (LDAP, Kerberos)
- ❌ **Limited to Windows 10/11** and Windows Server 2019+ (with limitations)
### **Option D: Hybrid Azure AD Join for Scale Sets**
**When:** Scale set VMs need both on-prem domain AND Azure AD registration
**Architecture:**
```
Scale Set VM ─┬─► On-Prem/Azure AD (Domain-joined)
└─► Azure AD (Registered for cloud services)Implementation:
{
"name": "AADLoginForWindows",
"properties": {
"publisher": "Microsoft.Azure.ActiveDirectory",
"type": "AADLoginForWindows",
"typeHandlerVersion": "1.0",
"autoUpgradeMinorVersion": true,
"settings": {
"mdmId": "0000000a-0000-0000-c000-000000000000" // Intune enrollment
}
}
}Domain Considerations:
Problem: When scale set scales in (removes VMs), computer accounts remain in Active Directory.
Solutions:
A. PowerShell Automation (Proactive Cleanup)
# Run periodically (Azure Automation Runbook)
$vmss = Get-AzVmss -ResourceGroupName "rg-app" -VMScaleSetName "vmss-web"
$vmInstances = $vmss.Instances.InstanceId
# Get AD computers for this VMSS
$adComputers = Get-ADComputer -Filter "Name -like 'vmss-web-*'" -SearchBase "OU=ScaleSetVMs,DC=corp,DC=contoso,DC=com"
foreach ($adComputer in $adComputers) {
$instanceId = $adComputer.Name -replace 'vmss-web-', ''
if ($instanceId -notin $vmInstances) {
# Orphaned - remove from AD
Remove-ADComputer -Identity $adComputer -Confirm:$false
Write-Output "Removed orphaned computer: $($adComputer.Name)"
}
}B. Event-Driven Cleanup (Azure Function)
// Trigger on scale set events
// Azure Event Grid → Azure Function → Remove AD computer
[FunctionName("CleanupOrphanedComputers")]
public static async Task Run(
[EventGridTrigger] EventGridEvent eventGridEvent,
ILogger log)
{
if (eventGridEvent.EventType == "Microsoft.Compute.VirtualMachineScaleSets.VirtualMachineDeleted")
{
var vmName = eventGridEvent.Subject.Split('/').Last();
// Remove computer from AD
await RemoveComputerFromAD(vmName);
}
}C. Short-Lived Computer Accounts
Problem: NetBIOS names limited to 15 characters, scale sets generate long names.
Solution:
# Implement smart naming strategy
$vmssName = "web" # Keep short (3-5 chars)
$instanceId = "00012" # Padded instance ID
$environment = "p" # p=prod, d=dev (1 char)
$region = "e" # e=east, w=west (1 char)
# Result: webpe00012 (10 chars, room for growth)
$computerName = "$vmssName$environment$region$instanceId"Problem: VM provisioned before network/DNS ready, domain join fails.
Solution:
# Add retry logic to domain join script
$maxRetries = 5
$retryCount = 0
$joined = $false
while (-not $joined -and $retryCount -lt $maxRetries) {
try {
# Test domain connectivity first
$dc = Resolve-DnsName -Name "_ldap._tcp.dc._msdcs.$domainName" -Type SRV -ErrorAction Stop
# Domain reachable, attempt join
Add-Computer -DomainName $domainName -Credential $credential -Force -ErrorAction Stop
$joined = $true
Write-Output "Successfully joined domain"
}
catch {
$retryCount++
Write-Output "Domain join attempt $retryCount failed: $_"
Start-Sleep -Seconds 30
}
}
if (-not $joined) {
Write-Error "Failed to join domain after $maxRetries attempts"
exit 1
}Problem: VMs boot before GPOs apply, applications fail.
Solution:
# Add to domain join script - wait for GPO application
Add-Computer -DomainName $domainName -Credential $credential -Force
# Force GPO update
Start-Sleep -Seconds 60
gpupdate /force /wait:0
# Verify critical GPOs applied
$gpos = gpresult /r
if ($gpos -notmatch "Critical-GPO-Name") {
Write-Warning "Critical GPO not applied, forcing sync"
gpupdate /force /sync
}
# Restart to ensure all GPOs apply at startup
Restart-Computer -ForceProblem: Scale set instances run services as domain accounts, credentials need distribution.
Solution:
A. Group Managed Service Accounts (gMSA) - Recommended
# One-time setup in AD
New-ADServiceAccount -Name "gmsa-webservice" `
-DNSHostName "gmsa-webservice.corp.contoso.com" `
-PrincipalsAllowedToRetrieveManagedPassword "VMSS-WebServers$"
# In VMSS startup script
Install-ADServiceAccount -Identity "gmsa-webservice"
Set-Service -Name "MyWebService" -StartupType Automatic `
-Credential "CORP\gmsa-webservice$"B. Azure Key Vault
# VMSS uses Managed Identity to get credentials
$credential = Get-AzKeyVaultSecret -VaultName "myvault" -Name "service-account-password"| Aspect | Single Azure VM | Azure VM Scale Set |
|---|---|---|
| Domain join method | Manual or extension (one-time) | Automated via extension (every instance) |
| Computer account lifecycle | Static, permanent | Dynamic, ephemeral |
| Orphaned accounts | Rare (only if VM deleted) | Common (every scale-in event) |
| Computer naming | Administrator chooses | Auto-generated (needs strategy) |
| GPO application | Standard, predictable | Timing challenges, must handle |
| Service accounts | Can use regular domain accounts | Should use gMSA or Managed Identity |
| Domain join credentials | Can be manual | Must be automated (Key Vault) |
| DNS requirements | Configure once | Every instance must resolve DCs |
| Cleanup automation | Not needed | Essential |
| Identity preference | Traditional domain join fine | Consider Azure AD Join |
| Stateful workloads | ✅ Suitable | ⚠️ Challenging |
| Stateless workloads | ⚠️ Over-provisioned | ✅ Ideal |
| Current State | Recommended VMSS Domain Strategy | Rationale |
|---|---|---|
| Domain-joined Windows Server with legacy apps | Domain join via JsonADDomainExtension + automated cleanup | Maintains compatibility, requires cleanup automation |
| Domain-joined Windows Server with modern apps | Consider breaking domain dependency, use Managed Identity | Simplifies management, cloud-native |
| Domain-joined Windows 10/11 | Azure AD Join | Modern, stateless, no cleanup needed |
| Hybrid Azure AD joined | Hybrid Azure AD Join for VMSS | Maintains SSO, cloud+on-prem access |
| Azure AD joined | Azure AD Join for VMSS | Continue cloud-native approach |
| Standalone/workgroup | Keep standalone or Azure AD Join | Simplest, no domain overhead |
# Document domain configuration
Get-WmiObject -Class Win32_ComputerSystem | Select Domain, PartOfDomain
gpresult /h gpo-report.html
Get-Service | Where {$_.StartType -eq 'Automatic'} | Select Name, StartNameTest orphaned account removal
# Generalize VM
az vm deallocate --resource-group rg-app --name vm-web
az vm generalize --resource-group rg-app --name vm-web
# Create image
az image create --resource-group rg-images --name img-web-v1 \
--source vm-web az vmss create \
--resource-group rg-app \
--name vmss-web \
--image img-web-v1 \
--upgrade-policy-mode Automatic \
--instance-count 2 \
--vnet-name vnet-prod \
--subnet subnet-web \
--lb lb-web \
--admin-username azureuser \
--generate-ssh-keys
# Add domain join extension
az vmss extension set \
--vmss-name vmss-web \
--resource-group rg-app \
--name JsonADDomainExtension \
--publisher Microsoft.Compute \
--version 1.3 \
--settings '{"Name": "corp.contoso.com", "OUPath": "OU=ScaleSetVMs,DC=corp,DC=contoso,DC=com", "User": "domain-joiner@corp.contoso.com", "Restart": "true", "Options": "3"}' \
--protected-settings '{"Password": "SecurePassword123!"}' # Connect to instance
$instance = Get-AzVmssVM -ResourceGroupName "rg-app" -VMScaleSetName "vmss-web" -InstanceId "0"
# Verify domain membership
Invoke-AzVMRunCommand -ResourceGroupName "rg-app" -VMScaleSetName "vmss-web" -InstanceId "0" `
-CommandId "RunPowerShellScript" `
-ScriptString 'Get-WmiObject -Class Win32_ComputerSystem | Select Domain, PartOfDomain'✅ Do:
❌ Don't:
✅ Do:
❌ Don't:
✅ Do:
❌ Don't:
This comprehensive guide covers all domain considerations for these three migration scenarios. The key is understanding that domain membership is critical infrastructure that requires careful planning, testing, and automation, especially in dynamic environments like scale sets.