Efficient DNS Zone Management: Scripts for Forward, Reverse Lookup Zones and Recovery

At its core, DNS is a system that eliminates the complexity of accessing services via IP addresses by allowing users to connect effortlessly using domain names.

  • It facilitates domain name resolution and management.
  • Directs queries to the appropriate destinations.

DNS zones and reverse lookup zones are crucial components of a robust and secure network infrastructure.

In this article, I will share several scripts that you can use as needed. These scripts are designed to address the following requirements:

  • Exporting the names of all zones in DNS
  • Extracting all records associated with the zones listed in the exported file
  • Exporting all records from a specific zone of your choice
  • Restoring a deleted zone if necessary
  • Add Reverse Lookup Zones

Exporting All Zones in DNS

# Output file path

$outputFile = "C:\Script\DNSOperation\DNSZoneNameExport.csv"

try {

    # Fetching DNS zones

    $zones = Get-DnsServerZone

    if ($zones) {

        # Preparing data for export

        $zoneData = $zones | Select-Object -Property ZoneName

        # Exporting to CSV

        $zoneData | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8

        Write-Host "DNS zones have been successfully exported to $outputFile" -ForegroundColor Green

    } else {

        Write-Host "No DNS zones found." -ForegroundColor Yellow

    }

} catch {

    Write-Host "An error occurred while fetching DNS zones: $_" -ForegroundColor Red

}

Exporting All Zones in Records

# Zones CSV Path

$zonesFile = "C:\Script\DNSOperation\DNSZoneNameExport.csv"

$zones = Import-Csv -Path $zonesFile

foreach ($zone in $zones) {

    $zoneName = $zone.ZoneName

    $OutputFile = "C:\Script\DNSOperation\$zoneName.csv"

    $Report = [System.Collections.Generic.List[Object]]::new()

    $zoneInfo = Get-DnsServerResourceRecord -ZoneName $zoneName

    foreach ($info in $zoneInfo) {

        $timestamp = if ($info.Timestamp) { $info.Timestamp } else { "static" }

        $timetolive = $info.TimeToLive.TotalSeconds

        $recordData = switch ($info.RecordType) {

            'A' { $info.RecordData.IPv4Address }

            'CNAME' { $info.RecordData.HostnameAlias }

            'NS' { $info.RecordData.NameServer }

            'SOA' { "[$($info.RecordData.SerialNumber)] $($info.RecordData.PrimaryServer), $($info.RecordData.ResponsiblePerson)" }

            'SRV' { $info.RecordData.DomainName }

            'PTR' { $info.RecordData.PtrDomainName }

            'MX' { $info.RecordData.MailExchange }

            'AAAA' { $info.RecordData.IPv6Address }

            'TXT' { $info.RecordData.DescriptiveText }

            default { $null }

        }

        $ReportLine = [PSCustomObject]@{

            Name       = $zoneName

            Hostname   = $info.Hostname

            Type       = $info.RecordType

            Data       = $recordData

            Timestamp  = $timestamp

            TimeToLive = $timetolive

        }

        $Report.Add($ReportLine)

    }

    $Report | Export-Csv -Path $OutputFile -NoTypeInformation -Encoding utf8

    Write-Host "DNS records for $zoneName exported successfully." -ForegroundColor Green

}


Exporting Specific Zone in Records

try {

    $Report = [System.Collections.Generic.List[Object]]::new()

    $zoneName = "yusufustundag.com"

    # Fetching zone information

    try {

        $zoneInfo = Get-DnsServerResourceRecord -ZoneName $zoneName -ErrorAction Stop

    } catch {

        Write-Host "Error: Unable to fetch zone information. Zone name: $zoneName" -ForegroundColor Red

        throw $_

    }

    # Processing records

    foreach ($info in $zoneInfo) {

        try {

            $timestamp = if ($info.Timestamp) { $info.Timestamp } else { "static" }

            $timetolive = if ($info.TimeToLive) { $info.TimeToLive.TotalSeconds } else { "N/A" }

            $recordData = switch ($info.RecordType) {

                'A'      { if ($info.RecordData.IPv4Address) { $info.RecordData.IPv4Address } else { "No IP" } }

                'CNAME'  { if ($info.RecordData.HostnameAlias) { $info.RecordData.HostnameAlias } else { "No Alias" } }

                'NS'     { if ($info.RecordData.NameServer) { $info.RecordData.NameServer } else { "No NameServer" } }

                'SOA'    { if ($info.RecordData) { "[$($info.RecordData.SerialNumber)] $($info.RecordData.PrimaryServer), $($info.RecordData.ResponsiblePerson)" } else { "No SOA Information" } }

                'SRV'    { if ($info.RecordData.DomainName) { $info.RecordData.DomainName } else { "No Domain" } }

                'PTR'    { if ($info.RecordData.PtrDomainName) { $info.RecordData.PtrDomainName } else { "No PTR" } }

                'MX'     { if ($info.RecordData.MailExchange) { $info.RecordData.MailExchange } else { "No Mail Exchange" } }

                'AAAA'   { if ($info.RecordData.IPv6Address) { $info.RecordData.IPv6Address } else { "No IPv6" } }

                'TXT'    { if ($info.RecordData.DescriptiveText) { $info.RecordData.DescriptiveText } else { "No TXT" } }

                default  { "Unsupported record type: $($info.RecordType)" }

            }

            $ReportLine = [PSCustomObject]@{

                Name       = $zoneName

                Hostname   = $info.Hostname

                Type       = $info.RecordType

                Data       = $recordData

                Timestamp  = $timestamp

                TimeToLive = $timetolive

            }

            $Report.Add($ReportLine)

        } catch {

            Write-Host "Error: An issue occurred while processing the record. Hostname: $($info.Hostname), Type: $($info.RecordType)" -ForegroundColor Red

            Write-Host "Details: $_" -ForegroundColor Yellow

        }

    }

    # Writing to CSV

    try {

        $Report | Export-Csv "C:\Script\DNSOperation\DNSZoneRecord.csv" -NoTypeInformation -Encoding utf8

        Write-Host "DNS records have been successfully exported." -ForegroundColor Green

    } catch {

        Write-Host "Error: Failed to write to the CSV file." -ForegroundColor Red

        throw $_

    }

} catch {

    Write-Host "An unexpected error occurred: $_" -ForegroundColor Red

}

 

These scripts might seem a bit complex, but the .csv data they generate can effectively meet your needs in situations requiring reporting or verification.

Reading data may not always be easier than working with a .csv file, but in the event of a zone deletion, our other script utilizing the Export-DnsServerZone and dnscmd commands will provide a quick recovery solution.

Recovery – Export Import Specific Zone

Export-DNSServerZone -name yusufustundag.com -FileName yusufustundagExport

After completing the export process, you can find the exported file under the C:\Windows\System32\dns directory.

Now, here’s a critical point: suppose you’ve exported all your zones using this method, but someone accidentally deleted one or more zones. How can you quickly recover them in such a scenario?

The script we will use for this recovery must include the zone name and the name of the exported zone, as shown below.

dnscmd /zoneadd "yusufustundag.com" /primary /file yusufustundagExport /Load

 

Add Reverse Lookup Zone

$Zones = @(

    "1.1.0.0/24",  # Subnet /24 (255.255.255.0)

    "2.2.0.0/24",

    "10.10.151.0/24"

)

foreach ($Zone in $Zones) {

    try {

        # Split the NetworkID and Subnet Mask

        $SubnetArray = $Zone -split '/'

        $IPAddress = $SubnetArray[0]

        $SubnetMask = [int]$SubnetArray[1]

        # Generate the Reverse Lookup Zone Name based on Subnet Mask

        $Octets = $IPAddress -split '\.'

        if ($SubnetMask -le 8) {

            $RevZone = "$($Octets[0]).in-addr.arpa"

        } elseif ($SubnetMask -le 16) {

            $RevZone = "$($Octets[1]).$($Octets[0]).in-addr.arpa"

        } elseif ($SubnetMask -le 24) {

            $RevZone = "$($Octets[2]).$($Octets[1]).$($Octets[0]).in-addr.arpa"

        } else {

            $RevZone = "$($Octets[3]).$($Octets[2]).$($Octets[1]).$($Octets[0]).in-addr.arpa"

        }

        # Check if the Reverse Zone Already Exists

        $ExistingZone = Get-DnsServerZone -Name $RevZone -ErrorAction SilentlyContinue

        

        if ($ExistingZone) {

            Write-Output "Reverse Lookup Zone '$RevZone' already exists."

        } else {

            # Add the Reverse Lookup Zone

            Add-DnsServerPrimaryZone -NetworkID $Zone -ReplicationScope "Domain"

            Write-Output "Reverse Lookup Zone '$RevZone' added successfully."

        }

    } catch {

        # Handle Errors Gracefully

        if ($_.Exception.Message -match "ResourceExists") {

            Write-Output "Reverse Lookup Zone '$RevZone' already exists (error caught)."

        } else {

            Write-Error "Failed to add Reverse Lookup Zone '$RevZone': $_"

        }

    }

}

 

Have a nice day!

Leave a Reply

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