Task 05: Configure DNS Servers
DOCUMENT CATEGORY: Runbook SCOPE: DNS client configuration PURPOSE: Set DNS server addresses on the management NIC of all nodes using explicit values from
variables.yml— adapter resolved by exact name, not auto-detected MASTER REFERENCE: Phase 03: OS Configuration
Status: Active
Overview
Configure the primary and secondary DNS server addresses on the management network adapter of each Azure Local node. DNS must be set before Active Directory operations, Azure Arc registration, and any hostname resolution required later in the deployment.
Task 04 (disable DHCP) must be complete. Setting DNS before DHCP is disabled has no harmful effect, but the standard sequence is Tasks 03 → 04 → 05.
What the scripts do:
- Hard-fail on any
REPLACEplaceholder value remaining in#region CONFIGURATION - Find the management adapter by exact name — exit and list adapters if not found
- Check idempotency — exit clean if DNS is already set to the target values
- Call
Set-DnsClientServerAddresswith the two explicit DNS IPs - Read back and validate the DNS configuration
Prerequisites
| Requirement | Description | Source |
|---|---|---|
| Task 04 complete | DHCP disabled on all adapters | Task 04: Disable DHCP |
| DNS IPs confirmed | Validated DNS server addresses | variables.yml: dns.primary, dns.secondary |
| NIC name confirmed | Management adapter name | variables.yml: cluster.management_nic_name |
Configuration Reference
variables.yml path | Script variable | Example |
|---|---|---|
cluster.management_nic_name | $ManagementNIC | Embedded NIC 1 |
dns.primary | $DNSPrimary | 10.100.10.2 |
dns.secondary | $DNSSecondary | 10.100.10.3 |
nodes.<name>.management_ip | PSRemoting target (orchestrated only) | 10.100.200.11 |
Execution Options
- SConfig Utility
- Direct Script (On Node)
- Orchestrated Script (Mgmt Server)
Configure DNS interactively on each node via console, KVM, or RDP.
SConfig requires repeating these steps on every node individually. For multi-node deployments, the Direct or Orchestrated tab is faster and less error-prone.
Steps — for each node:
- Open SConfig (if not already running, type
sconfigin PowerShell) - Select option
8— Network settings - Select the management adapter (the one configured with a static IP in Task 03)
- Select option
2— Set DNS Servers - Enter the Primary DNS IP from
variables.yml→dns.primary - Enter the Secondary DNS IP from
variables.yml→dns.secondary - Press Enter to confirm
Verify in SConfig:
After setting DNS, return to the Network settings menu and confirm the DNS servers shown
match the values from variables.yml.
Alternatively, verify in PowerShell:
# Run on the node after SConfig configuration
Get-DnsClientServerAddress -AddressFamily IPv4 |
Where-Object { $_.ServerAddresses.Count -gt 0 } |
Select-Object InterfaceAlias, ServerAddresses |
Format-Table -AutoSize
Run on each node individually — via RDP, console, or KVM.
Toolkit script: scripts/deploy/04-cluster-deployment/phase-03-os-configuration/task-05-configure-dns-servers/powershell/Set-DnsServers.ps1
Edit the #region CONFIGURATION block before running:
| Variable | variables.yml path | Example |
|---|---|---|
$ManagementNIC | cluster.management_nic_name | Embedded NIC 1 |
$DNSPrimary | dns.primary | 10.100.10.2 |
$DNSSecondary | dns.secondary | 10.100.10.3 |
#Requires -Version 5.1
<#
.SYNOPSIS
Set-DnsServers.ps1
Configures primary and secondary DNS servers on the management NIC.
.DESCRIPTION
Run directly on each Azure Local node (locally or via PSRemoting).
Sets DNS server addresses on the management network adapter by exact name,
then validates the configuration.
Behaviour:
- Hard-fails on startup if any REPLACE placeholder values remain in the
configuration block — prevents accidentally pushing unconfigured values
- Finds the management adapter by exact name — lists all adapters and
exits cleanly if the name does not match
- Checks idempotency — exits clean if DNS is already set to the target values
- Sets DNS servers via Set-DnsClientServerAddress
- Validates by reading back the DNS configuration
- Optionally tests resolution against a known host
.NOTES
Author: Azure Local Cloud Azure Local Cloud
Version: 1.0.0
Phase: 03-os-configuration
Task: task-05-configure-dns-servers
Execution: Run directly on the node (console, KVM, RDP, or PSRemoting)
Prerequisites: PowerShell 5.1+, local admin rights
Run after: Task 04 — DHCP disabled on all adapters
Variables: All values come from variables.yml (see mapping table below)
variables.yml mapping:
$ManagementNIC -> cluster.management_nic_name
$DNSPrimary -> dns.primary
$DNSSecondary -> dns.secondary
.EXAMPLE
.\Set-DnsServers.ps1
#>
# ============================================================================
#region CONFIGURATION
# Edit ALL values below before running.
# Hard-fails on startup if any value still contains "REPLACE".
# ============================================================================
$ManagementNIC = "REPLACE_WITH_cluster.management_nic_name"
# Example: "Embedded NIC 1" — must match Get-NetAdapter Name exactly
$DNSPrimary = "REPLACE_WITH_dns.primary"
# Example: "10.100.10.2"
$DNSSecondary = "REPLACE_WITH_dns.secondary"
# Example: "10.100.10.3"
#endregion CONFIGURATION
# ============================================================================
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# ============================================================================
# HELPERS
# ============================================================================
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$color = switch ($Level) {
"ERROR" { "Red" }
"WARN" { "Yellow" }
"SUCCESS" { "Green" }
"HEADER" { "Cyan" }
default { "White" }
}
Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color
}
function Assert-ConfigValues {
$errors = @()
if ($ManagementNIC -match "REPLACE") { $errors += " ManagementNIC : $ManagementNIC" }
if ($DNSPrimary -match "REPLACE") { $errors += " DNSPrimary : $DNSPrimary" }
if ($DNSSecondary -match "REPLACE") { $errors += " DNSSecondary : $DNSSecondary" }
if ($errors.Count -gt 0) {
Write-Log "Configuration placeholders not replaced:" "ERROR"
$errors | ForEach-Object { Write-Host $_ -ForegroundColor Red }
Write-Log "Edit the #region CONFIGURATION block with values from variables.yml before running." "ERROR"
exit 1
}
}
function Get-ManagementAdapter {
$adapter = Get-NetAdapter | Where-Object { $_.Name -eq $ManagementNIC }
if (-not $adapter) {
Write-Log "Management adapter '$ManagementNIC' not found." "ERROR"
Write-Log "Available adapters:" "WARN"
Get-NetAdapter | Sort-Object Name | ForEach-Object {
Write-Host " $($_.Name) [$($_.Status)] $($_.InterfaceDescription)" -ForegroundColor Yellow
}
Write-Log "Update cluster.management_nic_name in variables.yml with the correct name." "ERROR"
exit 1
}
return $adapter
}
function Test-AlreadyConfigured {
param($IfIndex)
$current = (Get-DnsClientServerAddress -InterfaceIndex $IfIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).ServerAddresses
if ($current -and
$current.Count -ge 2 -and
$current[0] -eq $DNSPrimary -and
$current[1] -eq $DNSSecondary) {
return $true
}
return $false
}
function Test-DnsConfiguration {
param($IfIndex)
$current = (Get-DnsClientServerAddress -InterfaceIndex $IfIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).ServerAddresses
$pass = $true
if (-not $current -or $current.Count -lt 2) {
Write-Log " FAIL: DNS server list has fewer than 2 entries (got: $($current -join ','))" "WARN"
$pass = $false
} else {
if ($current[0] -ne $DNSPrimary) {
Write-Log " FAIL: Primary DNS is '$($current[0])', expected '$DNSPrimary'" "WARN"
$pass = $false
}
if ($current[1] -ne $DNSSecondary) {
Write-Log " FAIL: Secondary DNS is '$($current[1])', expected '$DNSSecondary'" "WARN"
$pass = $false
}
}
return $pass
}
# ============================================================================
# MAIN
# ============================================================================
try {
Write-Log "=== Set-DnsServers.ps1 ===" "HEADER"
Write-Log "Node: $($env:COMPUTERNAME)"
# Step 1 — Validate config block
Write-Log "Validating configuration block..."
Assert-ConfigValues
Write-Log " ManagementNIC : $ManagementNIC"
Write-Log " DNSPrimary : $DNSPrimary"
Write-Log " DNSSecondary : $DNSSecondary"
# Step 2 — Find adapter
Write-Log "Locating management adapter '$ManagementNIC'..."
$adapter = Get-ManagementAdapter
Write-Log " Found: $($adapter.Name) [$($adapter.Status)] $($adapter.InterfaceDescription)"
# Step 3 — Idempotency check
Write-Log "Checking current DNS configuration..."
if (Test-AlreadyConfigured -IfIndex $adapter.ifIndex) {
Write-Log "DNS already configured to target values. Nothing to do." "SUCCESS"
exit 0
}
$currentDNS = (Get-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).ServerAddresses
Write-Log " Current DNS: $($currentDNS -join ', ')"
Write-Log " Target DNS : $DNSPrimary, $DNSSecondary"
# Step 4 — Set DNS
Write-Log "Setting DNS servers..."
Set-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex `
-ServerAddresses @($DNSPrimary, $DNSSecondary) `
-ErrorAction Stop
Write-Log " Set-DnsClientServerAddress completed"
# Step 5 — Validate
Write-Log "Validating DNS configuration..."
if (-not (Test-DnsConfiguration -IfIndex $adapter.ifIndex)) {
Write-Log "DNS validation FAILED. Review errors above." "ERROR"
exit 1
}
$afterDNS = (Get-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4).ServerAddresses
Write-Log " Primary : $($afterDNS[0])" "SUCCESS"
Write-Log " Secondary : $($afterDNS[1])" "SUCCESS"
Write-Log "DNS CONFIGURATION COMPLETE" "SUCCESS"
exit 0
} catch {
Write-Log "CRITICAL ERROR: $_" "ERROR"
exit 1
}
Expected output:
[2026-03-05 09:20:01] [HEADER] === Set-DnsServers.ps1 ===
[2026-03-05 09:20:01] [INFO] Node: AZL-NODE-01
[2026-03-05 09:20:01] [INFO] Validating configuration block...
[2026-03-05 09:20:01] [INFO] ManagementNIC : Embedded NIC 1
[2026-03-05 09:20:01] [INFO] DNSPrimary : 10.100.10.2
[2026-03-05 09:20:01] [INFO] DNSSecondary : 10.100.10.3
[2026-03-05 09:20:01] [INFO] Locating management adapter 'Embedded NIC 1'...
[2026-03-05 09:20:01] [INFO] Found: Embedded NIC 1 [Up] Intel(R) Ethernet Connection 25G 4P E823-C LOM
[2026-03-05 09:20:01] [INFO] Checking current DNS configuration...
[2026-03-05 09:20:01] [INFO] Current DNS:
[2026-03-05 09:20:01] [INFO] Target DNS : 10.100.10.2, 10.100.10.3
[2026-03-05 09:20:01] [INFO] Setting DNS servers...
[2026-03-05 09:20:01] [INFO] Set-DnsClientServerAddress completed
[2026-03-05 09:20:01] [INFO] Validating DNS configuration...
[2026-03-05 09:20:02] [SUCCESS] Primary : 10.100.10.2
[2026-03-05 09:20:02] [SUCCESS] Secondary : 10.100.10.3
[2026-03-05 09:20:02] [SUCCESS] DNS CONFIGURATION COMPLETE
Run from the management server. Reads all DNS and NIC values from variables.yml — no manual variable editing required.
Toolkit script: scripts/deploy/04-cluster-deployment/phase-03-os-configuration/task-05-configure-dns-servers/powershell/Invoke-ConfigureDNS-Orchestrated.ps1
variables.yml values used:
| Path | Purpose |
|---|---|
cluster.management_nic_name | Which adapter to configure DNS on |
dns.primary | Primary DNS server IP |
dns.secondary | Secondary DNS server IP |
nodes.<name>.management_ip | PSRemoting connection target per node |
#Requires -Version 5.1
<#
.SYNOPSIS
Invoke-ConfigureDNS-Orchestrated.ps1
Configures DNS servers on the management NIC of every Azure Local node using PSRemoting.
.DESCRIPTION
Runs from the management server. Reads DNS and node IP values from variables.yml,
connects to each node over PSRemoting, and sets the DNS server addresses on the
management NIC.
variables.yml paths used:
nodes.<name>.management_ip - PSRemoting connection target
cluster.management_nic_name - Adapter name to configure DNS on
dns.primary - Primary DNS server IP
dns.secondary - Secondary DNS server IP
.NOTES
Author: Azure Local Cloud Azure Local Cloud
Version: 1.0.0
Phase: 03-os-configuration
Task: task-05-configure-dns-servers
Execution: Run from management server (PSRemoting outbound to nodes)
Prerequisites: PowerShell 5.1+, WinRM enabled on all nodes, admin credentials
Run after: Task 04 — DHCP disabled on all adapters
.EXAMPLE
.\Invoke-ConfigureDNS-Orchestrated.ps1
.\Invoke-ConfigureDNS-Orchestrated.ps1 -ConfigPath "C:\config\variables.yml"
#>
[CmdletBinding()]
param(
[string]$ConfigPath
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# ============================================================================
# LOGGING
# ============================================================================
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$color = switch ($Level) {
"ERROR" { "Red" }
"WARN" { "Yellow" }
"SUCCESS" { "Green" }
"HEADER" { "Cyan" }
default { "White" }
}
Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color
}
# ============================================================================
# CONFIGURATION RESOLVER
# ============================================================================
function Resolve-ConfigPath {
[CmdletBinding()]
param([string]$Provided)
if ($Provided -and (Test-Path $Provided)) {
Write-Log "Config: $Provided"
return $Provided
}
$searchPaths = @(
(Join-Path $PSScriptRoot "..\..\..\..\configs"),
(Join-Path $PSScriptRoot "..\..\..\..\..\configs"),
"C:\configs",
"C:\AzureLocal\configs"
)
$found = @()
foreach ($dir in $searchPaths) {
if (Test-Path $dir) {
$found += Get-ChildItem -Path $dir -Filter "infrastructure*.yml" -File -ErrorAction SilentlyContinue
}
}
$found = @($found | Sort-Object FullName -Unique)
if ($found.Count -eq 0) {
Write-Log "No infrastructure*.yml found. Provide -ConfigPath manually." "ERROR"
throw "Config file not found."
}
if ($found.Count -eq 1) {
Write-Log "Config: $($found[0].FullName)"
return $found[0].FullName
}
Write-Log "Multiple config files found:" "WARN"
for ($i = 0; $i -lt $found.Count; $i++) {
Write-Host " [$($i+1)] $($found[$i].FullName)" -ForegroundColor Yellow
}
$choice = Read-Host "Select config [1-$($found.Count)]"
$idx = [int]$choice - 1
if ($idx -lt 0 -or $idx -ge $found.Count) { throw "Invalid selection." }
return $found[$idx].FullName
}
# ============================================================================
# YAML PARSER (PowerShell 5.1 compatible)
# ============================================================================
function Get-YamlValue {
param([string[]]$Lines, [string[]]$KeyPath)
$current = $Lines
foreach ($key in $KeyPath) {
$pattern = "^\s*${key}\s*:"
$lineIdx = -1
for ($i = 0; $i -lt $current.Count; $i++) {
if ($current[$i] -match $pattern) { $lineIdx = $i; break }
}
if ($lineIdx -eq -1) { return $null }
if ($current[$lineIdx] -match "^\s*${key}\s*:\s*(.+)$") {
$val = $Matches[1].Trim().Trim('"').Trim("'")
if ($KeyPath[-1] -eq $key) { return $val }
return $null
}
$keyIndent = ($current[$lineIdx] -replace "^(\s*).*", '$1').Length
$blockLines = @()
for ($j = $lineIdx + 1; $j -lt $current.Count; $j++) {
$line = $current[$j]
if ($line -match "^\s*$") { continue }
$thisIndent = ($line -replace "^(\s*).*", '$1').Length
if ($thisIndent -le $keyIndent) { break }
$blockLines += $line
}
$current = $blockLines
}
return $null
}
function Get-NodeNames {
param([string[]]$Lines)
$inNodes = $false
$names = @()
foreach ($line in $Lines) {
if ($line -match "^\s*nodes\s*:") { $inNodes = $true; continue }
if ($inNodes) {
if ($line -match "^\s*(\w[\w\-_]*):\s*$") { $names += $Matches[1] }
elseif ($line -match "^\S" -and $line -notmatch "^\s*#") { break }
}
}
return $names
}
# ============================================================================
# GET CLUSTER CONFIG
# ============================================================================
function Get-ClusterConfig {
[CmdletBinding()]
param([string]$ConfigPath)
$raw = Get-Content -Path $ConfigPath -Raw
$lines = $raw -split "`n"
# Cluster-level values
$mgmtNIC = Get-YamlValue -Lines $lines -KeyPath @("cluster", "management_nic_name")
if (-not $mgmtNIC) { throw "cluster.management_nic_name not found in $ConfigPath." }
$dnsPrimary = Get-YamlValue -Lines $lines -KeyPath @("dns", "primary")
$dnsSecondary = Get-YamlValue -Lines $lines -KeyPath @("dns", "secondary")
if (-not $dnsPrimary) { throw "dns.primary not found in $ConfigPath." }
if (-not $dnsSecondary) { throw "dns.secondary not found in $ConfigPath." }
Write-Log " management_nic_name : $($mgmtNIC.Trim())"
Write-Log " dns.primary : $($dnsPrimary.Trim())"
Write-Log " dns.secondary : $($dnsSecondary.Trim())"
# Per-node IPs
$nodeNames = Get-NodeNames -Lines $lines
if ($nodeNames.Count -eq 0) { throw "No nodes found in $ConfigPath." }
$nodes = @()
foreach ($nodeName in $nodeNames) {
$mgmtIP = Get-YamlValue -Lines $lines -KeyPath @("nodes", $nodeName, "management_ip")
if (-not $mgmtIP) {
Write-Log " WARN: nodes.$nodeName.management_ip not found — skipping" "WARN"
continue
}
$nodes += [PSCustomObject]@{
NodeName = $nodeName
IP = $mgmtIP.Trim()
}
Write-Log " Node: $nodeName IP: $($mgmtIP.Trim())"
}
if ($nodes.Count -eq 0) { throw "No valid node entries found." }
return [PSCustomObject]@{
ManagementNIC = $mgmtNIC.Trim()
DNSPrimary = $dnsPrimary.Trim()
DNSSecondary = $dnsSecondary.Trim()
Nodes = $nodes
}
}
# ============================================================================
# REMOTE SCRIPTBLOCK
# ============================================================================
$RemoteScriptBlock = {
param([string]$ManagementNIC, [string]$DNSPrimary, [string]$DNSSecondary)
Set-StrictMode -Version Latest
# Find adapter by exact name
$adapter = Get-NetAdapter | Where-Object { $_.Name -eq $ManagementNIC }
if (-not $adapter) {
$available = (Get-NetAdapter | Sort-Object Name | ForEach-Object { $_.Name }) -join ", "
return [PSCustomObject]@{
Result = "Error"
Detail = "Adapter '$ManagementNIC' not found. Available: $available"
DNSAfter = ""
}
}
# Idempotency check
$current = (Get-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).ServerAddresses
if ($current -and $current.Count -ge 2 -and $current[0] -eq $DNSPrimary -and $current[1] -eq $DNSSecondary) {
return [PSCustomObject]@{
Result = "AlreadyConfigured"
Detail = "DNS already set to $DNSPrimary, $DNSSecondary"
DNSAfter = "$DNSPrimary, $DNSSecondary"
}
}
# Set DNS
try {
Set-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex `
-ServerAddresses @($DNSPrimary, $DNSSecondary) -ErrorAction Stop
$after = (Get-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).ServerAddresses
$ok = ($after -and $after.Count -ge 2 -and $after[0] -eq $DNSPrimary -and $after[1] -eq $DNSSecondary)
return [PSCustomObject]@{
Result = if ($ok) { "Changed" } else { "Failed" }
Detail = if ($ok) { "DNS set successfully" } else { "Post-set DNS mismatch: $($after -join ', ')" }
DNSAfter = $after -join ", "
}
} catch {
return [PSCustomObject]@{
Result = "Error"
Detail = $_.ToString()
DNSAfter = ""
}
}
}
# ============================================================================
# MAIN
# ============================================================================
try {
Write-Log "=== Invoke-ConfigureDNS-Orchestrated.ps1 ===" "HEADER"
$resolvedConfig = Resolve-ConfigPath -Provided $ConfigPath
Write-Log "Loading configuration from: $resolvedConfig"
$config = Get-ClusterConfig -ConfigPath $resolvedConfig
Write-Log "Nodes to process: $($config.Nodes.Count)"
$credential = Get-Credential -Message "Enter local admin credentials for Azure Local nodes"
$nodeResults = @()
foreach ($node in $config.Nodes) {
Write-Log "--- $($node.NodeName) ($($node.IP)) ---" "HEADER"
try {
$session = New-PSSession -ComputerName $node.IP -Credential $credential -ErrorAction Stop
Write-Log " Connected to $($node.IP)"
$result = Invoke-Command -Session $session -ScriptBlock $RemoteScriptBlock `
-ArgumentList $config.ManagementNIC, $config.DNSPrimary, $config.DNSSecondary
$level = switch ($result.Result) {
"Changed" { "SUCCESS" }
"AlreadyConfigured" { "SUCCESS" }
default { "WARN" }
}
Write-Log " $($node.NodeName): $($result.Result) — $($result.Detail)" $level
if ($result.DNSAfter) {
Write-Log " DNS after: $($result.DNSAfter)"
}
$nodeResults += [PSCustomObject]@{
Node = $node.NodeName
IP = $node.IP
Result = $result.Result
DNSAfter = $result.DNSAfter
Detail = $result.Detail
}
Remove-PSSession -Session $session -ErrorAction SilentlyContinue
} catch {
Write-Log " ERROR connecting to $($node.NodeName) ($($node.IP)): $_" "ERROR"
$nodeResults += [PSCustomObject]@{
Node = $node.NodeName
IP = $node.IP
Result = "ConnectionFailed"
DNSAfter = ""
Detail = $_.ToString()
}
}
}
Write-Log "=== ORCHESTRATION SUMMARY ===" "HEADER"
$nodeResults | Format-Table -AutoSize | Out-String | Write-Host
$failed = @($nodeResults | Where-Object { $_.Result -notin @("Changed","AlreadyConfigured") })
if ($failed.Count -gt 0) {
Write-Log "$($failed.Count) node(s) had issues. Review above." "WARN"
exit 1
}
Write-Log "All nodes: DNS configuration complete." "SUCCESS"
exit 0
} catch {
Write-Log "CRITICAL ERROR: $_" "ERROR"
exit 1
}
Expected output (excerpt):
[2026-03-05 09:22:00] [HEADER] === Invoke-ConfigureDNS-Orchestrated.ps1 ===
[2026-03-05 09:22:00] [INFO] Loading configuration from: C:\config\variables.yml
[2026-03-05 09:22:00] [INFO] management_nic_name : Embedded NIC 1
[2026-03-05 09:22:00] [INFO] dns.primary : 10.100.10.2
[2026-03-05 09:22:00] [INFO] dns.secondary : 10.100.10.3
[2026-03-05 09:22:00] [INFO] Node: node01 IP: 10.100.200.11
[2026-03-05 09:22:00] [INFO] Node: node02 IP: 10.100.200.12
[2026-03-05 09:22:00] [INFO] Nodes to process: 2
[2026-03-05 09:22:01] [HEADER] --- node01 (10.100.200.11) ---
[2026-03-05 09:22:01] [INFO] Connected to 10.100.200.11
[2026-03-05 09:22:02] [SUCCESS] node01: Changed — DNS set successfully
[2026-03-05 09:22:02] [INFO] DNS after: 10.100.10.2, 10.100.10.3
[2026-03-05 09:22:03] [HEADER] === ORCHESTRATION SUMMARY ===
Node IP Result DNSAfter Detail
---- -- ------ -------- ------
node01 10.100.200.11 Changed 10.100.10.2, 10.100.10.3 DNS set successfully
node02 10.100.200.12 Changed 10.100.10.2, 10.100.10.3 DNS set successfully
[2026-03-05 09:22:10] [SUCCESS] All nodes: DNS configuration complete.
Validation
Verify DNS server configuration on each node:
# Run on each node (or via PSRemoting)
Get-DnsClientServerAddress -AddressFamily IPv4 |
Where-Object { $_.ServerAddresses.Count -gt 0 } |
Select-Object InterfaceAlias, ServerAddresses |
Format-Table -AutoSize
Validation checklist:
| Check | Expected | Status |
|---|---|---|
| Primary DNS on management NIC | dns.primary from variables.yml | ☐ |
| Secondary DNS on management NIC | dns.secondary from variables.yml | ☐ |
| No other DNS entries on management NIC | Only 2 servers listed | ☐ |
Task 06 performs a full DNS resolution validation. Basic resolution testing at this stage is optional — proceed to Task 06 for a comprehensive verification.
Troubleshooting
| Issue | Root Cause | Remediation |
|---|---|---|
| Script hard-fails on startup | REPLACE placeholder values remain | Edit #region CONFIGURATION with real values from variables.yml |
Adapter not found error | NIC name in config doesn't match | Run Get-NetAdapter on the node; update cluster.management_nic_name in variables.yml |
| DNS validation fails (post-set mismatch) | Transient WMI delay | Wait 5 seconds and re-run the validation query manually |
| DNS not resolving names (verified in Task 06) | DNS servers unreachable | Verify DNS server IPs are reachable from node; check routing and firewall |
Orchestrated script: cluster.management_nic_name not found | Key missing from variables.yml | Add cluster.management_nic_name to the cluster: block in yml |
Orchestrated script: dns.primary not found | Key missing from variables.yml | Add dns.primary and dns.secondary to the dns: block in yml |
| Node unreachable via PSRemoting | WinRM not configured or firewall blocking | Verify Task 01 (WinRM) completed; check firewall rules |
Navigation
| ← Task 04: Disable DHCP | ↑ Phase 03: OS Configuration | Task 06: Verify DNS → |
Version Control
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-01-31 | Azure Local Cloud Azure Local Cloudnology | Initial document |
| 2.0 | 2026-03-05 | Azure Local Cloud Azure Local Cloudnology | Full rewrite to standards — complete frontmatter, 3-tab structure (SConfig, Direct, Orchestrated), Standalone tab removed, full embedded scripts, explicit NIC name from variables.yml, variables.yml integration in orchestrated script |