 Недавно для настройки представлений на серверах Bind мне потребовалась выгрузка DNS-записей с серверов Windows. Несмотря на очевидную простоту задачи на деле все оказалось далеко не так просто и быстро.
Недавно для настройки представлений на серверах Bind мне потребовалась выгрузка DNS-записей с серверов Windows. Несмотря на очевидную простоту задачи на деле все оказалось далеко не так просто и быстро.
Пока разбирался каким образом решать задачу и собрал все подводные камни, натолкнулся на множество интересных статей и ресурсов. Обязательно с вами поделюсь.
Если вам интересны статьи о Powershell, рекомендую обратиться к тегу PowerShell scripting на моем блоге
Выгрузка DNS-записей
Кому сразу нужен код/команды – листайте до пункта Скрипт, а пока разберем саму задачу и теорию.
Задача
Есть сервер Windows с ролью DNS. Есть сервер Bind на Linux с настроенными для различных клиентов представлениями. Нужно с сервера Windows выгрузить (крайне желательно в формате, который знает Bind 1) все записи определенной зоны и засунуть в Bind. Вручную копировать не вариант, записей сотни.
Теория
Выгрузить записи нужной вам зоны (или всех зон враз, почему бы и нет) роли Windows Server DNS можно разными путями. Вот основные.
1. Например записи неинтегрированных в AD зон можно просто скопировать из соответствующих файлов в директории C:\Windows\System32\dns. Для AD-integrated зон такой вариант не подойдет.
2. Вполне можно воспользоваться старой доброй утилитой dnscmd, например:
| 1 | dnscmd dc01.dns_zone.local /enumrecords dns_zone.local '@' | % { $_ -replace ' ',"`t" } | 
Где dc01.dns_zone.local и dns_zone.local имена контроллера домена и нужной вам зоны соответственно. Тем не менее, эта утилита уже достаточно стара и использовать её – не самый лучший вариант.
3. Самый разумный вариант на текущий момент – использовать Powershell для выгрузки записей. Но этот вариант далеко не самый простой. Почему, рассмотрим ниже.
Скрипт
Для получения всех записей определенной зоны подойдет командлет Get-DnsServerResourceRecord 2. Пример его использования:
| 1 | Get-DnsServerZone -Name 'dns_zone.local' | Get-DnsServerResourceRecord | 
Получите подобный вывод (количество записей уменьшено для наглядности):
| 1 2 3 4 | HostName                  RecordType Type       Timestamp            TimeToLive      RecordData                                         --------                  ---------- ----       ---------            ----------      ----------                                         _ldap._tcp.SPB._sites.... SRV        33         26.04.2019 23:00:00  00:10:00        [0][100][389][DC01.dns_zone.local.]                      EXCH01                    A          1          25.04.2019 14:00:00  00:20:00        192.168.0.15 | 
Многоточие в первой строчке явно намекает, что это сокращение. Этот вариант не подходит, попробуем ограничить выводимые столбцы и указать сортировку:
| 1 | Get-DnsServerZone -Name 'dns_zone.local' | Get-DnsServerResourceRecord | Format-Table HostName,RecordType,RecordData -AutoSize | 
Уже лучше, но что стало со столбцом RecordData:
| 1 2 3 4 | HostName                             RecordType RecordData                   --------                             ---------- ----------                   _ldap._tcp.SPB._sites.DomainDnsZones SRV        DnsServerResourceRecordSrv   EXCH01                               A          DnsServerResourceRecordA | 
Подобный вывод говорит о том, что RecordData – отдельный объект со своими свойствами. Хорошо, проверим что он из себя представляет командой:
| 1 | (Get-DnsServerZone -Name 'dns_zone.local' | Get-DnsServerResourceRecord).RecordData | Format-List | 
Вывод:
| 1 2 3 4 5 6 7 8 | DomainName     : DC01.dns_zone.local. Port           : 389 Priority       : 0 Weight         : 100 PSComputerName :  IPv4Address    : 192.168.0.15 PSComputerName : | 
Теперь становится понятно, что нам нужно отдельно обрабатывать 3 каждую запись в зависимости от её типа 4. Для этого можно воспользоваться хэш-таблицей 5 6 и командлетом Select-Object 7:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | Get-DnsServerZone -Name 'dns_zone.local' | Get-DnsServerResourceRecord | Select-Object -Property `     HostName, `     RecordType, `     @{Label="RecordData"; expression={         $RR = $_.RecordData             switch ($_.RecordType)                 {                     "A" { $RR.IPv4Address }                     "AAAA" { $RR.IPv6Address }                     "CNAME" {  $RR.HostNameAlias }                     "SRV"{                         "$($RR.Priority) "+                         " $($RR.Weight) "+                         " $($RR.Port) "+                         " $($RR.DomainName)"                     }                     "PTR" { $RR.PtrDomainName }                      "MX" {                         "$($RR.Preference) "+                         " $($RR.MailExchange)"                     }                     "TXT" { $RR.DescriptiveText }                 }       }     } | 
Вот что получилось:
| 1 2 3 4 | HostName                             RecordType RecordData                                                                --------                             ---------- ----------                                                                _ldap._tcp.SPB._sites.DomainDnsZones SRV        0  100  389  DC01.dns_zone.local.                                               EXCH01                               A          192.168.0.15 | 
В примере выше я сделал сопоставление только для семи типов записей (потому что для моей задачи больше и не надо), но на деле их будет больше. Мне также было нужно, чтобы каждая запись была определена как fqdn, поэтому к полю HostName нужно добавить суффикс в виде домена. Не лишним также будет выгружать все записи в CSV. Вот как изменится скрипт:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | $ZoneName='dns_zone.local' Get-DnsServerZone -Name $ZoneName | Get-DnsServerResourceRecord | Select-Object -Property `     @{Label="HostName";expression={ ( $_.HostName ) + '.' + $ZoneName }}, `     RecordType, `     @{Label="RecordData"; expression={         $RR = $_.RecordData             switch ($_.RecordType)                 {                     "A" { $RR.IPv4Address }                     "AAAA" { $RR.IPv6Address }                     "CNAME" {  $RR.HostNameAlias }                     "SRV"{                         "$($RR.Priority) "+                         " $($RR.Weight) "+                         " $($RR.Port) "+                         " $($RR.DomainName)"                     }                     "PTR" { $RR.PtrDomainName }                      "MX" {                         "$($RR.Preference) "+                         " $($RR.MailExchange)"                     }                     "TXT" { $RR.DescriptiveText }                 }       }     } | Export-Csv -Path C:\$ZoneName-output.csv | 
На этом задача решена. Возможно будет логично засунуть этот кусок кода в цикл, если нужно перебирать записи сразу нескольких зон. Это вы уже сделайте сами при необходимости. Успехов!





Как импортировать данные из csv обратно в зону на этом же сервере?
Добрый день. Единственный вариант – самостоятельно писать скрипт, который бы парсил ваш CSV и потом обращался к днс-серверу для добавления записей