Get and delete DNS A and PTR records via PowerShell

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)
}
This entry was posted in PowerShell, Scripting, Windows and tagged , , , , , , , , , , , , . Bookmark the permalink.

7 Responses to Get and delete DNS A and PTR records via PowerShell

  1. DanP. says:

    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’

    Like

  2. Alan says:

    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
    }

    Like

  3. curropar says:

    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
    }

    Like

  4. Tom says:

    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

    Like

  5. mik-konop says:

    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

    Like

  6. Slmiller says:

    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)
    }
    }

    Like

  7. eilsafin says:

    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.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.