Недавно для настройки представлений на серверах 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 |
На этом задача решена. Возможно будет логично засунуть этот кусок кода в цикл, если нужно перебирать записи сразу нескольких зон. Это вы уже сделайте сами при необходимости. Успехов!