3

I run BIND9 DNS servers and allow Dynamic DNS updates from my customers by using a TSIG key.

One of my customers uses only a Windows environment, and therefore PowerShell to run scripts. He wants to use PowerShell to send dynamic updates to my servers.

Doing this from a Linux shell for testing is easy: use nsupdate.

from: https://www.freeipa.org/page/Howto/DNS_updates_and_zone_transfers_with_TSIG

Client

For nsupdate from bind-utils package you have to either use option -y algorithm:keyname:keyvalue or -k keyfilename option. E.g.

$ nsupdate -y hmac-sha512:keyname:keyvalue

or

$ nsupdate -k Kkeyname.+165+0316.private

then do your update:

from https://linux.die.net/man/8/nsupdate:

# nsupdate
> update delete oldhost.example.com A
> update add newhost.example.com 86400 A 172.16.1.1
> send

To do an update from Powershell without TSIG is ... kinda easy... I think?: use a cmdlet (for example) Add-DnsServerResourceRecordA

Add-DnsServerResourceRecordA -Name "host23" -ZoneName "contoso.com" - AllowUpdateAny -IPv4Address "172.18.99.23" -TimeToLive 01:00:00

After scouring the documentation, I don't see any references to Transaction Signatures or somehow using a TSIG key.

How do I send a dynamic update using a TISG key to a BIND9 server from Powershell?

This is frustratingly hard to find an example of. Most examples I can find are using PowerShell to send updates via an API which then (probably) does some kind of deploy or dynamic update inside a black box. I want to just build a DDNS update and send it off using PowerShell.

Watki02
  • 637

3 Answers3

2

They will have to download nsupdate from BIND (https://www.isc.org/downloads/). It is possible to call nsupdate from a PowerShell host.

Jim B
  • 24,276
-1

Solution for those Who don't have Static IP Address and Need to Update IP Dynamically for DNS zone and reverse DNS PTR records too

            $Server = "your server"; $Hostname = "mail"; $Zonename = "your zone"; 
            $MZone = $Hostname + "." + $Zonename
            <# No need to edit below unless you have to change some internal component #>
        $oldobj = get-dnsserverresourcerecord -ComputerName $Server -name $Hostname -zonename $zonename -rrtype &quot;A&quot;
        $newobj = get-dnsserverresourcerecord -ComputerName $Server -name $Hostname -zonename $zonename -rrtype &quot;A&quot;
        $ip =  (Invoke-WebRequest ifconfig.me/ip).Content.Trim()   
        $oip = $oldobj.recorddata.Ipv4address.IpAddressToString
        $oipSplit = $oip.Split(&quot;.&quot;); $oipr = $oipSplit[2] + &quot;.&quot; + $oipSplit[1] + &quot;.&quot; + $oipSplit[0] + &quot;.in-addr.arpa&quot;
        $ipSplit = $ip.Split(&quot;.&quot;); $ipr = $ipSplit[2] + &quot;.&quot; + $ipSplit[1] + &quot;.&quot; + $ipSplit[0] + &quot;.in-addr.arpa&quot;
        $newobj.recorddata.ipv4address=[System.Net.IPAddress]::parse($ip)
        if ($oip -ne $ip) {
            Set-dnsserverresourcerecord -ComputerName $Server -newinputobject $newobj -oldinputobject $oldobj -zonename $zonename -passthru
            echo &quot;updated A record&quot;
        }
        $oiprZ = Resolve-DnsName -Name $oipr -Server $Server; $oiprR = 0;
        if ($oiprZ.count -gt 0) {
            $oiprR = get-dnsserverresourcerecord -ComputerName $Server -ZoneName $oipr -rrtype &quot;PTR&quot;  -Name $oipSplit[3] | Select-Object HostName, @{Name='RecordData';Expression={$MZone}} 
            if ($oiprR -ne 0 -And $oiprR -ne $null -and -not ($oiprR[0].HostName -eq $ipSplit[3] -and $oiprR[0].RecordData -eq $MZone -and  $oipr -eq $ipr )  ){
                Remove-DnsServerResourceRecord -ComputerName $Server -ZoneName $oipr -rrtype &quot;PTR&quot; -Name $oipSplit[3] -RecordData $MZone -Force
                echo &quot;removing existing ptr record&quot;
                echo $oiprR
                echo &quot;removed existing ptr record&quot;
            }
            $oiprR =get-dnsserverresourcerecord -ComputerName $Server -ZoneName $oipr -rrtype &quot;PTR&quot;
            if (((($oiprR) -eq $null) -or (($oiprR).Count  -eq 0)) -And $oipr -ne $ipr )  {
                Remove-DnsServerZone  -ComputerName $Server $oipr -PassThru -Verbose -Force
                echo &quot;Removing RDNS Zone&quot;
                echo $oiprZ
                echo &quot;Removing RDNS Zone&quot;

            }
        }


        $ipNID = $ipSplit[0] + &quot;.&quot; + $ipSplit[1] + &quot;.&quot; + $ipSplit[2] + &quot;.0/24&quot;
        $iprZ = Resolve-DnsName -Name $ipr -Server $Server; $iprR = 0;
        if ($iprZ.count -gt 0) {
            $iprR = get-dnsserverresourcerecord -ComputerName $Server -ZoneName $ipr -rrtype &quot;PTR&quot;  -Name $ipSplit[3] | Select-Object HostName, @{Name='RecordData';Expression={$MZone}} 
            if ($iprR -eq $null ){
            Add-DnsServerResourceRecordPtr  -ComputerName $Server  -Name $ipSplit[3] -ZoneName $ipr -AllowUpdateAny -TimeToLive 01:00:00 -AgeRecord -PtrDomainName $MZone
                echo &quot;adding ptr record&quot;
                echo $iprR
                echo &quot;added ptr record&quot;
            }
            else
            {

            }
        }
        if ($iprZ.count -eq 0) {
            Add-DnsServerPrimaryZone  -ComputerName $Server -DynamicUpdate Secure -NetworkId $ipNID -ReplicationScope Domain
            Add-DnsServerResourceRecordPtr  -ComputerName $Server  -Name $ipSplit[3] -ZoneName $ipr -AllowUpdateAny -TimeToLive 01:00:00 -AgeRecord -PtrDomainName $MZone
        }

-1

Here is a script that will create and submit DDNS requests if a tsig file is provided. Ensure NTFS permissions are set to prevent unauthorized users (including other admins) from accessing this file.

This does assume that you have installed nsupdate.exe and the associated dll's in C:\windows\system32 but it can be modified for other paths.

I welcome any pull requests. https://github.com/ACiDGRiM/UsefulScripts/blob/master/Update-DNS.ps1

Param (
    [String]$KeyPath = "C:\Windows\System32\drivers\etc\windows-update-client.txt",
    [String]$NSScriptPath = "$env:Temp\nsupdate.txt",
    [String]$NSUpdatePath = "$env:SystemRoot\System32"
)

begin {
    #Gather status of system IP Addresses, DNS Servers, and domains
    $IPAddresses = Get-NetIPAddress | Where-Object -FilterScript { ($_.InterfaceAlias -like "Ethernet*" -or $_.InterfaceAlias -like "Wi-Fi*") -and $_.IPAddress -notlike "fe*"}
    $DNSServers = Get-DnsClientServerAddress | Where-Object -FilterScript { $_.InterfaceAlias -like "Ethernet*" -or $_.InterfaceAlias -like "Wi-Fi*"}
    $DNSClient = Get-DnsClient | Where-Object -FilterScript { $_.InterfaceAlias -like "Ethernet*" -or $_.InterfaceAlias -like "Wi-Fi*"}
}

process {
    [array]$RequestOutput = @()
    #Parse network status into simplified objects
    foreach ( $if in $IPAddresses ) {
        $requesthash = @{
            IPAddress = @{Address = $if.IPAddress;AddressFamily = $if.AddressFamily}
            Zone = $DNSClient | Where-Object -FilterScript { $_.InterfaceAlias -eq $if.InterfaceAlias } | Select-Object -ExpandProperty "ConnectionSpecificSuffix" -First 1
            Servers = $DnsServers | Where-Object -FilterScript { $_.InterfaceAlias -eq $if.InterfaceAlias } | Select-Object -ExpandProperty "ServerAddresses"
        }
        $RequestObj = New-Object -TypeName psobject -Property $requesthash
        $RequestOutput += $RequestObj 

    }

    #Condense zones from multiple interfaces
    [array]$UniqueZones = ($RequestOutput.Zone|Sort-Object -Unique)
    #Combine IPv6 and IPv4 addresses into a single object property for each zone
    [array]$CombinedOutput = @()
    for ($i=0;$i -lt $UniqueZones.count;$i++) {
        $Combinedhash = @{
            Addresses = $RequestOutput | Where-Object -FilterScript {$_.Zone -eq $UniqueZones[$i]} | Select-Object -ExpandProperty "IPAddress"
            Servers = $RequestOutput | Where-Object -FilterScript {$_.Zone -eq $UniqueZones[$i]} | Select-Object -ExpandProperty "Servers" | Sort-Object -Unique
            Zone = $UniqueZones[$i]
        }
        $CombinedObj = New-Object -TypeName psobject -Property $Combinedhash
        $CombinedOutput += $CombinedObj 
    }

    foreach ( $o in $CombinedOutput ) {
        foreach ( $s in $o.Servers ) {
            $CurrentRecords = Resolve-DnsName $env:COMPUTERNAME`.$($o.Zone) -Server $s -Type "A_AAAA" -DnsOnly -DnssecOK -QuickTimeout -ErrorAction "SilentlyContinue" | Select-Object -ExpandProperty "IPAddress" -ErrorAction "SilentlyContinue"
            if ( $CurrentRecords ) {
                $CurrentState = Compare-Object $IPAddresses.IPAddress $CurrentRecords -ErrorAction "SilentlyContinue"
            } else {
                $CurrentState = $true
            }

            if ( $CurrentState ) {
                $script += "server $s
"
                foreach ( $a in $o.Addresses ) {
                    if ( $a.AddressFamily -eq "IPv4" ) {
                        $PTR = $a.Address -replace '^(\d+)\.(\d+)\.\d+\.(\d+)$','$3.$2.$1.in-addr.arpa.'
                    } else {
                        $PTR = (([char[]][BitConverter]::ToString(([IPAddress]$a.Address).GetAddressBytes())-ne'-')[31..0]-join".")+'.ip6.arpa.'
                    }
                    $script += "update delete $env:COMPUTERNAME.$($o.Zone). $(if($a.AddressFamily -eq "IPv4"){"A"}else{"AAAA"})

update add $env:COMPUTERNAME.$($o.Zone). 60 $(if($a.AddressFamily -eq "IPv4"){"A"}else{"AAAA"}) $($a.Address)

update delete $PTR PTR

update add $PTR 60 PTR $env:COMPUTERNAME.$($o.Zone).


"
                }
            }

        }
    }
}

end {
    $script | Out-File -FilePath $NSScriptPath -Encoding "ascii" -Force
    Start-Process -FilePath (Join-Path -Path $NSUpdatePath -ChildPath "nsupdate.exe") -ArgumentList "-d -k `"$KeyPath`" `"$NSScriptPath`"" -Wait -NoNewWindow -RedirectStandardError "$env:TEMP\nsstderr" -RedirectStandardOutput "$env:TEMP\nsstdout" -WorkingDirectory $NSUpdatePath | Out-Null

}
ACiD GRiM
  • 123
  • 1
  • 8