Windows DNS Name Server (NS) Record Management: Analysis & Reverse Lookup Zone Updates

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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!

Leave a Reply

Your email address will not be published. Required fields are marked *