Detect duplicate Windows DNS PTR records with PowerShell

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search

The main script uses the PowerShell module DnsShell from Codeplex, which you will need to download to use that script, and it finds duplicate PTR records on a Windows DNS server. DnsShell uses WMI calls. It was tested against a Windows Server 2008 R2 DNS server, but should also work against 2003 R2. Not tested against 2012 and up; feel free to provide feedback if you test it (check the about link at the bottom for contact details).

I also tossed in a version that can be adapted to not rely on DnsShell, but you will then somehow need to get the reverse zones into a file or variable in the correct format. It can also be used with DnsShell to produce slightly different-looking results from what the main script does.

The script is written for "interactive use", so it displays results to the screen. You can edit it to suit your needs. It also exports a CSV if it finds any duplicate PTR records (apparently it always has a positive count, so it gets written anyway).

Multiple host names for the same PTR record are displayed separated by a semicolon in the data.

It was tested with PowerShell v3, but should work with v2 (the latter being the default in Server 2008 R2 and Windows 7).




Download Main Script

Get-DupePTR.ps1.txt - right click and save.

You will need to supply the -DnsServer parameter.

You can also tack on the -Verbose parameter for verbose output (this will also cause verbose output from the DnsShell module to show up).

Script Code

[CmdletBinding()]
param([Parameter(Mandatory=$true)][string] $DnsServer)

# Copyright 2014, Svendsen Tech
# All rights reserved.
# Joakim Borger Svendsen
# 2014-08-28

Import-Module .\DnsShell # this has to exist ( http://dnsshell.codeplex.com/ )

$Zones = Get-DnsZone -Server $DnsServer

$ReverseZones = $Zones | Where-Object { $_.ZoneName -like '*.arpa' } |
    Select-Object -ExpandProperty ZoneName

# Example zone:
# $ReverseZones = @('12.10.in-addr.arpa')

$Dupes = @(foreach ($z in $ReverseZones) {
    
    Write-Verbose -Message "Processing zone: $z"
    
    $Records = Get-DnsRecord -ServerName $DnsServer -ZoneName $z -RecordType PTR
    $Records | ForEach-Object -Begin {
        $IpHash = @{}
        } -Process {
        $TempArray = $_.Name -replace '\.in-addr\.arpa$' -split '\.'
        $Ip = ($TempArray[-1..-($TempArray.Count)]) -join '.'
        if (-not $IpHash.ContainsKey($Ip)) {
            # New PTR. The Name property is the PTR. Create one-element array.
            $IpHash.$Ip = @($_.HostName)
        }
        else {
            # Duplicate/alias PTR, add to array.
            $IpHash.$Ip += $_.HostName
        }
            
    }
    $Duplicates = $IpHash.GetEnumerator() | Where-Object { $_.Value.Count -gt 1 }
    $Duplicates | ForEach-Object {
        New-Object psobject -Property @{
            IP = $_.Name
            Duplicates = $_.Value -join '; '
        }
    }
})

# Display in console.
$Dupes | Select IP, Duplicates | Format-Table -AutoSize

# Export CSV file (if any dupes found).
if ($Dupes.Count) {
    Write-Host -Fore Green 'Exporting CSV file PTRdupes.csv'
    $Dupes | Select IP, Duplicates | Export-Csv -Encoding UTF8 -NoTypeInformation -Path PTRdupes.csv -Delimiter ';'
}

Another Version That's not Necessarily Dependent on DnsShell

NB! This code below needs to be edited in order to work. Here's an example of a version of the duplicate DNS PTR script that can be adapted to not use DnsShell, if you can somehow get the zones into a file/array. The hard work has been taken care of with dnscmd.exe and parsing its output. This version also has some more detail than the one purely using DnsShell above, with the differentiation between host name and aliases.

This version emits objects to the pipeline, so you will probably want to do this (after editing it to fit your environment):

> $Var = .\Get-DupePTR.ps1
...
> $Var | Format-Table # or format-list, export-csv, or whatever sensible you want to do

Download (alternative version)

Get-DupePTR-dnscmd.ps1.txt - right click and download.

Script Code (alternative version)

Adding this code in case it's useful for someone.

[CmdletBinding()]
param()
# Copyright (C) 2014, Svendsen Tech. All rights reserved.
# Joakim Borger Svendsen
# 2014-08-15
# edited: 2014-11-08

$DnsServer = 'your.dns.server'

# This uses DnsShell to get the zones, but you can edit this to read
# zones from a file, or whatever, to avoid DnsShell.
# It uses dnscmd.exe below and parses its output, and then uses the
# (apparently obsolete) [Net.Dns] class' GetHostByAddress() method
# to look up the IP
####
#### Zones must be in the form 58.10.in-addr.arpa (for 10.58.0.0/16).
####

Import-Module .\DnsShell # this has to exist ( http://dnsshell.codeplex.com/ )
$Zones = Get-DnsZone -Server $DnsServer
$ReverseZones = $Zones | Where { $_.ZoneName -like '*.arpa' } | Select -ExpandProperty ZoneName # @('58.10.in-addr.arpa') # = 

foreach ($z in $ReverseZones) {
    Write-Host -Fore Green "Processing zone: $z" -NoNewline
    $Records = dnscmd.exe $DnsServer /ZonePrint $z # Get all records and parse them looking for duplicates below.
    #Write-Host -Fore Yellow " -- Num lines:" $Records.Count
    $Records | ForEach-Object -Begin { $IpHash = @{}; $Ctr = 0 } `
        -Process {
            if ($_ -match '^([\d.]+)\s+.*PTR\s+(\S+)\.$') {
                $IpEndReversed = $matches[1]
                #Write-Verbose $IpEndReversed
                $Ctr++
                Start-Sleep -Milliseconds 25
                $TempArray = $z -replace '\.in-addr\.arpa$' -split '\.'
                $IpStart = ($TempArray[-1..-($TempArray.Count)]) -join '.'
                $TempArray = $IpEndReversed -split '\.'
                $IpEnd = ($TempArray[-1..-($TempArray.Count)]) -join '.'
                #Write-Verbose $IpEnd
                $Ip = "$IpStart.$IpEnd"
                
                #Write-Verbose "Looking up $Ip..."
                $ErrorActionPreference = 'Stop'
                try {
                    $Dns = [Net.Dns]::GetHostByAddress($Ip)
                    # This means we found a duplicate. Send an object down the pipeline.
                    if ($Dns.Aliases -match '\S') {
                        New-Object psobject -Property @{
                            IP = $Ip
                            HostName = $Dns.HostName -join ', '
                            Aliases = $Dns.Aliases -join ', '
                            Error = $null
                        }
                    }
                }
                catch {
                    New-Object psobject -Property @{
                        IP = $Ip
                        HostName = $null
                        Aliases = $null
                        Error = $_
                    }
                }
                $ErrorActionPreference = 'Continue'
            }
        }
    Write-Host -Fore Yellow " -- Num records:" $Ctr
    #Write-Verbose "Processed $Ctr PTR records for $z"
    #Start-Sleep -Seconds 3
}