We deal with the exact same problem and I solved this issue by manually clearing out the duplicate DNS values via Powershell. We run the following script every 5 minutes on the domain controller. Be sure to modify the 5 variables at the top. This script only works when the VPN subnet is uniquely identifiable in DNS and does not share IPs with other subnets. Another requirement is that the VPN clients must be pingable and reachable via Invoke-Command. It is also only written for IPv4. Hope this helps you.
$DnsServer = "DC.company.local"
$Subnet = "192.168.200." # enter IP up to the point where the VPN subnet is uniquely identified within the DNS zone
$ForwardZone = "company.local"
$ReverseZone = "200.168.192.in-addr.arpa"
$forwardZoneRegex = [regex]"(.*\.company\.local)" # same as ForwardZone but add .* in front of it and escape all dots
function cleanForwardZone {
# get all VPN IP addresses from the DNS server and count them
$VpnIPs = @{}
Get-DnsServerResourceRecord -ZoneName $ForwardZone -ComputerName $DnsServer -RRType A | Where-Object {($_.RecordData.IPv4Address.IPAddressToString).StartsWith($Subnet)} | ForEach-Object {
$VpnIPs[$_.RecordData.IPv4Address.IPAddressToString] ++
}
# extract duplicate IPs from Hashtable and put them into a new array
$duplicateVpnIPs = @()
$VpnIPs.GetEnumerator() | Where-Object {$_.Value -gt 1} | ForEach-Object {
$duplicateVpnIPs += $_.Key.ToString()
}
if ($duplicateVpnIPs) {
foreach ($duplicateVpnIP in $duplicateVpnIPs) {
$sortedNodes = Get-DnsServerResourceRecord -ZoneName $ForwardZone -ComputerName $DnsServer -RRType A | Where-Object {$_.RecordData.IPv4Address.IPAddressToString -eq $duplicateVpnIP} | Sort-Object Timestamp
if ($null -eq $sortedNodes.Count) {
# this handles an edge case where one of the duplicate DNS entries was removed between the first and second call of Get-DnsServerResourceRecord, so the duplicate entry does not exist anymore. In this case $sortedNodes is not an array (only one element) and $sortedNodes.Count does not return anything
continue
}
if (($sortedNodes[$sortedNodes.Count - 1].Timestamp) -eq ($sortedNodes[$sortedNodes.Count - 2].Timestamp)) {
# the last two timestamps are identical. We need to find out which host is the right one. Invoke-Command only responds when the -ComputerName matches the actual hostname
$host1 = Invoke-Command -ComputerName $sortedNodes[$sortedNodes.Count - 1].HostName -ScriptBlock {hostname} -ErrorAction SilentlyContinue
$host2 = Invoke-Command -ComputerName $sortedNodes[$sortedNodes.Count - 2].HostName -ScriptBlock {hostname} -ErrorAction SilentlyContinue
if (($null -eq $host1) -and ($null -eq $host2)) {
# both hosts were unreachable, delete all DNS entries
$nodesToDelete = $sortedNodes
} elseif ($null -ne $host1) {
# host1 responded to the Invoke, he is the right one. Delete all but him
$nodesToDelete = $sortedNodes | Where-Object {$_.HostName -ne $host1}
} elseif ($null -ne $host2) {
# host2 responded to the Invoke, he is the right one. Delete all but him
$nodesToDelete = $sortedNodes | Where-Object {$_.HostName -ne $host2}
}
} else {
# no latest duplicate timestamp found. Keep latest entry and delete all others
$nodesToDelete = $sortedNodes | Sort-Object Timestamp | Select-Object -First ($sortedNodes.Count - 1)
}
$verboseOutput = $nodesToDelete | Remove-DnsServerResourceRecord -ZoneName $ForwardZone -Force -Verbose 4>&1
}
}
}
function cleanReverseZone {
$PTRs = Get-DnsServerResourceRecord -ZoneName $ReverseZone -ComputerName $DnsServer -RRType PTR
foreach ($PTR in $PTRs) {
$hostName = ([regex]::Match($PTR.RecordData.PtrDomainName,$forwardZoneRegex)).Groups[1].Value
$forwardIP = (Resolve-DnsName -Name $hostName -DnsOnly -Type A -Server $DnsServer -ErrorAction SilentlyContinue).IPAddress
$reverseIP = $Subnet + $PTR.Hostname
$pingResult = Test-Connection -Count 1 -ComputerName $ReverseIP -Quiet
if ($pingResult -eq $false) {
# the host would get another IP the next time anyway, so we can safely delete the IP. Also we have to delete the record so that a new client can safely claim the PTR record
$verboseOutput = $PTR | Remove-DnsServerResourceRecord -ZoneName $ReverseZone -Force -Verbose 4>&1
} elseif (($pingResult -eq $true) -and ($forwardIP -eq $reverseIP)) {
# the IPs match, which means that the PTR record is correct because it mirrors the A record. Do nothing in this case. The possibility of multiple A records with the same IP is getting eliminated because we run "cleanForwardZone" first
} elseif ($forwardIP -ne $reverseIP) {
$verboseOutput = $PTR | Remove-DnsServerResourceRecord -ZoneName $ReverseZone -Force -Verbose 4>&1
}
}
}
# call functions
cleanForwardZone
cleanReverseZone