Analyzing and Updating Name Server (NS) Records on Windows DNS Servers
Managing Windows DNS is crucial for ensuring that network infrastructures remain up-to-date and function correctly. The accuracy and timeliness of Name Server (NS) records are essential elements that must be frequently monitored. In this article, we will discuss how a script operates to analyze and update Name Server records on Windows DNS servers.
How Does the Script Work?
The developed script runs on Windows DNS servers and performs the following key operations:
- Scanning All Name Server Records:
- When the “Analyze” button is pressed, the script scans all Forward Lookup Zone and Reverse Lookup Zone areas and lists the current Name Server (NS) records.
- Forward Lookup Zone and Reverse Lookup Zone Scanning:
- It collects the existing Name Server records for the Forward Lookup Zone and Reverse Lookup Zone but does not make any modifications.
- Updating Reverse Lookup Zone:
- When the “Update NS” button is pressed, the NS records in the Reverse Lookup Zone are checked.
- Missing NS records are added.
- Old and unused NS records are removed.
- Listing Old Name Server Records:
- No updates are made for the Forward Lookup Zone, but outdated and unused NS records are displayed for the system administrator’s review.
Name Server Checker & Reverse Lookup Zone Update
# DNS Active NS Record Manager GUI Application
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Create the main form
$form = New-Object System.Windows.Forms.Form
$form.Text = 'DNS Active NS Record Manager'
$form.Size = New-Object System.Drawing.Size(900,700)
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'FixedDialog'
$form.MaximizeBox = $false
$form.BackColor = [System.Drawing.Color]::FromArgb(240,240,240)
# Create banner panel
$bannerPanel = New-Object System.Windows.Forms.Panel
$bannerPanel.Size = New-Object System.Drawing.Size(900,60)
$bannerPanel.Location = New-Object System.Drawing.Point(0,0)
$bannerPanel.BackColor = [System.Drawing.Color]::FromArgb(0,120,215)
$form.Controls.Add($bannerPanel)
# Create banner title
$bannerTitle = New-Object System.Windows.Forms.Label
$bannerTitle.Text = "DNS Active NS Record Management"
$bannerTitle.ForeColor = [System.Drawing.Color]::White
$bannerTitle.Font = New-Object System.Drawing.Font("Segoe UI", 16, [System.Drawing.FontStyle]::Regular)
$bannerTitle.AutoSize = $true
$bannerTitle.Location = New-Object System.Drawing.Point(20,15)
$bannerPanel.Controls.Add($bannerTitle)
# Create main panel for controls
$mainPanel = New-Object System.Windows.Forms.Panel
$mainPanel.Size = New-Object System.Drawing.Size(860,580)
$mainPanel.Location = New-Object System.Drawing.Point(20,80)
$mainPanel.BackColor = [System.Drawing.Color]::White
$form.Controls.Add($mainPanel)
# Create DNS Server label and textbox
$lblDNSServer = New-Object System.Windows.Forms.Label
$lblDNSServer.Location = New-Object System.Drawing.Point(20,20)
$lblDNSServer.Size = New-Object System.Drawing.Size(100,20)
$lblDNSServer.Text = 'DNS Server:'
$lblDNSServer.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$mainPanel.Controls.Add($lblDNSServer)
$txtDNSServer = New-Object System.Windows.Forms.TextBox
$txtDNSServer.Location = New-Object System.Drawing.Point(120,20)
$txtDNSServer.Size = New-Object System.Drawing.Size(200,20)
$txtDNSServer.Text = 'localhost'
$txtDNSServer.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$mainPanel.Controls.Add($txtDNSServer)
# Create Active NS Servers ListView
$lblActiveNS = New-Object System.Windows.Forms.Label
$lblActiveNS.Location = New-Object System.Drawing.Point(20,60)
$lblActiveNS.Size = New-Object System.Drawing.Size(150,20)
$lblActiveNS.Text = 'Active NS Servers:'
$lblActiveNS.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$mainPanel.Controls.Add($lblActiveNS)
$listActiveNS = New-Object System.Windows.Forms.ListView
$listActiveNS.Location = New-Object System.Drawing.Point(20,85)
$listActiveNS.Size = New-Object System.Drawing.Size(400,150)
$listActiveNS.View = [System.Windows.Forms.View]::Details
$listActiveNS.FullRowSelect = $true
$listActiveNS.GridLines = $true
$listActiveNS.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$listActiveNS.Columns.Add("NS Server", 380)
$mainPanel.Controls.Add($listActiveNS)
# Create button panel
$buttonPanel = New-Object System.Windows.Forms.Panel
$buttonPanel.Size = New-Object System.Drawing.Size(420,30)
$buttonPanel.Location = New-Object System.Drawing.Point(440,85)
$buttonPanel.BackColor = [System.Drawing.Color]::White
$mainPanel.Controls.Add($buttonPanel)
# Create Refresh button
$btnRefresh = New-Object System.Windows.Forms.Button
$btnRefresh.Location = New-Object System.Drawing.Point(0,0)
$btnRefresh.Size = New-Object System.Drawing.Size(100,30)
$btnRefresh.Text = 'Refresh'
$btnRefresh.FlatStyle = 'Flat'
$btnRefresh.BackColor = [System.Drawing.Color]::FromArgb(0,120,215)
$btnRefresh.ForeColor = [System.Drawing.Color]::White
$btnRefresh.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$btnRefresh.Cursor = [System.Windows.Forms.Cursors]::Hand
$buttonPanel.Controls.Add($btnRefresh)
# Create Analyze button
$btnAnalyze = New-Object System.Windows.Forms.Button
$btnAnalyze.Location = New-Object System.Drawing.Point(105,0)
$btnAnalyze.Size = New-Object System.Drawing.Size(100,30)
$btnAnalyze.Text = 'Analyze'
$btnAnalyze.FlatStyle = 'Flat'
$btnAnalyze.BackColor = [System.Drawing.Color]::FromArgb(0,120,215)
$btnAnalyze.ForeColor = [System.Drawing.Color]::White
$btnAnalyze.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$btnAnalyze.Cursor = [System.Windows.Forms.Cursors]::Hand
$buttonPanel.Controls.Add($btnAnalyze)
# Create Clean button
$btnClean = New-Object System.Windows.Forms.Button
$btnClean.Location = New-Object System.Drawing.Point(210,0)
$btnClean.Size = New-Object System.Drawing.Size(100,30)
$btnClean.Text = 'Update NS'
$btnClean.FlatStyle = 'Flat'
$btnClean.BackColor = [System.Drawing.Color]::FromArgb(0,120,215)
$btnClean.ForeColor = [System.Drawing.Color]::White
$btnClean.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$btnClean.Enabled = $false
$btnClean.Cursor = [System.Windows.Forms.Cursors]::Hand
$buttonPanel.Controls.Add($btnClean)
# Create Exit button
$btnExit = New-Object System.Windows.Forms.Button
$btnExit.Location = New-Object System.Drawing.Point(315,0)
$btnExit.Size = New-Object System.Drawing.Size(100,30)
$btnExit.Text = 'Exit'
$btnExit.FlatStyle = 'Flat'
$btnExit.BackColor = [System.Drawing.Color]::FromArgb(232,17,35)
$btnExit.ForeColor = [System.Drawing.Color]::White
$btnExit.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$btnExit.Cursor = [System.Windows.Forms.Cursors]::Hand
$buttonPanel.Controls.Add($btnExit)
# Create RichTextBox for output
$txtOutput = New-Object System.Windows.Forms.RichTextBox
$txtOutput.Location = New-Object System.Drawing.Point(20,250)
$txtOutput.Size = New-Object System.Drawing.Size(820,310)
$txtOutput.ReadOnly = $true
$txtOutput.BackColor = [System.Drawing.Color]::FromArgb(30,30,30)
$txtOutput.ForeColor = [System.Drawing.Color]::White
$txtOutput.Font = New-Object System.Drawing.Font("Consolas", 10)
$txtOutput.BorderStyle = 'None'
$mainPanel.Controls.Add($txtOutput)
# Function to write colored output
function Write-ColorOutput {
param([string]$Message, [string]$Color = "White")
$txtOutput.SelectionColor = [System.Drawing.Color]::$Color
$txtOutput.AppendText("$Message`n")
$txtOutput.ScrollToCaret()
}
# Function to write separator line
function Write-Separator {
Write-ColorOutput ("-" * 80) "DarkGray"
}
# Function to test if a server is online with simple checks
function Test-ServerConnection {
param([string]$ServerName)
try {
Write-ColorOutput "Testing NS server: $ServerName" "Yellow"
# Try to get IP address
try {
$ipAddress = [System.Net.Dns]::GetHostAddresses($ServerName) |
Where-Object { $_.AddressFamily -eq 'InterNetwork' } |
Select-Object -First 1
if ($ipAddress) {
Write-ColorOutput " - IP Address: $($ipAddress.IPAddressToString)" "Gray"
} else {
Write-ColorOutput " - Could not resolve IP address" "Red"
return $false
}
} catch {
Write-ColorOutput " - DNS resolution failed" "Red"
return $false
}
# Try ping
$ping = Test-Connection -ComputerName $ServerName -Count 2 -Quiet
if ($ping) {
Write-ColorOutput " - Ping successful" "Green"
# Try basic DNS port check
try {
$tcp = New-Object System.Net.Sockets.TcpClient
$tcp.SendTimeout = 2000
$tcp.ReceiveTimeout = 2000
$result = $tcp.BeginConnect($ServerName, 53, $null, $null)
$success = $result.AsyncWaitHandle.WaitOne(2000, $false)
if ($success) {
Write-ColorOutput " - DNS port is accessible" "Green"
$tcp.EndConnect($result)
return $true
} else {
Write-ColorOutput " - DNS port is not accessible" "Red"
return $false
}
} catch {
Write-ColorOutput " - DNS port check failed" "Red"
return $false
} finally {
if ($tcp) { $tcp.Close() }
}
} else {
Write-ColorOutput " - Ping failed" "Red"
return $false
}
} catch {
Write-ColorOutput " - Connection test failed: $_" "Red"
return $false
}
}
# Function to validate NS server with simple logic
function Test-NSServerValidity {
param([string]$NSServer)
return Test-ServerConnection -ServerName $NSServer
}
# Function to get active NS records
function Get-ActiveNSRecords {
$listActiveNS.Items.Clear()
Write-ColorOutput "Loading active NS records..." "Yellow"
Write-Separator
try {
# Get all zones (both forward and reverse)
$allZones = Get-DnsServerZone -ComputerName $txtDNSServer.Text |
Where-Object { $_.ZoneName -ne "TrustAnchors" }
$script:activeNSServers = @()
$script:inactiveNSServers = @{} # Track inactive NS servers and their source zones
$potentialNSServers = @{}
foreach ($zone in $allZones) {
Write-ColorOutput "Checking zone: $($zone.ZoneName)" "Cyan"
# Get NS records for each zone
$nsRecords = Get-DnsServerResourceRecord -ZoneName $zone.ZoneName -RRType "NS" -ComputerName $txtDNSServer.Text |
Where-Object { $_.RecordType -eq "NS" }
foreach ($ns in $nsRecords) {
$nsHost = $ns.RecordData.NameServer.Trim('.')
if (-not $potentialNSServers.ContainsKey($nsHost)) {
$potentialNSServers[$nsHost] = $zone.ZoneName
}
}
}
Write-ColorOutput "`nValidating NS servers..." "Yellow"
Write-Separator
foreach ($nsServer in $potentialNSServers.Keys) {
Write-ColorOutput "Processing NS server from zone '$($potentialNSServers[$nsServer])'" "Cyan"
if (Test-NSServerValidity -NSServer $nsServer) {
if (-not $script:activeNSServers.Contains($nsServer)) {
$script:activeNSServers += $nsServer
$listActiveNS.Items.Add($nsServer)
Write-ColorOutput "Confirmed active NS: $nsServer" "Green"
}
} else {
Write-ColorOutput "NS server validation failed: $nsServer" "Red"
if (-not $script:inactiveNSServers.ContainsKey($nsServer)) {
$script:inactiveNSServers[$nsServer] = $potentialNSServers[$nsServer]
}
}
Write-Separator
}
Write-ColorOutput "`nTotal active NS servers found: $($script:activeNSServers.Count)" "Cyan"
Write-ColorOutput "Active NS Servers:" "Yellow"
foreach ($ns in $script:activeNSServers) {
Write-ColorOutput " $ns" "White"
}
Write-Separator
} catch {
Write-ColorOutput "Error getting active NS records: $_" "Red"
Write-Separator
}
}
# Function to analyze DNS zones
function Analyze-ReverseLookupZones {
Write-ColorOutput "Starting DNS zone analysis..." "Yellow"
Write-Separator
try {
# Get all zones for information
$allZones = Get-DnsServerZone -ComputerName $txtDNSServer.Text |
Where-Object { $_.ZoneName -ne "TrustAnchors" }
# Get only reverse lookup zones for updates
$reverseLookupZones = $allZones | Where-Object { $_.ZoneName -like "*.in-addr.arpa" }
$script:inactiveNSCount = 0
$script:analyzedZones = @{} # Will only contain reverse lookup zones
$script:zonesToUpdate = @{} # Will only contain reverse lookup zones
# First, show inactive NS servers from all zones (informational)
Write-ColorOutput "`nInactive NS Servers in All Zones (Informational):" "Yellow"
Write-Separator
foreach ($inactiveNS in $script:inactiveNSServers.Keys) {
Write-ColorOutput "Found inactive NS server: $inactiveNS" "Red"
Write-ColorOutput "Originally detected in zone: $($script:inactiveNSServers[$inactiveNS])" "Yellow"
Write-Separator
}
# Process all zones for information
Write-ColorOutput "`nAnalyzing All Zones (Informational):" "Yellow"
Write-Separator
foreach ($zone in $allZones) {
if ($zone.ZoneName -notlike "*.in-addr.arpa") {
Write-ColorOutput "Checking zone: $($zone.ZoneName)" "Cyan"
$nsRecords = Get-DnsServerResourceRecord -ZoneName $zone.ZoneName -RRType "NS" -ComputerName $txtDNSServer.Text |
Where-Object { $_.RecordType -eq "NS" }
foreach ($ns in $nsRecords) {
$nsHost = $ns.RecordData.NameServer.Trim('.')
if ($script:inactiveNSServers.ContainsKey($nsHost)) {
Write-ColorOutput " - Inactive NS found: $nsHost" "Red"
} else {
Write-ColorOutput " - Active NS found: $nsHost" "Green"
}
}
Write-Separator
}
}
# Process reverse lookup zones for updates
Write-ColorOutput "`nAnalyzing Reverse Lookup Zones (Will Be Updated):" "Yellow"
Write-Separator
foreach ($zone in $reverseLookupZones) {
Write-ColorOutput "Analyzing zone: $($zone.ZoneName)" "Cyan"
$nsRecords = Get-DnsServerResourceRecord -ZoneName $zone.ZoneName -RRType "NS" -ComputerName $txtDNSServer.Text |
Where-Object { $_.RecordType -eq "NS" }
$inactiveNS = @()
$existingNS = @()
$missingNS = @()
# Check for inactive NS records
foreach ($ns in $nsRecords) {
$nsHost = $ns.RecordData.NameServer.Trim('.')
$existingNS += $nsHost
if ($script:inactiveNSServers.ContainsKey($nsHost) -or -not $script:activeNSServers.Contains($nsHost)) {
$inactiveNS += $nsHost
$script:inactiveNSCount++
Write-ColorOutput " - Inactive NS found: $nsHost" "Red"
} else {
Write-ColorOutput " - Active NS found: $nsHost" "Green"
}
}
# Check for missing active NS records
foreach ($activeNS in $script:activeNSServers) {
if (-not $existingNS.Contains($activeNS)) {
$missingNS += $activeNS
Write-ColorOutput " + Missing active NS: $activeNS" "Yellow"
}
}
if ($inactiveNS.Count -gt 0) {
$script:analyzedZones[$zone.ZoneName] = $inactiveNS
}
if ($missingNS.Count -gt 0) {
$script:zonesToUpdate[$zone.ZoneName] = $missingNS
}
if ($inactiveNS.Count -eq 0 -and $missingNS.Count -eq 0) {
Write-ColorOutput " Zone is properly configured." "Green"
}
Write-Separator
}
Write-ColorOutput "`nAnalysis Summary:" "Cyan"
Write-ColorOutput "Total zones analyzed: $($allZones.Count)" "White"
Write-ColorOutput "Reverse lookup zones to update: $($reverseLookupZones.Count)" "White"
Write-ColorOutput "Total inactive NS records in reverse zones: $script:inactiveNSCount" "Yellow"
Write-ColorOutput "Total reverse zones needing NS records: $($script:zonesToUpdate.Count)" "Yellow"
Write-Separator
if ($script:inactiveNSCount -gt 0 -or $script:zonesToUpdate.Count -gt 0) {
$btnClean.Enabled = $true
} else {
$btnClean.Enabled = $false
}
} catch {
Write-ColorOutput "Error analyzing zones: $_" "Red"
Write-Separator
}
}
# Function to clean inactive NS records and add missing ones
function Clean-InactiveNSRecords {
if ([System.Windows.Forms.MessageBox]::Show(
"Are you sure you want to update NS records in reverse lookup zones?`n`nThis will:`n- Remove inactive NS records from reverse lookup zones`n- Add missing active NS records to reverse lookup zones`n`nOther zones will not be modified.",
"Confirm Update",
[System.Windows.Forms.MessageBoxButtons]::YesNo,
[System.Windows.Forms.MessageBoxIcon]::Warning) -eq 'Yes')
{
Write-ColorOutput "Starting reverse lookup zone NS records update..." "Yellow"
Write-Separator
try {
$removedCount = 0
$addedCount = 0
# Process only reverse lookup zones
foreach ($zone in $script:analyzedZones.Keys) {
Write-ColorOutput "Processing zone: $zone" "Cyan"
foreach ($inactiveNS in $script:analyzedZones[$zone]) {
try {
Remove-DnsServerResourceRecord -ZoneName $zone -RRType "NS" -Name "@" `
-RecordData $inactiveNS -Force -ComputerName $txtDNSServer.Text
Write-ColorOutput "Removed NS record: $inactiveNS" "Green"
$removedCount++
} catch {
Write-ColorOutput "Error removing NS record $inactiveNS : $_" "Red"
}
}
}
foreach ($zone in $script:zonesToUpdate.Keys) {
Write-ColorOutput "`nAdding missing NS records to zone: $zone" "Cyan"
foreach ($missingNS in $script:zonesToUpdate[$zone]) {
try {
Add-DnsServerResourceRecord -ZoneName $zone -NS -Name "@" `
-NameServer $missingNS -ComputerName $txtDNSServer.Text
Write-ColorOutput "Added NS record: $missingNS" "Green"
$addedCount++
} catch {
Write-ColorOutput "Error adding NS record $missingNS : $_" "Red"
}
}
}
Write-ColorOutput "`nUpdate Summary:" "Cyan"
Write-ColorOutput "Total NS records removed from reverse zones: $removedCount" "Yellow"
Write-ColorOutput "Total NS records added to reverse zones: $addedCount" "Yellow"
Write-Separator
$btnClean.Enabled = $false
$script:analyzedZones.Clear()
$script:zonesToUpdate.Clear()
$script:inactiveNSCount = 0
} catch {
Write-ColorOutput "Error during update: $_" "Red"
Write-Separator
}
}
}
# Button click events
$btnRefresh.Add_Click({ Get-ActiveNSRecords })
$btnAnalyze.Add_Click({ Analyze-ReverseLookupZones })
$btnClean.Add_Click({ Clean-InactiveNSRecords })
$btnExit.Add_Click({ $form.Close() })
# Load active NS records when form loads
$form.Add_Shown({
Get-ActiveNSRecords
})
# Show the form
$form.ShowDialog()
What Is the Purpose of This Script?
This script was developed to improve DNS management efficiency. Over time, DNS servers may experience performance issues due to outdated records, making regular updates crucial. It is particularly useful in the following scenarios:
- When making major changes to the network.
- When updating existing Name Server records.
- When identifying and removing old NS records.
- When regularly optimizing Reverse Lookup Zone areas.
As a result, with this script
To ensure that Windows DNS servers function correctly, NS records must be continuously monitored and updated. This script provides a great convenience for DNS administrators by offering both analysis and update functions. Using automation like this to keep your DNS infrastructure healthy and up to date is highly beneficial.
If you found this trick helpful, feel free to share it with your team and check out my blog for more quick tips and insights!