Task 01: OU Creation & Pre-Creation Artifacts
DOCUMENT CATEGORY: Runbook SCOPE: Active Directory preparation PURPOSE: Create Azure Local OU and LCM user account MASTER REFERENCE: Microsoft Learn - AD Prerequisites
Status: Active Estimated Time: 15 minutes Last Updated: 2026-01-31
Overview
Create the Azure Local OU container and LCM (Lifecycle Manager) user account using Microsoft's AsHciADArtifactsPreCreationTool.
Prerequisites
| Requirement | Description |
|---|---|
| Permissions | Create OU and user account in AD |
| Module | AsHciADArtifactsPreCreationTool v2402+ |
AD structure should be defined during the planning phase. Update variables below for your environment.
Variables from variables.yml
| Variable Path | Type | Description |
|---|---|---|
identity.active_directory.ad_clusters_ou_path | string | OU path for Azure Local cluster computer objects |
identity.active_directory.ad_domain_fqdn | string | Active Directory domain FQDN |
identity.accounts.account_lcm_username | string | Lifecycle Manager deployment account username |
identity.accounts.account_lcm_password | string | LCM account password (keyvault:// URI) |
platform.kv_platform_name | string | Key Vault name for secret retrieval |
azure_vms.dc01.resource_group | string | Resource group of the domain controller VM |
azure_vms.dc01.name | string | Domain controller VM name (for AzVM execution) |
azure_vms.dc01.hostname | string | Domain controller hostname (for Arc execution) |
Execution
- Standalone Script
- Azure VM Run Command
- Azure Arc Run Command
Run directly on a domain controller or domain-joined machine with AD tools installed (e.g., RSAT).
# Adjust variable values for your environment; these are examples.
$lcm_password = '<password>' # Replace with a strong password for LCM user
$lcm_user = 'AzureLocalDeployUser' # LCM user name
$OuPath = "OU=AzureLocal-Cluster01,OU=AzureLocal,OU=Servers,DC=hybrid,DC=mgmt" # OU path
$password = ConvertTo-SecureString $lcm_password -AsPlainText -Force
$user = $lcm_user
$credential = New-Object System.Management.Automation.PSCredential ($user, $password)
if (-not (Get-Module -ListAvailable -Name AsHciADArtifactsPreCreationTool)) {
Install-Module AsHciADArtifactsPreCreationTool -Repository PSGallery -Force
}
if (-not (Get-KdsRootKey -ErrorAction SilentlyContinue)) {
Add-KdsRootKey -EffectiveTime (Get-Date).AddHours(-10)
}
New-HciAdObjectsPreCreation -AzureStackLCMUserCredential $credential -AsHciOUName $OuPath -Verbose
When to use: Run remotely against an Azure VM (e.g., domain controller) using
Invoke-AzVMRunCommand. No RDP or direct connectivity required — commands are sent through the Azure fabric.
Script
Primary: scripts/deploy/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/powershell/Invoke-OUCreation-AzVM.ps1
Alternatives:
| Variant | Path |
|---|---|
| Azure CLI | scripts/deploy/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/azure-cli/Invoke-OUCreation-AzVM.ps1 |
| Bash | scripts/deploy/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/bash/invoke-ou-creation-azvm.sh |
Code
Run remotely against an Azure VM (e.g., domain controller) using Invoke-AzVMRunCommand. No RDP or direct connectivity required — commands are sent through the Azure fabric.
Prerequisites
Az.Computemodule installed (Install-Module Az.Compute)powershell-yamlmodule installed (Install-Module powershell-yaml)- Authenticated to Azure (
Connect-AzAccount)- Contributor or Virtual Machine Contributor role on the target VM
param(
[Parameter(Mandatory = $true)]
[string]$ConfigFile
)
$ErrorActionPreference = "Stop"
# Load configuration
Import-Module powershell-yaml -ErrorAction Stop
$config = Get-Content $ConfigFile -Raw | ConvertFrom-Yaml
# Resolve VM target from config
$ResourceGroupName = $config.azure_vms.dc01.resource_group
$VMName = $config.azure_vms.dc01.name
# Extract AD values from config
$clusterOUPath = $config.active_directory.ad_clusters_ou_path
if (-not $clusterOUPath) { $clusterOUPath = $config.cluster.arm_deployment.ou_path }
$lcmUsername = $config.accounts.account_lcm_username
$lcmSamAccount = if ($lcmUsername -match '^([^@]+)@') { $Matches[1] } else { $lcmUsername }
# Retrieve LCM password from Key Vault
$kvName = $config.platform.kv_platform_name
$lcmPassword = Get-AzKeyVaultSecret -VaultName $kvName -Name "lcm-password" -AsPlainText -ErrorAction Stop
# Build remote script — config values baked in as literals
$remoteScript = @"
`$ErrorActionPreference = 'Stop'
if (-not (Get-Module -ListAvailable -Name AsHciADArtifactsPreCreationTool)) {
Install-Module AsHciADArtifactsPreCreationTool -Repository PSGallery -Force
}
if (-not (Get-KdsRootKey -ErrorAction SilentlyContinue)) {
Add-KdsRootKey -EffectiveTime (Get-Date).AddHours(-10)
Write-Output 'KDS Root Key created.'
}
`$password = ConvertTo-SecureString '$lcmPassword' -AsPlainText -Force
`$credential = New-Object System.Management.Automation.PSCredential ('$lcmSamAccount', `$password)
New-HciAdObjectsPreCreation -AzureStackLCMUserCredential `$credential -AsHciOUName '$clusterOUPath' -Verbose
Write-Output 'OU creation and LCM user pre-creation complete.'
"@
# Verify Azure context
$ctx = Get-AzContext -ErrorAction Stop
if (-not $ctx) { Write-Host "ERROR: Run Connect-AzAccount first." -ForegroundColor Red; exit 1 }
# Verify VM is running
$vm = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName -Status -ErrorAction Stop
$powerState = ($vm.Statuses | Where-Object { $_.Code -like "PowerState/*" }).DisplayStatus
if ($powerState -ne 'VM running') {
Write-Host "ERROR: VM '$VMName' is not running (state: $powerState)." -ForegroundColor Red; exit 1
}
# Execute remote script
Write-Host "Executing OU creation on $VMName..." -ForegroundColor Cyan
$result = Invoke-AzVMRunCommand `
-ResourceGroupName $ResourceGroupName `
-VMName $VMName `
-CommandId "RunPowerShellScript" `
-ScriptString $remoteScript `
-ErrorAction Stop
# Display output
$stdout = $result.Value | Where-Object { $_.Code -eq "ComponentStatus/StdOut/succeeded" }
$stderr = $result.Value | Where-Object { $_.Code -eq "ComponentStatus/StdErr/succeeded" }
if ($stdout.Message) { Write-Host $stdout.Message }
if ($stderr.Message) { Write-Host "--- Errors ---" -ForegroundColor Red; Write-Host $stderr.Message -ForegroundColor Red }
Tip: If empty output is returned, verify the VM name and resource group, and confirm the VM is running.
When to use: Run remotely against an Azure Arc-enabled server. Works for on-premises domain controllers that are Arc-registered — no RDP, VPN, or direct network path required.
Script
Primary: scripts/deploy/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/powershell/Invoke-OUCreation-Arc.ps1
Alternatives:
| Variant | Path |
|---|---|
| Azure CLI | scripts/deploy/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/azure-cli/Invoke-OUCreation-Arc.ps1 |
| Bash | scripts/deploy/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/bash/invoke-ou-creation-arc.sh |
Code
Run remotely against an Azure Arc-enabled server using New-AzConnectedMachineRunCommand. This works for on-premises domain controllers that are Arc-registered — no RDP, VPN, or direct network path required.
Prerequisites
Az.ConnectedMachinemodule installed (Install-Module Az.ConnectedMachine)powershell-yamlmodule installed (Install-Module powershell-yaml)- Authenticated to Azure (
Connect-AzAccount)- Target server must be Arc-enrolled and Connected
- Azure Connected Machine Resource Administrator or Contributor role on the Arc resource
param(
[Parameter(Mandatory = $true)]
[string]$ConfigFile,
[switch]$SkipCleanup
)
$ErrorActionPreference = "Stop"
# Load configuration
Import-Module powershell-yaml -ErrorAction Stop
$config = Get-Content $ConfigFile -Raw | ConvertFrom-Yaml
# Resolve Arc target from config
$ResourceGroupName = $config.azure_vms.dc01.resource_group
$MachineName = $config.azure_vms.dc01.hostname
$Location = $config.azure_vms.dc01.location
if (-not $Location) { $Location = "eastus" }
# Extract AD values from config
$clusterOUPath = $config.active_directory.ad_clusters_ou_path
if (-not $clusterOUPath) { $clusterOUPath = $config.cluster.arm_deployment.ou_path }
$lcmUsername = $config.accounts.account_lcm_username
$lcmSamAccount = if ($lcmUsername -match '^([^@]+)@') { $Matches[1] } else { $lcmUsername }
# Retrieve LCM password from Key Vault
$kvName = $config.platform.kv_platform_name
$lcmPassword = Get-AzKeyVaultSecret -VaultName $kvName -Name "lcm-password" -AsPlainText -ErrorAction Stop
# Build remote script — config values baked in as literals
$remoteScript = @"
`$ErrorActionPreference = 'Stop'
if (-not (Get-Module -ListAvailable -Name AsHciADArtifactsPreCreationTool)) {
Install-Module AsHciADArtifactsPreCreationTool -Repository PSGallery -Force
}
if (-not (Get-KdsRootKey -ErrorAction SilentlyContinue)) {
Add-KdsRootKey -EffectiveTime (Get-Date).AddHours(-10)
Write-Output 'KDS Root Key created.'
}
`$password = ConvertTo-SecureString '$lcmPassword' -AsPlainText -Force
`$credential = New-Object System.Management.Automation.PSCredential ('$lcmSamAccount', `$password)
New-HciAdObjectsPreCreation -AzureStackLCMUserCredential `$credential -AsHciOUName '$clusterOUPath' -Verbose
Write-Output 'OU creation and LCM user pre-creation complete.'
"@
# Verify Azure context
$ctx = Get-AzContext -ErrorAction Stop
if (-not $ctx) { Write-Host "ERROR: Run Connect-AzAccount first." -ForegroundColor Red; exit 1 }
Import-Module Az.ConnectedMachine -ErrorAction Stop
# Create and execute the run command
$runCommandName = "CreateOUAndLCM-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
Write-Host "Creating run command on $MachineName..." -ForegroundColor Cyan
$null = New-AzConnectedMachineRunCommand `
-MachineName $MachineName `
-ResourceGroupName $ResourceGroupName `
-RunCommandName $runCommandName `
-Location $Location `
-SourceScript $remoteScript `
-ErrorAction Stop
# Retrieve the result
$cmd = Get-AzConnectedMachineRunCommand `
-MachineName $MachineName `
-ResourceGroupName $ResourceGroupName `
-RunCommandName $runCommandName
Write-Host "Execution State: $($cmd.InstanceViewExecutionState)" -ForegroundColor Cyan
if ($cmd.InstanceViewOutput) { Write-Host $cmd.InstanceViewOutput }
if ($cmd.InstanceViewError) {
Write-Host "--- Errors ---" -ForegroundColor Red
Write-Host $cmd.InstanceViewError -ForegroundColor Red
}
# Cleanup
if (-not $SkipCleanup) {
Remove-AzConnectedMachineRunCommand `
-MachineName $MachineName `
-ResourceGroupName $ResourceGroupName `
-RunCommandName $runCommandName `
-ErrorAction SilentlyContinue
Write-Host "Run command '$runCommandName' cleaned up." -ForegroundColor Green
} else {
Write-Host "Skipping cleanup. Run manually:" -ForegroundColor Yellow
Write-Host "Remove-AzConnectedMachineRunCommand -MachineName $MachineName -ResourceGroupName $ResourceGroupName -RunCommandName $runCommandName"
}
Tip: Use
-SkipCleanupto inspect the run command resource in the Azure portal after execution.
Verification
- Standalone Script
- Azure VM Run Command
- Azure Arc Run Command
Run directly from a domain-joined machine or a session with AD tools installed (e.g., RDP into DC, jump box, or local RSAT).
# Verify OU was created
Get-ADOrganizationalUnit -LDAPFilter "(name=AzureLocal-Cluster01)" | Select-Object DistinguishedName
# Verify LCM user was created in OU
Get-ADUser -Identity "AzureLocalDeployUser" | Select-Object Name, DistinguishedName, Enabled
# Verify KDS Root Key exists
Get-KdsRootKey | Select-Object KeyId, EffectiveTime
When to use: Run remotely against an Azure VM (e.g., domain controller) using
Invoke-AzVMRunCommand. No RDP or direct connectivity required — commands are sent through the Azure fabric.
Script
Primary: scripts/validation/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/powershell/Test-OUCreation-AzVM.ps1
Alternatives:
| Variant | Path |
|---|---|
| Azure CLI | scripts/validation/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/azure-cli/Test-OUCreation-AzVM.ps1 |
| Bash | scripts/validation/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/bash/test-ou-creation-azvm.sh |
Code
Run remotely against an Azure VM (e.g., domain controller) using Invoke-AzVMRunCommand. No RDP or direct connectivity required — commands are sent through the Azure fabric.
Prerequisites
Az.Computemodule installed (Install-Module Az.Compute)powershell-yamlmodule installed (Install-Module powershell-yaml)- Authenticated to Azure (
Connect-AzAccount)- Contributor or Virtual Machine Contributor role on the target VM
param(
[Parameter(Mandatory = $true)]
[string]$ConfigFile
)
$ErrorActionPreference = "Stop"
# Load configuration
Import-Module powershell-yaml -ErrorAction Stop
$config = Get-Content $ConfigFile -Raw | ConvertFrom-Yaml
# Resolve VM target from config
$ResourceGroupName = $config.azure_vms.dc01.resource_group
$VMName = $config.azure_vms.dc01.name
# Extract AD values from config
$adDomainFqdn = $config.active_directory.ad_domain_fqdn
if (-not $adDomainFqdn) { $adDomainFqdn = $config.active_directory.domain.fqdn }
$clusterOUPath = $config.active_directory.ad_clusters_ou_path
if (-not $clusterOUPath) { $clusterOUPath = $config.cluster.arm_deployment.ou_path }
$lcmUsername = $config.accounts.account_lcm_username
$lcmSamAccount = if ($lcmUsername -match '^([^@]+)@') { $Matches[1] } else { $lcmUsername }
# Build remote script — config values baked in as literals
$remoteScript = @"
`$ErrorActionPreference = 'Continue'
Import-Module ActiveDirectory -ErrorAction Stop
`$passed = 0; `$failed = 0; `$warned = 0
function Write-Check(`$Name, `$Status, `$Detail) {
`$icon = switch (`$Status) { 'PASS' { '✓' } 'FAIL' { '✗' } 'WARN' { '⚠' } 'INFO' { 'ℹ' } }
`$msg = " `$icon `$Name"
if (`$Detail) { `$msg += " - `$Detail" }
Write-Output `$msg
switch (`$Status) { 'PASS' { `$script:passed++ } 'FAIL' { `$script:failed++ } 'WARN' { `$script:warned++ } }
}
Write-Output "========================================="
Write-Output "Task 01: OU & Pre-Creation Validation (Remote)"
Write-Output "========================================="
Write-Output " Domain: $adDomainFqdn"
Write-Output " Cluster OU: $clusterOUPath"
Write-Output " LCM Account: $lcmSamAccount"
Write-Output ""
# CHECK: KDS Root Key
Write-Output "Checking KDS Root Key..."
try {
`$kds = Get-KdsRootKey -ErrorAction SilentlyContinue
if (`$kds) { Write-Check 'KDS Root Key' 'PASS' "Key ID: `$(`$kds[0].KeyId.ToString().Substring(0,8))..." }
else { Write-Check 'KDS Root Key' 'FAIL' 'Not found' }
} catch { Write-Check 'KDS Root Key' 'WARN' "Cannot check: `$_" }
# CHECK: OU Structure
Write-Output ""
Write-Output "Checking OU structure..."
try {
`$ou = Get-ADOrganizationalUnit -Identity "$clusterOUPath" -ErrorAction Stop
Write-Check 'Cluster OU' 'PASS' "$clusterOUPath"
} catch {
Write-Check 'Cluster OU' 'FAIL' "Not found: $clusterOUPath"
}
# CHECK: LCM User
Write-Output ""
Write-Output "Checking LCM user account..."
try {
`$user = Get-ADUser -Filter "SamAccountName -eq '$lcmSamAccount'" -Properties Enabled, DistinguishedName
if (`$user) {
Write-Check 'LCM user exists' 'PASS' "$lcmSamAccount (`$(`$user.DistinguishedName))"
if (`$user.Enabled) { Write-Check 'LCM user enabled' 'PASS' }
else { Write-Check 'LCM user enabled' 'FAIL' 'Account is disabled' }
} else { Write-Check 'LCM user exists' 'FAIL' "Not found: $lcmSamAccount" }
} catch { Write-Check 'LCM user exists' 'FAIL' "Query failed: `$_" }
# SUMMARY
Write-Output ""
Write-Output "========================================="
Write-Output "SUMMARY: Passed=`$passed Failed=`$failed Warnings=`$warned"
Write-Output "========================================="
if (`$failed -eq 0) { Write-Output '✓ AD configuration validation passed!' }
else { Write-Output "✗ `$failed check(s) failed. Review above and remediate." }
"@
# Verify Azure context
$ctx = Get-AzContext -ErrorAction Stop
if (-not $ctx) { Write-Host "ERROR: Run Connect-AzAccount first." -ForegroundColor Red; exit 1 }
# Verify VM is running
$vm = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName -Status -ErrorAction Stop
$powerState = ($vm.Statuses | Where-Object { $_.Code -like "PowerState/*" }).DisplayStatus
if ($powerState -ne 'VM running') { Write-Host "ERROR: VM is not running." -ForegroundColor Red; exit 1 }
# Execute remote script
Write-Host "Executing AD validation on $VMName..." -ForegroundColor Cyan
$result = Invoke-AzVMRunCommand `
-ResourceGroupName $ResourceGroupName `
-VMName $VMName `
-CommandId "RunPowerShellScript" `
-ScriptString $remoteScript `
-ErrorAction Stop
# Display output
$stdout = $result.Value | Where-Object { $_.Code -eq "ComponentStatus/StdOut/succeeded" }
$stderr = $result.Value | Where-Object { $_.Code -eq "ComponentStatus/StdErr/succeeded" }
if ($stdout.Message) { Write-Host $stdout.Message }
if ($stderr.Message) { Write-Host "--- Errors ---" -ForegroundColor Red; Write-Host $stderr.Message -ForegroundColor Red }
Tip: If empty output is returned, verify the VM name and resource group, and confirm the VM is running.
When to use: Run remotely against an Azure Arc-enabled server. Works for on-premises domain controllers that are Arc-registered — no RDP, VPN, or direct network path required.
Script
Primary: scripts/validation/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/powershell/Test-OUCreation-Arc.ps1
Alternatives:
| Variant | Path |
|---|---|
| Azure CLI | scripts/validation/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/azure-cli/Test-OUCreation-Arc.ps1 |
| Bash | scripts/validation/03-onprem-readiness/phase-01-active-directory/task-01-ou-creation-pre-creation-artifacts/bash/test-ou-creation-arc.sh |
Code
Run remotely against an Azure Arc-enabled server using New-AzConnectedMachineRunCommand. This works for on-premises domain controllers that are Arc-registered — no RDP, VPN, or direct network path required.
Prerequisites
Az.ConnectedMachinemodule installed (Install-Module Az.ConnectedMachine)powershell-yamlmodule installed (Install-Module powershell-yaml)- Authenticated to Azure (
Connect-AzAccount)- Target server must be Arc-enrolled and Connected
- Azure Connected Machine Resource Administrator or Contributor role on the Arc resource
param(
[Parameter(Mandatory = $true)]
[string]$ConfigFile,
[switch]$SkipCleanup
)
$ErrorActionPreference = "Stop"
# Load configuration
Import-Module powershell-yaml -ErrorAction Stop
$config = Get-Content $ConfigFile -Raw | ConvertFrom-Yaml
# Resolve Arc target from config
$ResourceGroupName = $config.azure_vms.dc01.resource_group
$MachineName = $config.azure_vms.dc01.hostname
$Location = $config.azure_vms.dc01.location
if (-not $Location) { $Location = "eastus" }
# Extract AD values from config
$adDomainFqdn = $config.active_directory.ad_domain_fqdn
if (-not $adDomainFqdn) { $adDomainFqdn = $config.active_directory.domain.fqdn }
$clusterOUPath = $config.active_directory.ad_clusters_ou_path
if (-not $clusterOUPath) { $clusterOUPath = $config.cluster.arm_deployment.ou_path }
$lcmUsername = $config.accounts.account_lcm_username
$lcmSamAccount = if ($lcmUsername -match '^([^@]+)@') { $Matches[1] } else { $lcmUsername }
# Build remote script — config values baked in as literals
$remoteScript = @"
`$ErrorActionPreference = 'Continue'
Import-Module ActiveDirectory -ErrorAction Stop
`$passed = 0; `$failed = 0; `$warned = 0
function Write-Check(`$Name, `$Status, `$Detail) {
`$icon = switch (`$Status) { 'PASS' { '✓' } 'FAIL' { '✗' } 'WARN' { '⚠' } 'INFO' { 'ℹ' } }
`$msg = " `$icon `$Name"
if (`$Detail) { `$msg += " - `$Detail" }
Write-Output `$msg
switch (`$Status) { 'PASS' { `$script:passed++ } 'FAIL' { `$script:failed++ } 'WARN' { `$script:warned++ } }
}
Write-Output "========================================="
Write-Output "Task 01: OU & Pre-Creation Validation (Arc Remote)"
Write-Output "========================================="
Write-Output " Domain: $adDomainFqdn"
Write-Output " Cluster OU: $clusterOUPath"
Write-Output " LCM Account: $lcmSamAccount"
Write-Output ""
# CHECK: KDS Root Key
Write-Output "Checking KDS Root Key..."
try {
`$kds = Get-KdsRootKey -ErrorAction SilentlyContinue
if (`$kds) { Write-Check 'KDS Root Key' 'PASS' "Key ID: `$(`$kds[0].KeyId.ToString().Substring(0,8))..." }
else { Write-Check 'KDS Root Key' 'FAIL' 'Not found' }
} catch { Write-Check 'KDS Root Key' 'WARN' "Cannot check: `$_" }
# CHECK: OU Structure
Write-Output ""
Write-Output "Checking OU structure..."
try {
`$ou = Get-ADOrganizationalUnit -Identity "$clusterOUPath" -ErrorAction Stop
Write-Check 'Cluster OU' 'PASS' "$clusterOUPath"
} catch {
Write-Check 'Cluster OU' 'FAIL' "Not found: $clusterOUPath"
}
# CHECK: LCM User
Write-Output ""
Write-Output "Checking LCM user account..."
try {
`$user = Get-ADUser -Filter "SamAccountName -eq '$lcmSamAccount'" -Properties Enabled, DistinguishedName
if (`$user) {
Write-Check 'LCM user exists' 'PASS' "$lcmSamAccount (`$(`$user.DistinguishedName))"
if (`$user.Enabled) { Write-Check 'LCM user enabled' 'PASS' }
else { Write-Check 'LCM user enabled' 'FAIL' 'Account is disabled' }
} else { Write-Check 'LCM user exists' 'FAIL' "Not found: $lcmSamAccount" }
} catch { Write-Check 'LCM user exists' 'FAIL' "Query failed: `$_" }
# SUMMARY
Write-Output ""
Write-Output "========================================="
Write-Output "SUMMARY: Passed=`$passed Failed=`$failed Warnings=`$warned"
Write-Output "========================================="
if (`$failed -eq 0) { Write-Output '✓ AD configuration validation passed!' }
else { Write-Output "✗ `$failed check(s) failed. Review above and remediate." }
"@
# Verify Azure context
$ctx = Get-AzContext -ErrorAction Stop
if (-not $ctx) { Write-Host "ERROR: Run Connect-AzAccount first." -ForegroundColor Red; exit 1 }
Import-Module Az.ConnectedMachine -ErrorAction Stop
# Create and execute the run command
$runCommandName = "ValidateADConfig-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
Write-Host "Creating run command on $MachineName..." -ForegroundColor Cyan
$null = New-AzConnectedMachineRunCommand `
-MachineName $MachineName `
-ResourceGroupName $ResourceGroupName `
-RunCommandName $runCommandName `
-Location $Location `
-SourceScript $remoteScript `
-ErrorAction Stop
# Retrieve the result
$cmd = Get-AzConnectedMachineRunCommand `
-MachineName $MachineName `
-ResourceGroupName $ResourceGroupName `
-RunCommandName $runCommandName
Write-Host "Execution State: $($cmd.InstanceViewExecutionState)" -ForegroundColor Cyan
if ($cmd.InstanceViewOutput) { Write-Host $cmd.InstanceViewOutput }
if ($cmd.InstanceViewError) { Write-Host "--- Errors ---" -ForegroundColor Red; Write-Host $cmd.InstanceViewError -ForegroundColor Red }
# Cleanup
if (-not $SkipCleanup) {
Remove-AzConnectedMachineRunCommand `
-MachineName $MachineName `
-ResourceGroupName $ResourceGroupName `
-RunCommandName $runCommandName `
-ErrorAction SilentlyContinue
Write-Host "Run command '$runCommandName' cleaned up." -ForegroundColor Green
} else {
Write-Host "Skipping cleanup (-SkipCleanup). Manual: Remove-AzConnectedMachineRunCommand -MachineName $MachineName -ResourceGroupName $ResourceGroupName -RunCommandName $runCommandName" -ForegroundColor Yellow
}
Validation Checklist
- AsHciADArtifactsPreCreationTool module installed (v2402+)
- KDS Root Key exists
- OU created with Block Inheritance enabled
- LCM user created in OU
- Credentials stored securely
Troubleshooting
| Issue | Cause | Resolution |
|---|---|---|
New-ADOrganizationalUnit fails with access denied | Insufficient permissions to create OUs | Run as Domain Admin or delegate OU creation rights on the parent container |
| KDS Root Key not effective immediately | Key needs 10-hour replication delay | Use -EffectiveImmediately in lab or wait 10 hours in production |
| AsHciADArtifactsPreCreationTool module not found | Module not installed or wrong PS version | Install via Install-Module AsHciADArtifactsPreCreationTool on PowerShell 5.1+ |
| LCM user creation fails with duplicate | Account already exists from prior attempt | Verify existing account properties match requirements or remove and recreate |
Next Steps
Proceed to Task 2 - Security Groups to create optional security groups.
Navigation
| ← Phase 01: Active Directory | ↑ Part 3: On-Premises Readiness | Task 02: Security Groups → |
Version Control
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-31 | Azure Local Cloud Azure Local Cloudnology | Initial document |
| 1.1 | 2026-03-03 | Azure Local Cloud Azure Local Cloudnology | Standardized runbook format |
End of Task