<< Back to Script Library

Find Duplicate DNS entries

Find all the duplicate IP addresses and the duplicate names contained within the DNS zone for the local domain. It skips root entries for the domain, however ("@"). See the script for more documentation.
Version: 1.6.10
Created: 2016-10-06
Modified: 2016-11-07
Creator: Michael B. Smith
Downloads: 385
Tags: DNS
The Script Copy Script Copied to clipboard
##
## build-dns-objects.ps1
##

##
## Michael B. Smith
## michael (at) TheEssentialExchange.com
## April, 2012
##
## Patched 2016-02-03
## If a computer had multiple IP addresses, the scanner would get confused and
## not properly populate the $objects array.
##

##
## Primary functionality:
##
## Based on either an input file or the output of a default command:
##
##  dnscmd ( $env:LogonServer ).SubString( 2 ) /enumrecords $env:UserDnsDomain "@"
##
## Create an array containing all of the DNS objects describing the input.
##
## ----
##
## Secondary functionality:
##
## Find all the duplicate IP addresses and the duplicate names 
## contained within either the file or the command output.
##
## By specifying the -skipRoot option, all records for the root of
## the domain are ignored.
##

##
## General record format returned by DNScmd.exe:
##
## name
## [aging:xxxxxxxx] 
## TTL
## resource-record-type
## value
## [optional additional values]
##
## Fields may be separated by one-or-more spaces or one-or-more tabs
## [aging:xxxxxxxx] fields are optional
##

[CmdletBinding(SupportsShouldProcess=$false, ConfirmImpact='None')]

Param(
 [string]$filename,
 [switch]$skipRoot=$true
)

Set-StrictMode -Version 2.0

function new-dns-object
{
 return ( "" | Select Name, Aging, TTL, RRtype, Value )
}

function tmpFileName
{
 [string] $strFile = ( Join-Path $Env:Temp ( Get-Random ) ) + ".txt"
 Write-Verbose "tmpFileName $strFile"

 if( ( Test-Path -Path $strFile -PathType Leaf ) )
 {
  rm $strNetworkFile -EA 0
  if( $? )
  {
  ## write-output "...file was deleted"
  }
  else
  {
  ## write-output "...couldn't delete file, error: $($error[0].ToString())"
  }
 }

 return $strFile
}

# verify dnscmd.exe exists locally before running the script
If (! (Test-Path (Join-Path (Split-Path $env:comspec) "dnscmd.exe") )) {
    Write-Host "dnscmd.exe does not exist on this computer. The script cannot continue."
    Exit 1
} Else {

    if( $filename -and ( $filename.Length -gt 0 ) )
    {
     $tmp = $filename
    }
    else
    {
     $tmp = tmpFileName
     dnscmd ( $env:LogonServer ).SubString( 2 ) /enumrecords $env:UserDnsDomain "@" >$tmp
    }

    $objects = @()
    $records = gc $tmp
    Write-Verbose "records = $( $records.Count )"

    $priorName = ''

    ## Primary functionality:

    foreach( $record in $records )
    {
     ## Write-Debug "Processing: $record"

     if( !$record )
     {
      continue
     }
     if( $record -eq "Returned records:" )
     {
      continue
     }
     if( $record -eq "Command completed successfully." )
     {
      continue
     }

     if( $record -match "SOA" )
     {
      continue
     }

     $firstChar = $record.SubString( 0, 1 )
     $record = $record.Trim()

     if( $record.Length -eq 0 )
     {
      continue
     }

     $object = new-dns-object

     $index = 0

     $record = $record.Replace( "`t", " " )
     $record = $record.Replace( "  ", " " )
     $record = $record.Replace( "  ", " " )
     $record = $record.Replace( "  ", " " )
     Write-Debug "'$record'"

     $array = $record.Split( ' ' )
     Write-Debug "array contains $( $array.Count ) elements"
     if( $array.Count -gt 5 )
     {
      Write-Warning "This record has been parsed incorrectly: '$record'"
     }

     if( ( $firstchar -eq " " ) -or ( $firstchar -eq "`t" ) )
     {
      $object.Name = $priorName
      Write-Debug "Assigned priorName '$priorName'"
     }
     else
     {
      $object.Name = $array[ 0 ]
      $priorName   = $array[ 0 ]
      $index++
     }

     if(($array[$index].Length -ge 3) -and ($array[ $index ].SubString( 0, 3 ) -eq "[Ag"))  ## [Aging:3604987]
     {
      $object.Aging = $array[ $index ]
      $index++
     }
     
     $object.TTL    = $array[ $index ]
     $object.RRType = $array[ $index + 1 ]
     $object.Value  = $array[ $index + 2 ]

     $objects += $object

     Write-Debug $object
    }

    ## Secondary functionality:

    ## There are more efficient ways to do this, but this is easy.

    ## search for duplicate names

    Write-Host "Duplicates for $env:UserDnsDomain :"
    $hash = @{}
    $duplicates = 0
    foreach( $o in $objects )
    {
     if( $o.RRtype -eq "A" )
     {
      $name = $o.Name
      if( $skipRoot -and ( $name -eq "@" ) )
      {
       continue
      }
      if( $hash.ContainsKey( $name ) )
      {
       "Duplicate name: $name, IP: $($o.Value), original IP: $($hash[ $name ])"
                $duplicates++
      }
      else
      {
       $hash[ $name ] = $o.Value
      }
     }
    }
    $hash = $null

    ## search for duplicate IP addresses

    $hash = @{}
    foreach( $o in $objects )
    {
     if( $o.RRtype -eq "A" )
     {
      if( $skipRoot -and ( $o.Name -eq "@" ) )
      {
       continue
      }

      $ip = $o.Value
      if( $hash.ContainsKey( $ip ) )
      {
       "Duplicate IP: $ip, name: $($o.Name), original name: $($hash[ $ip ])"
                $duplicates++
      }
      else
      {
       $hash[ $ip ] = $o.Name
      }
     }
    }
    $hash = $null

    #$global:DNSobjects = $objects
    
    If ($duplicates -eq "0") {
        Write-Host "No duplicates found."
    }
    " "
    "Done"
}