08 November, 2015

List DNS scavenging settings on multiple servers remotely - AD

DDNS (Dynamic DNS where clients register their own DNS records) was a very good idea when it was published in RFC2136 and had been missing like a slice of bread, but it inevitably left some questions on the table. For example, if I let 100 000 hosts register their own records, who will tell them to clean-up their stuff if they don't need it anymore? On the other hand, if I don't use DDNS and I have only one DHCP server registering addresses, I can just regulate that one guy and tell it off if it doesn't cleanup its rubbish.

The answer is DNS scavenging which would be the butter on that slice of bread just to make it taste better and proper. But we want to make sure that the butter we put on the bread is not rotten. Otherwise we would need to throw the bread to the bin with the butter... ok, enough of this nonsense.

DNS scavenging essentially deletes stale / old records from the given DNS Zone. What we want to make sure that the scavenging process is properly configured otherwise we could end up losing very valuable DNS records and cause outages - believe me, you don't want an outage caused by something as fundamental as DNS.

There are a couple of rules we need to keep in mind:
  • The scavenging intervals have to be thought through - I'd go with the default settings 7+7 days
  • There should be only 1 DNS server scavenging the zone regularly even if we have lots of e.g. Domain Controllers hosting the zone.
  • The zone should be restricted and only that one server should be allowed to scavenge the zone. You can read more about scavenging e.g. here and here 
Question: if I have 100 domain controllers hosting an AD integrated zone how can I check if there's only one set to scavenge the zone. To get these settings from one server, you can use dnscmd /info. To do it on multiple servers you can do some dnscmd output parsing in powershell, e.g.:

Create an object where you will store the name of the DNS host, the scavenging interval set on that server and the default aging state on that server:
$sObject = "" | select hostname,ScavengingInterval,LastScav,DefaultAgingState

Take the output of dnscmd /info and go through each line:
dnscmd $srv /info | %{

If it's the line where scavenging info is stored, do some regex matching to take out the bits you need:
if($_ -imatch "last scav"){
$value = ([regex]::Match($_, "= .+$")).value -replace "= ",""

Add it to your output object:
$sObject.LastScav = $value

It will show you an output like this:
Note the date and result of the last scavenging run









The full script:
 # get the list of Windows DNS servers from the pipe  
 $hostlist = @($Input)  
   
 $hostlistlength = ($hostlist | measure).count  
   
 # go through each host  
 foreach($srv in $hostlist){  
    $sObject = "" | select hostname,ScavengingInterval,LastScav,DefaultAgingState  
   
    # run dnscmd to get the detailed info of each DNS server  
    dnscmd $srv /info | %{  
       $value = $null  
   
       # pick out the data from dnscmd output with regex matches  
       if($_ -imatch "last scav"){  
          $value = ([regex]::Match($_, "= .+$")).value -replace "= ",""  
          $sObject.LastScav = $value  
          $value = $null  
       }  
       elseif($_ -imatch "ScavengingInterval"){  
          $value = ([regex]::Match($_, "= .+$")).value -replace "= ",""  
          $sObject.ScavengingInterval = $value  
          $value = $null  
       }  
       elseif($_ -imatch "DefaultAgingState"){  
          $value = ([regex]::Match($_, "= .+$")).value -replace "= ",""  
          $sObject.ScavengingInterval = $value  
          $value = $null  
       }  
    }  
    $sObject  
 }  
   




2 comments:

  1. Thanks for posting this script. It helped me discover that no server was doing the scavenging. I did change is slightly. I changed the read from the text file to read from AD to list all domain controllers and pipe that to into the hostlist. I kept the file reader in there so now if I want some DNS servers, I build the list. Otherwise I check them all.

    $hostlist = [system.directoryservices.activedirectory.domain]::GetCurrentDomain() | ForEach-Object {$_.DomainControllers} | ForEach-Object {$_.Name}
    # $hostlist = Get-Content $scriptFile

    $hostlistlength = ($hostlist | measure).count

    ReplyDelete
  2. I resolved my missing Hostname with this code:

    # get the list of Windows DNS servers from the pipe
    $hostlist = @($Input)
    $hostlist = [system.directoryservices.activedirectory.domain]::GetCurrentDomain() | ForEach-Object {$_.DomainControllers} | ForEach-Object {$_.Name}
    # $hostlist = Get-Content $scriptFile

    $hostlistlength = ($hostlist | measure).count

    # go through each host
    foreach($srv in $hostlist){
    $sObject = "" | select hostname,ScavengingInterval,LastScav,DefaultAgingState

    # run dnscmd to get the detailed info of each DNS server
    dnscmd $srv /info | %{
    $value = $null

    # pick out the data from dnscmd output with regex matches
    if($_ -imatch "server name"){
    $value = ([regex]::Match($_, "= .+$")).value -replace "= ",""
    $sObject.hostname = $value
    $value = $null
    }
    elseif($_ -imatch "last scav"){
    $value = ([regex]::Match($_, "= .+$")).value -replace "= ",""
    $sObject.LastScav = $value
    $value = $null
    }
    elseif($_ -imatch "ScavengingInterval"){
    $value = ([regex]::Match($_, "= .+$")).value -replace "= ",""
    $sObject.ScavengingInterval = $value
    $value = $null
    }
    elseif($_ -imatch "DefaultAgingState"){
    $value = ([regex]::Match($_, "= .+$")).value -replace "= ",""
    $sObject.DefaultAgingState = $value
    $value = $null
    }
    }
    $sObject
    }

    ReplyDelete