Create DNS PTR record if A record exists.

This script will help you to have a DNS PTR record for each existing A record in your DNS zone.

If a A record has been created in the DNS zone ($dnsDomainName variable) without a PTR record, the script detect it and the PTR record is created in the correct DNS reverse zone.

Script :


Reference

Invoke-WmiMethod

Syntax

Detailed Description

The Invoke-WmiMethod cmdlet calls the methods of WMI objects.

New CIM cmdlets, introduced Windows PowerShell 3.0, perform the same tasks as the WMI cmdlets. The CIM cmdlets comply with WS-Management (WSMan) standards and with the Common Information Model (CIM) standard, which enables the cmdlets to use the same techniques to manage Windows computers and those running other operating systems. Instead of using Invoke-WmiMethod, consider using Invoke-CimMethod.

Parameters
-ArgumentList

Specifies the parameters to pass to the called method. The value of this parameter must be an array of objects, and they must appear in the order required by the called method (note that new Invoke-CimCommand does not have these limitations).

To determine the order in which to list those objects, run the GetMethodParameters() method on the WMI class, as illustrated in Example 1, near the end of this topic.

Important: If the first value is an array that contains more than one element, a second value of $null is required. Otherwise, the command generates an error, such as “Unable to cast object of type ‘System.Byte’ to type ‘System.Array’.”.

An example using an array of objects ($binSD) followed by a null value ($null) follows:

PS C:\>$acl = get-acl test.txt

PS C:\>$binSD = $acl.GetSecurityDescriptorBinaryForm()

PS C:\>invoke-wmimethod -class Win32_SecurityDescriptorHelper -Name BinarySDToSDDL -argumentlist $binSD, $null

Aliases Args
Required? false
Position? named
Default Value none
Accept Pipeline Input? false
Accept Wildcard Characters? false
-AsJob

Runs the command as a background job. Use this parameter to run commands that take a long time to finish.

When you use the AsJob parameter, the command returns an object that represents the background job and then displays the command prompt. You can continue to work in the session while the job finishes. If Invoke-WmiMethod is used against a remote computer, the job is created on the local computer, and the results from remote computers are automatically returned to the local computer. To manage the job, use the cmdlets that contain the Job noun (the Job cmdlets). To get the job results, use the Receive-Job cmdlet.

Note: To use this parameter with remote computers, the local and remote computers must be configured for remoting. Additionally, you must start Windows PowerShell by using the “Run as administrator” option in Windows Vista and later versions of Windows. For more information, seeabout_Remote_Requirements.

For more information about Windows PowerShell background jobs, see about_Jobs and about_Remote_Jobs.

Aliases none
Required? false
Position? named
Default Value False
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Authentication

Specifies the authentication level to be used with the WMI connection. Valid values are:

-1: Unchanged

0: Default

1: None (No authentication in performed.)

2: Connect (Authentication is performed only when the client establishes a relationship with the application.)

3: Call (Authentication is performed only at the beginning of each call when the application receives the request.)

4: Packet (Authentication is performed on all the data that is received from the client.)

5: PacketIntegrity (All the data that is transferred between the client and the application is authenticated and verified.)

6: PacketPrivacy (The properties of the other authentication levels are used, and all the data is encrypted.)

Aliases none
Required? false
Position? named
Default Value 0
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Authority

Specifies the authority to use to authenticate the WMI connection. You can specify standard NTLM or Kerberos authentication. To use NTLM, set the authority setting to ntlmdomain:, where identifies a valid NTLM domain name. To use Kerberos, specify kerberos:. You cannot include the authority setting when you connect to the local computer.

Aliases none
Required? false
Position? named
Default Value none
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Class

Specifies the WMI class that contains a static method to call.

Aliases none
Required? true
Position? 1
Default Value none
Accept Pipeline Input? false
Accept Wildcard Characters? false
-ComputerName

Runs the command on the specified computers. The default is the local computer.

Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more computers. To specify the local computer, type the computer name, a dot (.), or “localhost”.

This parameter does not rely on Windows PowerShell remoting. You can use the ComputerName parameter even if your computer is not configured to run remote commands.

Aliases Cn
Required? false
Position? named
Default Value Local computer
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Credential

Specifies a user account that has permission to perform this action. The default is the current user. Type a user name, such as “User01”, “Domain01\User01”, or User@Contoso.com. Or, enter a PSCredential object, such as an object that is returned by the Get-Credential cmdlet. When you type a user name, you will be prompted for a password.

Aliases none
Required? false
Position? named
Default Value Current user
Accept Pipeline Input? false
Accept Wildcard Characters? false
-EnableAllPrivileges

Enables all the privileges of the current user before the command makes the WMI call.

Aliases none
Required? false
Position? named
Default Value False
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Impersonation

Specifies the impersonation level to use. Valid values are:

0: Default (Reads the local registry for the default impersonation level, which is usually set to “3: Impersonate”.)

1: Anonymous (Hides the credentials of the caller.)

2: Identify (Allows objects to query the credentials of the caller.)

3: Impersonate (Allows objects to use the credentials of the caller.)

4: Delegate (Allows objects to permit other objects to use the credentials of the caller.)

Aliases none
Required? false
Position? named
Default Value 0
Accept Pipeline Input? false
Accept Wildcard Characters? false
-InputObject

Specifies a ManagementObject object to use as input. When this parameter is used, all other parameters except the Flag and Argument parameters are ignored.

Aliases none
Required? true
Position? named
Default Value none
Accept Pipeline Input? True (ByValue)
Accept Wildcard Characters? false
-Locale

Specifies the preferred locale for WMI objects. Specify the value of the Locale parameter as an array in the MS_ format in the preferred order.

Aliases none
Required? false
Position? named
Default Value none
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Name

Specifies the name of the method to be invoked. This parameter is mandatory and cannot be null or empty.

Aliases none
Required? true
Position? 2
Default Value none
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Namespace

When used with the Class parameter, this parameter specifies the WMI repository namespace where the referenced WMI class or object is located.

Aliases NS
Required? false
Position? named
Default Value none
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Path

Specifies the WMI object path of a WMI class, or specifies the WMI object path of an instance of a WMI class. The class or the instance that you specify must contain the method that is specified in the Name parameter.

Aliases none
Required? true
Position? named
Default Value none
Accept Pipeline Input? false
Accept Wildcard Characters? false
-ThrottleLimit

Allows the user to specify a throttling value for the number of WMI operations that can be executed simultaneously. This parameter is used together with the AsJob parameter. The throttle limit applies only to the current command, not to the session or to the computer.

Aliases none
Required? false
Position? named
Default Value none
Accept Pipeline Input? false
Accept Wildcard Characters? false
-Confirm

Prompts you for confirmation before running the cmdlet.

Required? false
Position? named
Default Value false
Accept Pipeline Input? false
Accept Wildcard Characters? false
-WhatIf

Shows what would happen if the cmdlet runs. The cmdlet is not run.

Required? false
Position? named
Default Value false
Accept Pipeline Input? false
Accept Wildcard Characters? false

This cmdlet supports the common parameters: -Verbose, -Debug, -ErrorAction, -ErrorVariable, -OutBuffer, and -OutVariable. For more information, see  about_CommonParameters (http://go.microsoft.com/fwlink/p/?LinkID=113216).

Inputs

The input type is the type of the objects that you can pipe to the cmdlet.

  • NoneThis cmdlet does not accept any input.
Outputs

The output type is the type of the objects that the cmdlet emits.

  • NoneThis cmdlet does not generate any output.
Examples
————————– EXAMPLE 1 ————————–

To invoke WMI in PowerShell 3.0 differs from alternate methods, and requires that object values are entered in a specific order. This command lists the required order of the objects.

————————– EXAMPLE 2 ————————–

The following two commands start an instance of Notepad by calling the Create method of the Win32_Process class.

Note: The ReturnValue property is populated with a 0, and the ProcessId property is populated with an integer (the next process ID number) if the command is completed.

————————– EXAMPLE 3 ————————–

This command renames a file. It uses the Path parameter to reference an instance of the CIM_DataFile class. Then, it applies the Rename method to that particular instance.

Note: The ReturnValue property is populated with a 0 if the command is completed.

9 thoughts on “Create DNS PTR record if A record exists

  • December 27, 2017 at 18:26
    Permalink

    Excellent script and exactly what I’m looking for. I ran the script in a lab with existing A records while intentionally omitting reverse zones for those records. I was expecting it to detect the missing reverse zones with output in line 48.
    Any ideas?
    Thanks!

    Reply
    • January 15, 2018 at 11:46
      Permalink

      Hello Chris,

      Sorry for the late answer. Can you tell me which OS you are running on ? I think I can rewrite the script because Windows 2012 offers new powershell possibilities with DNS server.

      Keep me informed

      Cheers,
      Nico

      Reply
  • April 14, 2018 at 17:39
    Permalink

    Hi Nico,

    I was working on something similar myself but ran into problems with the reverse zones and was searching for some alternate ways when I found this, it looks great. Did you ever rewrite it for 2012 powershell? The problem I am running into is the trailing dots. While I am confirming that PTR records exist for each A record and they match the hostname.domain.name, I also want to check that the trailing dot is there.

    # Get all A records in the specified DNS zone
    # Ping host
    # Check for matching reverse record

    $zone = « domain.name »
    # Initialize output array
    $inv = @()
    # Get all DNS A records from $zone except @ records
    $records = Get-DnsServerResourceRecord -ZoneName $zone -RRType « A » | Where {$_.HostName -notlike « @ »}

    # Loop through all the records to test them and collect status
    Foreach ($record in $records) {
    # PTR record should be FQDN plus the trailing dot.
    $fname = $record.hostname + « . » + $zone

    # Check to see if the record is static or dynamic
    If (!($record.Timestamp)) {
    $timestamp = « Static »
    }
    Else {
    $timestamp = $record.timestamp
    }

    # Test ping status
    Try {
    $ping = Test-Connection -ComputerName ($record.recorddata.IPv4Address.ToString()) -Count 1 -ErrorAction Stop
    If ($ping) {
    $pstatus = « Up »
    }
    }
    Catch {
    $pstatus = « Down »
    }

    # check the PTR record
    Try {
    #$ptr = Resolve-DNSName -Name ($record.recorddata.IPv4Address.ToString()) -Type PTR -ErrorAction Stop
    $rzone = ($record.recorddata.IPv4Address.ToString()).split(« . »)
    $rzonename = « $rzone[1].$rzone[0].in.addr.arpa »
    $filter = «  » »ContainerName=’ » + $rzonename + »‘ » » »

    # « ContainerName=’0.10.in-addr.arpa’ »
    $ptr = Get-WmiObject -ComputerName dc1.domain.name -Namespace root\MicrosoftDNS -Class « MicrosoftDNS_PTRType » -Filter « ContainerName=’0.10.in-addr.arpa’ » |
    Where {$_.recorddata -like $fname}

    If ($fname -eq $ptr.namehost) {
    $rmatch = « PTR OK »
    $rstatus = « OK »
    }
    Else {
    $rmatch = $ptr.namehost
    $rstatus = « Not matched »
    }
    }
    Catch {
    $rmatch = « No PTR »
    $rstatus = « Error »
    }

    # Create entry and add to output array
    $entry = $record.hostname + »; »+ $timestamp + »; »+ $pstatus + »; »+ $rmatch + »; »+ $rstatus
    $inv += $entry
    # End of records loop
    }
    $inv | Out-file c:\temp\dnscheck.csv

    The Resolve-DNSName function won’t show the trailing dots, but the gwmi query does with the recorddata so I am trying it.
    The problem I am running into is the filter for the gwmi. I want to wildcard it. I worked this out:
    $filter = «  » »ContainerName=’ » + $rzonename + »‘ » » »
    to give me this: « ContainerName=’0.10.in.addr.arpa’ » that matches the working name below.

    This command line works:
    $ptr = Get-WmiObject -ComputerName ‘dns1’ -Namespace root\MicrosoftDNS -Class « MicrosoftDNS_PTRType » -Filter « ContainerName=’0.10.in-addr.arpa’ »

    While this one doesn’t:
    $ptr = Get-WmiObject -ComputerName ‘dns1’ -Namespace root\MicrosoftDNS -Class « MicrosoftDNS_PTRType » -Filter $filter

    Very painful. I have been at this one for a while and can’t seem to find a key. I think I might be able to integrate your method of trying the reverse zones one-by-one, but would be difficult. Do you have any other ideas on what I could try there? Thanks!

    Reply
  • April 14, 2018 at 17:51
    Permalink

    Ahh, never mind. I see now where you are constructing the hostname with the trailing dot.

    Reply
  • April 15, 2018 at 20:53
    Permalink

    Hi Nico,

    This ended up being a very useful project as I learned how to properly use hash tables. I was rewriting my script and incorporating ideas from yours and things seemed to be going well until I realized I had a logic flaw which exists in your script as well. What if a given host has a PTR record for another IP than the IP it currently has? I wasn’t catching that case, I was just checking for existence of a PTR record like you are above from an array list and not that the PTR entry actually matched the hostname with the trailing dot. I realized I had to use a hash table instead of an array but wasn’t exactly sure how to go about it. I put in some time and was able to figure it out thankfully. I went back to your script and realized it would easier to bolt on a superstructure there instead of continuing on the approach I was trying. I also needed a static/stale check so once I figured out the hash tables I was able to add that check and do some different color coding. Thanks for posting the original script, it helped me get this part of the DNS cleanup project done.

    Here is the full script with updates. The output is a bit different than yours, I needed both visual and a csv style output.

    begin {}
    process {
    # This script uses WMI to check DNS A records for a given zone and looks for several things.
    # It pings the host for Online status. It checks the A records for staleness.
    # It checks the PTR records for staleness and presence.
    # The output is color coded for the results.
    # The host being online can be White, Green, or Yellow.
    # If the hostname and Online are in White, it has a Static A record.
    # If the hostname and Online are in Green, it has a recently updated A record.
    # If the hostname and Online are in Yellow, it has a stale A record.
    # If the host is offline, it will be in Red.
    # Static DNS records are White, Updated DNS records are Green, Stale DNS records are Yellow, No DNS records = Red
    # If the reverse DNS zone exists it will be in Green.

    # Set some variables up
    $dnsServer = “dc.domain.local”
    $dnsDomainName = “domain.local”
    $staledate = (Get-Date).AddDays(-90)
    $ProgressCounter = 0

    # Define static date for later comparison
    $staticdate = “1/1/1601 12:00:00 AM”

    # Function to create the PTR records, invoked below
    function CreatePTR($dnsServer,$reverse_zone,$reverse_ip,$hostname) {
    Invoke-WmiMethod -Name CreateInstanceFromPropertyData -Class MicrosoftDNS_PTRType
    -Namespace root\MicrosoftDNS -ArgumentList “$reverse_zone”,”$dnsServer”,”$reverse_ip”,”$hostname”

    -ComputerName $dnsServer
    }

    # Get all PTR records and turn it into a hash table for lookup name verifications
    $record_R_list = Gwmi -Namespace “root\MicrosoftDNS” -Class MicrosoftDNS_PTRtype -ComputerName $dnsServer | Select Ownername,RecordData
    # Initialize empty hash table
    $record_R_hash = @{}
    # Loop through all records and add them as key entries to the hash table
    Foreach ($record in $record_R_list) {
    $record_R_hash.($record.ownername) = $record.recorddata
    }

    # Get all PTR records with timestamps and make new hashtable named $record_R_date for stale date check
    # datetime expression converts WMI time to readable time
    $record_R_datelist = Gwmi -Namespace “root\MicrosoftDNS” -Class MicrosoftDNS_PTRtype -ComputerName $dnsServer |
    Select OwnerName,@{n=”Timestamp”;e={([datetime]”1.1.1601″).AddHours($_.Timestamp)}}
    # Initialize empty hash table
    $record_R_date = @{}
    # Loop through all records and add them as key entries to the hash table
    Foreach ($record in $record_R_datelist) {
    $record_R_date.($record.ownername) = $record.Timestamp
    }

    # Get all A records with datetime expression to convert WMI time to readable time. Filter out msdcs records
    $record_A_list = Gwmi -Namespace “root\MicrosoftDNS” -Class MicrosoftDNS_Atype -ComputerName $dnsServer |
    Select ownername,IPaddress,@{n=”Timestamp”;e={([datetime]”1.1.1601″).AddHours($_.Timestamp)}} |
    Where {($_.ownername -like “*.$dnsDomainName”) -and ($_.ownername -notlike “*msdcs*”) -and ($_.ownername -notlike “*dnszones*”)}

    # Get all reverse zones
    $reverse_zone_list = Gwmi MicrosoftDNS_Zone -Namespace ‘root\MicrosoftDNS’ -filter “reverse=true” -computer $dnsServer | Select Name -ExpandProperty Name

    # Write header
    Write-Host -NoNewLine -Foreground White “Static = White ”
    Write-Host -NoNewLine -Foreground Green “Updated = Green ”
    Write-Host -NoNewLine -Foreground Yellow “Stale = Yellow ”
    Write-Host -Foreground Red “Not Present = Red”
    Write-Host -Foreground White “Computer Status IP ReverseZone”

    # Loop through all A records and verify their ping status and check for PTR records and reverse zone presence
    Foreach ($a_record in $record_A_list) {
    # Build hostname with trailing dot.
    $hostname = $a_record.ownername+”.”

    # Get the IP address from the A record and convert to reverse format
    $ipaddress = $a_record.IPaddress
    $arr = $ipaddress.split(“.”)
    [array]::Reverse($arr)
    $reverse_ip = ($arr -join ‘.’) + “.in-addr.arpa”

    # Check ping status and stale check and write first part of line with no new line
    $PingStatus = Gwmi Win32_PingStatus -Filter “Address = ‘$hostname’” | Select-Object StatusCode
    If ($PingStatus.StatusCode -eq 0) {
    If ($a_record.Timestamp -eq $staticdate) {
    Write-Host -NoNewLine -Foreground White $hostname “Online ”
    }
    ElseIf ($a_record.Timestamp -ge $staledate) {
    Write-Host -NoNewLine -Foreground Green $hostname “Online ”
    }
    Else {
    Write-Host -NoNewLine -Foreground Yellow $hostname “Online ”
    }
    }
    Else {
    Write-Host -NoNewline -Foreground Red $hostname “Offline ”
    }

    # Check to see if the host is in the PTR list or not and if it is stale and write the next part of the line
    If ($record_R_hash[$reverse_ip] -eq $hostname) {
    If ($record_R_date[$reverse_ip] -eq $staticdate) {
    Write-Host -NoNewline -Foreground White $ipaddress “”
    }
    Elseif ($record_R_date[$reverse_ip] -ge $staledate) {
    Write-Host -NoNewline -Foreground Green $ipaddress “”
    }
    Else {
    Write-Host -NoNewline -Foreground Yellow $ipaddress “”
    }
    }
    Else {
    Write-Host -NoNewline -Foreground Red $ipaddress “”
    }

    #detect the correct dns reverse lookup zone
    $arr_rvr = $reverse_ip.Split(“.”)
    $arr_rvr1 = $arr_rvr[1] + “.” + $arr_rvr[2] + “.” + $arr_rvr[3] + “.in-addr.arpa”
    $arr_rvr2 = $arr_rvr[2] + “.” + $arr_rvr[3] + “.in-addr.arpa”
    $arr_rvr3 = $arr_rvr[3] + “.in-addr.arpa”
    If ($reverse_zone_list -contains $arr_rvr1) {
    Write-Host -Foreground Green $arr_rvr1
    #CreatePTR $dnsServer $arr_rvr1 $reverse_ip $hostname
    }
    ElseIf ($reverse_zone_list -contains $arr_rvr2) {
    Write-Host -Foreground Green $arr_rvr2
    #CreatePTR $dnsServer $arr_rvr2 $reverse_ip $hostname
    }
    ElseIf ($reverse_zone_list -contains $arr_rvr3) {
    Write-Host -Foreground Green $arr_rvr3
    #CreatePTR $dnsServer $arr_rvr3 $reverse_ip $hostname
    }
    Else {
    Write-Host -Foreground Red “Reverse lookup zone does not exist.”
    }

    # Increment and write out the progress bar
    $ProgressCounter++
    Write-Progress -activity “Working on $hostname” -status “Please wait …” -PercentComplete (($ProgressCounter / ($record_A_list.length + 1)) * 100)

    # End of A record loop
    }
    # End of process block
    }
    end {}

    Reply
  • May 29, 2018 at 10:18
    Permalink

    Excellent job!

    However, instead of construction
    $arr_rvr = $reverse_ip.Split(“.”)
    $arr_rvr1 = $arr_rvr[1] + “.” + $arr_rvr[2] + “.” + $arr_rvr[3] + “.in-addr.arpa”
    $arr_rvr2 = $arr_rvr[2] + “.” + $arr_rvr[3] + “.in-addr.arpa”
    $arr_rvr3 = $arr_rvr[3] + “.in-addr.arpa”

    Can be used regular expression:
    $correctReverseZone = $reverse_zone_list -match “^($($arr_rvr.Split(‘.’)[2]))?\.?($($arr_rvr.Split(‘.’)[1]))?\.?($($arr_rvr.Split(‘.’)[0]))\.in-addr\.arpa” | sort Length -Descending | select -First 1

    Reply
    • May 29, 2018 at 11:45
      Permalink

      Hi Vlam. Excellent. Thank you for sharing. I will test that and update the script asap.

      Reply
      • May 29, 2018 at 13:26
        Permalink

        Hi Nicolas!

        I’m sorry, but I used wrong variable
        The correct regex looks like
        -match “^($third_octet)?\.?($second_octet)?\.?($first_octet)\.in-addr\.arpa”
        so
        $correctReverseZone = $reverse_zone_list -match “^($($ipaddress.Split(‘.’)[2]))?\.?($($ipaddress.Split(‘.’)[1]))?\.?($($ipaddress.Split(‘.’)[0]))\.in-addr\.arpa” | sort Length -Descending | select -First 1

        Recently I worked on similar task, so I wrote :
        #region reverse IP Address
        $IPAddressAsArray = $hostAddr.Split(‘.’) # Convert IP address to array using “.” as delimiter
        [array]::Reverse($IPAddressAsArray)
        $reversedIP = $IPAddressAsArray -join ‘.’ # The name of the PTR record is made of reversed IP Address, so the variable is used to construst the PTR resource record Name
        #endregion reverse IP Address

        #[string]$theZone = $reverseZones -match “^($octet3)?\.?($octet2)?\.?($octet1)\.in-addr\.arpa” | sort Length -Descending | select -First 1
        [string]$theZone = $reverseZones -match “^($($hostAddr.Split(‘.’)[2]))?\.?($($hostAddr.Split(‘.’)[1]))?\.?($($hostAddr.Split(‘.’)[0]))\.in-addr\.arpa” | sort Length -Descending | select -First 1

        #region Create / Change PTR-record
        If( !([string]::IsNullOrWhiteSpace($theZone)) )#if the zone exists
        {
        $PtrDomainName = $FQDN + ‘.’ # for PTR records “.” is added to the domain name
        $ptrRRName = $reversedIP -replace $(‘.’ + $($theZone -replace ‘.in-addr.arpa’)) # PTR Resource record name. Zone Name subtracted out of reversed IP. “.in-addr.arpa” subtracted out of zone name
        …..

        I wish I came across your article earlier

        Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Social Media Auto Publish Powered By : XYZScripts.com
%d bloggers like this: