This post explores how identification of stale records in a dynamically updated Microsoft DNS zone.
The time stamp taken from a DNS record represents the numbers of hours since 01/01/1601 00:00. By default, all times are reported and tested in UTC.
A stale record is a record where both the No-Refresh Interval and Refresh Interval have passed without the time stamp updating. Ordinarily stale records would be removed by a Scavenging process.
Listing stale records with VbScript
This script uses a WMI query to return all A records for a domain, then it sorts through each record, echoing when the time stamp is older than our pre-defined maximum age. The script will work best when run with cscript.
' No-Refresh + Refresh (in Days)
Const MAXIMUM_AGE = 4
' Connect to the MicrosoftDNS Namespace
Set objWMIService = _
GetObject("winmgmts:\\dc01.domain.example\root\MicrosoftDNS")
' Query A records with MicrosoftDNS_AType class where the record is not static
Set colItems = objWMIService.ExecQuery("SELECT * FROM MicrosoftDNS_AType " & _
" WHERE ContainerName='domain.example' AND TimeStamp<>0")
For Each objItem In colItems
' Convert the timestamp into a date and time
dtmTimeStamp = DateAdd("h", objItem.TimeStamp, "01/01/1601 00:00:00")
' Compare the date and time with MAXIMUM_AGE
If dtmTimeStamp <= (Date() - MAXIMUM_AGE) Then
' Echo the record details if it is older than the MAXIMUM_AGE
WScript.Echo objItem.OwnerName & VbTab & objItem.IPAddress & _
VbTab & dtmTimeStamp
End If
Next
Listing stale records with PowerShell
This snippet uses Get-WMIObject and a improved query to return only stale records rather than sorting after returning all dynamic records.
A timespan value is generated to represent the minimum value of TimeStamp for valid records.
# No-Refresh + Refresh (in Days)
$TotalAgingInterval = 4
$params = @{
Filter = 'ContainerName="domain.example" AND TimeStamp<>0'
Class = 'MicrosoftDNS_AType'
Namespace = 'root\MicrosoftDNS'
ComputerName = 'dc01.domain.example'
}
Get-WmiObject @params |
Select-Object *, @{
Name = 'TimeStamp'
Expression = {
(Get-Date "01/01/1601").AddHours($_.TimeStamp)
}
} |
Where-Object { $_.TimeStamp -lt (Get-Date).AddDays($TotalAgingInterval) }
Reading Aging intervals with PowerShell
The Aging intervals and the date the zone can be scavenged set on a zone can be read using WMI using the MicrosoftDNS_Zone class. As with the TimeStamp the .AddHours method must be used to return a date.
$params = @{
Filter = 'ContainerName="domain.example"'
Class = 'MicrosoftDNS_Zone'
Namespace = 'root\MicrosoftDNS'
ComputerName = 'dc01.domain.example'
}
Get-WmiObject @params |
Select-Object NoRefreshInterval, RefreshInterval, @{
Name = 'AvailForScavengeTime'
Expression = { (Get-Date "01/01/1601").AddHours($_.AvailForScavengeTime) }
}
Localisation
As mentioned at the beginning of this post, all times are reported in UTC by default. By calling a the ToLocalTime method the date returned can be converted to local time, using the time zone configured on the system executing the query.
# No-Refresh + Refresh (in Days)
$TotalAgingInterval = 4
$params = @{
Filter = 'ContainerName="domain.example" AND TimeStamp<>0'
Class = 'MicrosoftDNS_AType'
Namespace = 'root\MicrosoftDNS'
ComputerName = 'dc01.domain.example'
}
Get-WmiObject @params |
Select-Object *, @{
Name = 'TimeStamp'
Expression = {
(Get-Date "01/01/1601").AddHours($_.TimeStamp).ToLocalTime()
}
}