My Remote Desktop Session Host build process is now pretty slick. I can easily rebuild a lot of RDSH VMs very quickly. The process basically involved powering off the existing VM, deleting both it and its Active Directory account, and provisioning a new VM. However this causes a slight issue because the AD DNS record is left behind from the old VM, with the security to update it still assigned to the old VM. This means that if I change the allocated IP address for an RDSH server as part of the rebuild, the new VM doesn’t have permission to update the DNS A and PTR records for itself (security is done by AD SID, DNS just works on names/IP addresses).
So, time to use Get-DnsServerResourceRecord and Remove-DnsServerResourceRecord.
Doing the A record is easy:
$NodeToDelete = "rdsh01" $DNSServer = "dns01.rcmtech.co.uk" $ZoneName = "rcmtech.co.uk" $NodeDNS = $null $NodeDNS = Get-DnsServerResourceRecord -ZoneName $ZoneName -ComputerName $DNSServer -Node $NodeToDelete -RRType A -ErrorAction SilentlyContinue if($NodeDNS -eq $null){ Write-Host "No DNS record found" } else { Remove-DnsServerResourceRecord -ZoneName $ZoneName -ComputerName $DNSServer -InputObject $NodeDNS -Force }
So we try and get the DNS record we’re interested in. If the record is found, we delete it. I was originally doing this not using an -ErrorAction of “SilentlyContinue” but a “Stop”, which worked to a point but was inconsistent: If the DNS name I was trying to get had never existed (i.e. I made something up) it worked fine, but if I created an A record, ran the script to delete it, then tried the script again with the same DNS name to delete, the Get-DnsServerResourceRecord didn’t error. It didn’t return anything either, and hence why this is coded to use an if statement to check whether $NodeDNS is $null or not, rather than using a try/catch as was my original plan.
The PTR record is a little more fiddly. Now, getting PTR info via nslookup is easy:
C:\> nslookup 192.168.1.123 Server: dns01.rcmtech.co.uk Address: 192.168.1.10 Name: rdsh01.rcmtech.co.uk Address 192.168.1.123
Not so with PowerShell… You have to specify the reverse lookup zone, and also give the right bit of the IP address in the right order.
For example, I want to find the name of the machine with IP address 192.168.1.123, and let’s assume my internet class B address range is 192.168.0.0.
Get-DnsServerResourceRecord -ZoneName "168.192.in-addr.arpa" -ComputerName "dns01.rcmtech.co.uk" -Node "123.1" -RRtype Ptr
…nice. You can’t just pass it the full IP address as you can with nslookup. Oh no, we have to be a little more “creative”.
What I’ve ended up doing is using Get-DnsServerResourceRecord to look up the A record of the server to get its IP address, split this into an array – each element containing an octet of the address, then delete the PTR record and finally the A record.
param($NodeToDelete= "") if($NodeToDelete-eq ""){ Write-Error "You need to specify a name to delete" -Category NotSpecified -CategoryReason "Need a name to delete" -CategoryTargetName "Missing parameter" -CategoryTargetType "DNS name" return } $DNSServer = "dns01.rcmtech.co.uk" $ZoneName = "rcmtech.co.uk" $ReverseZoneName = "168.192.in-addr.arpa" $NodeARecord = $null $NodePTRRecord = $null Write-Host "Check for existing DNS record(s)" $NodeARecord = Get-DnsServerResourceRecord -ZoneName $ZoneName -ComputerName $DNSServer -Node $NodeToDelete -RRType A -ErrorAction SilentlyContinue if($NodeARecord -eq $null){ Write-Host "No A record found" } else { $IPAddress = $NodeARecord.RecordData.IPv4Address.IPAddressToString $IPAddressArray = $IPAddress.Split(".") $IPAddressFormatted = ($IPAddressArray[3]+"."+$IPAddressArray[2]) $NodePTRRecord = Get-DnsServerResourceRecord -ZoneName $ReverseZoneName -ComputerName $DNSServer -Node $IPAddressFormatted -RRType Ptr -ErrorAction SilentlyContinue if($NodePTRRecord -eq $null){ Write-Host "No PTR record found" } else { Remove-DnsServerResourceRecord -ZoneName $ReverseZoneName -ComputerName $DNSServer -InputObject $NodePTRRecord -Force Write-Host ("PTR gone: "+$IPAddressFormatted) } Remove-DnsServerResourceRecord -ZoneName $ZoneName -ComputerName $DNSServer -InputObject $NodeARecord -Force Write-Host ("A gone: "+$NodeARecord.HostName) }
I think you have to account for ptr’s in any reverse zone before you declare there isn’t a ptr. I have yet to see an organization that is really strict on creating a reverse zone for every subnet.
Test the last three octets, then two, then one.
$reversezone = $ip -replace ‘^(\d+)\.(\d+)\.(\d+)\.(\d+)$’,’$3.$2.$1.in-addr.arpa’
LikeLike
Hi Guys, I Just created on Script using PtrDomainName as key to filter and delete Ptr records, I think is better becouse we don´t need to searh for Ip address in the code…
In My case I deleted all HostNames starting with 12 numbers, that is garbage in my DNS.
But you can adapt to Hostname geting by the main Zone.
$zone=”20.10.in-addr.arpa”
$DNSServer=”SERVER”
$recordtype=”Ptr”
$records=Get-DnsServerResourceRecord -ZoneName “$zone” -ComputerName $DNSServer |
Where-Object {$_.RecordType -eq “$recordtype” -and $_.RecordData.PtrDomainName.Substring(0,12) -match “^[0-9]*$”}
Foreach ($record in $records)
{
# Remove the DNS record by filtering
Try
{
$hostadi=$record.HostName
$info=$record.RecordData.PtrDomainName
Remove-DnsServerResourceRecord -ZoneName $zone -ComputerName $DNSServer -Force -RRType “$recordtype” -Name $record.HostName
Write-Host (“[{0}] deleted record name is : $hostadi ($info)” -f (Get-Date))
(“[{0}] delete record name: $hostadi ($info)” -f (Get-Date)) | out-file “E:\LimpaDNS\LimpaPTR.txt” -Append
}
Catch
{
Write-Host (“[{0}] Cannot delete the record: $hostadi ($info)” -f (Get-Date))
(“[{0}] Cannot delete the record name: $hostadi ($info)” -f (Get-Date)) | out-file “E:\LimpaDNS\LimpaPTR.txt” -Append
}
LikeLike
Hi,
Inspired in these two (the original and the one in the comments), I just came across with this one. Adventages:
– Environment agnostic: it finds the DNS zones and a DNS server by itself.
– Use of Resolve-DnsName to simplify the obtention of the properties of the record (I really don’t understand why the output Get-DnsServerResourceRecord is so “complex”).
Cons:
– Not that I’ve controlled it much, just that the A and the PTR records are for the same machine.
– Use of Resolve-DnsName, so at least 2012 R2 is required, but that should not be a problem by now.
The script:
$ListOfHosts = @("Server1","Server2","Server3")
foreach ($HostToDelete in $ListOfHosts){
$DNSDirectZone = $env:userdnsdomain
$DNSServer = $env:logonserver -replace '\\',''
$DNSARecord = Resolve-DnsName $HostToDelete
$AHostName = $DNSARecord.Name -replace $DNSDirectZone,"" -replace "\.$",""
$DNSPtrRecord = Resolve-DnsName $DNSARecord.IPAddress
$DNSReverseZone = (Get-DnsServerZone -ComputerName $DNSServer | ?{$DNSPtrRecord.Name -match $_.ZoneName -and $_.IsDsIntegrated -eq $true}).ZoneName
$PtrHostName = $DNSARecord.IPAddress -split "\."
[array]::Reverse($PtrHostName)
$PtrHostName = $PtrHostName -join "." -replace $DNSReverseZoneSuffix,"" -replace "\.$",""
if ($DNSPtrRecord.NameHost -eq $DNSARecord.Name) {
Remove-DnsServerResourceRecord -ComputerName $DNSServer -ZoneName $DNSReverseZone -Name $PtrHostName -RRType Ptr -Confirm:$false -Force
}
Remove-DnsServerResourceRecord -ComputerName $DNSServer -ZoneName $DNSDirectZone -Name $AHostName -RRType A -Confirm:$false -Force
}
LikeLike
Hi curropar.
Thanks for your combined script.
I’ve fixed one line in it though:
Old:
$DNSReverseZone = (Get-DnsServerZone -ComputerName $DNSServer | ?{$DNSPtrRecord.Name -match $_.ZoneName -and $_.IsDsIntegrated -eq $true}).ZoneName
New:
$DNSReverseZone = (Get-DnsServerZone -ComputerName $DNSServer | Where-Object {($DNSPtrRecord.Name.Substring($DNSPtrRecord.Name.Split(‘.’)[0].Length+1,$DNSPtrRecord.Name.Length-$DNSPtrRecord.Name.Split(‘.’)[0].Length-1) -eq $_.Zonename) -and ($_.IsDsIntegrated -eq $true)}).ZoneName
This is better because “-match” could return more than one value.
Regards,
Tom
LikeLike
I’ve created more automated way of doing this. It goes trough all Reverse Lookup zones and matches where PTR record exists and then removes both A and PTR
To be found here:
https://www.powershellgallery.com/packages/Remove-DNSEntry/1.0
LikeLike
Inspired by these and other scripts, but noting they nearly all had at least one bug, especially around removing PTR records, I’ve built the following:
/code
[CmdletBinding()]
param(
[parameter(Position=0)]$HostToDelete
)
if($HostToDelete -eq $null) {
Write-Host “Usage:`n rm-from-dns.ps1 [-Verbose] -HostToDelete `n”
exit
}
$DNSServer = Get-DnsClientServerAddress | select-object -expandproperty ServerAddresses | select -first 1
$DNSARecord = Resolve-DnsName $HostToDelete
$ReverseZoneNames = @(Get-DnsServerZone -ComputerName $DNSServer | ? { $_.IsReverseLookupZone -and $_.IsDsIntegrated -and -NOT($_.ZoneType -eq “Forwarder”) } | select-object -expandproperty ZoneName)
if($DNSARecord -eq $null){
Write-Host “No record found”
} else {
$ZnName = $DNSARecord.Name
$ZnName = $ZnName -replace ‘^[^\.]+\.’,”
$AHostName = $DNSARecord.Name -replace $ZnName,”” -replace “\.$”,””
Write-Verbose (“host name: $AHostName zone name: $ZnName”)
if($DNSARecord.Type -eq “A”) {
$DNSPtrRecord = Resolve-DnsName $DNSARecord.IPAddress
if($DNSPtrRecord -eq $null){
Write-Verbose (“No PTR record found”)
} else {
if ($DNSPtrRecord.NameHost -ne $DNSARecord.Name) {
Write-Verbose (“Fwd/Reverse Names didn’t match: -ne “)
} else {
# Finding the correct reverse zone name is a bit of a pain.
# Lots of scripts assume /16’s, others attempt to check for a /24, then for a /16
# Many scripts found on the internet are just broken, my partially functional but broken version:
# foreach($thing in $ReverseZoneNames) {
# if($DNSPtrRecord.Name -match [Regex]::Escape($thing)) {
# Write-Host “$thing matched”
# }
# }
# The idea would be to take the above, adjust to find the longest match, but that won’t work either
# suppose we have the following reverse zones; and yes, a /16 and assorted /24’s is bad form, but it’s a real world possibility
# 167.10.in-addr.arpa
# 3.167.10.in-addr.arpa
# 10.167.10.in-addr.arpa
# 33.167.10.in-addr.arpa
# and we’re trying to find the reverse zone for 12.133.167.10.in-addr.arpa.
# The longest match is the last one above, but the correct match is the first one.
$RevAry = $DNSPtrRecord.Name.Split(‘.’)
for($n = 5; $n -ge 3; $n–) {
$testZone = ($RevAry | select -last $n) -join “.”
Write-Verbose (“checking for $testZone”)
if($ReverseZoneNames -contains $testZone) {
Write-Verbose (” — Found it!”)
Break
}
}
if($n -ge 3) {
$i = 6 – $n
$RevZone = $testZone
$RevHost = ($RevAry | select -first $i) -join “.”
Write-Verbose (“ReverseZone: $RevZone PtrHostName: $RevHost”)
Remove-DnsServerResourceRecord -ComputerName $DNSServer -ZoneName $RevZone -Name $RevHost -RRType Ptr -Confirm:$false -Force
Write-Host (“PTR record deleted: ” + $RevHost + ” from ” + $RevZone)
} else {
Write-Host (“Found PTR record, but not zone?: ” + $DNSPtrRecord.Name)
}
}
}
Remove-DnsServerResourceRecord -ComputerName $DNSServer -ZoneName $ZnName -Name $AHostName -RRType A -Confirm:$false -Force
Write-Host (“A record deleted: ” + $AHostName + ” from ” + $ZnName)
}
if($DNSARecord.Type -eq “CNAME”) {
Remove-DnsServerResourceRecord -ComputerName $DNSServer -ZoneName $ZnName -Name $AHostName -RRType CNAME -Confirm:$false -Force
Write-Host (“CNAME record deleted: ” + $AHostName + ” from ” + $ZnName)
}
}
LikeLike
Ahem… Why not use “-recorddata”?
$name = “myservername”
$zone = “zone.local”
$rdat = (Resolve-dnsname $name”.”$zone).ipaddress
Remove-DnsServerResourceRecord -RRType A -zonename $zone -Name $name -RecordData $rdat -verbose
Feel free to limit the script back to one-liner.
LikeLike