{"id":16721,"date":"2010-10-24T00:01:00","date_gmt":"2010-10-24T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2010\/10\/24\/weekend-scripter-use-a-free-powershell-module-to-ease-dns-administration\/"},"modified":"2010-10-24T00:01:00","modified_gmt":"2010-10-24T00:01:00","slug":"weekend-scripter-use-a-free-powershell-module-to-ease-dns-administration","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/weekend-scripter-use-a-free-powershell-module-to-ease-dns-administration\/","title":{"rendered":"Weekend Scripter: Use a Free PowerShell Module to Ease DNS Administration"},"content":{"rendered":"<p>&nbsp;<\/p>\n<p><b>Summary:<\/b> Learn how to use a free Windows PowerShell module to ease administration of Windows DNS.<\/p>\n<p>&nbsp;<\/p>\n<p>Microsoft Scripting Guy Ed Wilson here. I have been talking to Chris Dent for several months now, and I finally convinced him to write a guest blog for me. <\/p>\n<p>Guest blogger, Chris Dent, is with us today. Chris works as a systems and network engineer for a software developer in London. Chris is a PowerShell fanatic and a network and directory service specialist. Chris can often be found on the <a href=\"http:\/\/powershellgroup.org\/virtual\">Virtual PowerShell Group<\/a> or Experts-Exchange. Chris is the author of <a href=\"http:\/\/code.msdn.microsoft.com\/dnsshell\">DnsShell<\/a>. <\/p>\n<p>&nbsp;<\/p>\n<h2>What is DnsShell?<\/h2>\n<p>The majority of the cmdlets in DnsShell are wrappers around the WMI interface. The WMI interface tends to be fairly difficult to work with, or at least more difficult than it needs to be. For the most part this is due to the infamous Generic Error it returns whenever something goes wrong.<\/p>\n<p>In addition to the WMI wrappers, DnsShell contains an interface for working with DNS via LDAP with decoders for the <b>dnsProperty<\/b> and <b>dnsRecord<\/b> attributes. <\/p>\n<p>The final cmdlet, <b>Get-Dns<\/b>, is a DNS resolver, designed to exceed the capabilities of <b>nslookup<\/b> and to be comparable with Dig.<\/p>\n<p>This post aims to explore some of the capabilities for DnsShell, based on the tasks I use it for on a regular basis.<\/p>\n<p>&nbsp;<\/p>\n<h2>Basic tasks<\/h2>\n<h3>Listing zones<\/h3>\n<p><b>Get-DnsZone<\/b> can be used to return information about zones configured on a server. This cmdlet uses WMI to grab details of the zone. The parameters used with this cmdlet are used to build a WQL filter.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># All zones<br \/>Get-DnsZone<br \/># Primary zones<br \/>Get-DnsZone -ZoneType Primary | Format-List<\/span><\/pre>\n<\/blockquote>\n<p>&nbsp;<b>Zone<\/b> information can be returned from <b>Active Directory<\/b> using <b>Get-ADDnsZone<\/b>. By default, <b>Get-ADDnsZone<\/b> targets the <b>DomainDnsZones<\/b> application partition.<\/p>\n<p>&nbsp;The information returned by this cmdlet differs slightly from <b>Get-DnsRecord<\/b>, only showing detail stored in the <b>dnsProperty<\/b> attribute.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Returning all zones in DomainDnsZoness (for the current domain)<br \/>Get-ADDnsZone<br \/># Returning all zones from all partitions<br \/>Get-ADDnsPartition | Get-ADDnsZone<\/span><\/pre>\n<\/blockquote>\n<h3>&nbsp;<\/h3>\n<h3>Listing records<\/h3>\n<p>The following examples demonstrate how <b>Get-DnsRecord<\/b> can be used to pick up records configured on a server.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Listing all records on the current server<br \/>Get-DnsRecord<br \/># List A records in domain.example only<br \/>Get-DnsRecord -RecordType A -Zone domain.example<br \/># List all static records on the server<br \/>Get-DnsRecord -Filter \"TimeStamp=0\"<br \/># Name is a regular expression and can be used for simple or complex filters<br \/>Get-DnsRecord -Name '_tcp' -RecordType SRV<\/span><\/pre>\n<\/blockquote>\n<p><b>Get-ADDnsRecord<\/b> may also be used to retrieve record information. By its nature <b>Get-ADDnsRecord<\/b> is limited to querying records stored in Active Directory-integrated zones.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># All records in the DomainDnsZones partition<br \/>Get-ADDnsRecord<br \/># All Service (SRV) records in all partitions<br \/>Get-ADDnsPartition | Get-ADDnsRecord | Where-Object { $_.RecordType -eq \"SRV\" }<br \/># Name supports wildcards (used to build an LDAP filter)<br \/>Get-ADDnsRecord -Name \"_gc*\"<br \/># List all records in a specific zone<br \/>Get-ADDnsZone domain.example | Get-ADDnsRecord<\/span><\/pre>\n<\/blockquote>\n<p>Unlike <b>Get-DnsRecord<\/b>, which offers a parameter to filter on <b>RecordType<\/b>, <b>Get-ADDnsRecord<\/b> is reliant on <b>Where-Object<\/b>. This is used because there is no way to construct an <b>LDAP Filter<\/b> to limit a search to specific record types as the record type is encoded as part of the <b>dnsRecord<\/b> attribute. Wildcards or partial matches are not permissible for <b>DnsRecord<\/b> as it is a Binary Large Object (BLOB).<\/p>\n<p>The <b>RecordType<\/b> field used above is defined in an Enumeration. The possible values can be seen with:<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\">[Enum]::GetNames([DnsShell.RecordType])<\/span><\/pre>\n<\/blockquote>\n<p>Using the following filter for Where-Object returns the same results, it is equivalent to the filter above:<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Get all records from AD (DomainDnsZones). Filter to Service Records<br \/>Get-ADDnsRecord | Where-Object { $_.RecordType -eq [DnsShell.RecordType]::SRV }<\/span><\/pre>\n<\/blockquote>\n<h3>&nbsp;<\/h3>\n<h3>Returning server configuration<\/h3>\n<p>Returning a DNS servers configuration is a simple task. The <b>Get-DnsServer<\/b> cmdlets returns each of the configuration options available to MS DNS server.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Ask for DNS Server settings from $ServerName<br \/>Get-DnsServer $ServerName<\/span><\/pre>\n<\/blockquote>\n<h3>&nbsp;<\/h3>\n<h3>Creating zones<\/h3>\n<p><b>New-DnsZone<\/b> uses the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms682760%28VS.85%29.aspx\">CreateZone<\/a> method from WMI, checking for a few potential errors along the way.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># A new standard Primary forward lookup zone. Returns an object representing <br \/># the new zone<br \/>New-DnsZone domain.example -ZoneType Primary -PassThru<\/span><\/pre>\n<\/blockquote>\n<h3>&nbsp;<\/h3>\n<h3>Creating records<\/h3>\n<p><b>New-DnsRecord<\/b> is one of the most complex cmdlets in the module. It provides an abstract interface to the <b>CreateInstanceFromPropertyData<\/b> method on each of the record classes. Many of the parameters accept pipeline input to further simplify usage.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># A new Host (A) record<br \/>New-DnsRecord -Name mail -RecordType A -Zone domain.example -IPAddress 1.2.3.4<br \/># A new Mail Exchanger (MX) record<br \/>New-DnsRecord -RecordType MX -Zone domain.example '<br \/>&nbsp; -TargetName mail.domain.example -Preference 10<\/span><\/pre>\n<\/blockquote>\n<h2>&nbsp;<\/h2>\n<h2>Advanced tasks<\/h2>\n<h3>Automating secondary zone setup<\/h3>\n<p>On occasion it is nice to be able to create secondary zones for every Primary zone on another server.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Alternate credentials for this operation<br \/>$Credential = Get-Credential<br \/><br \/># The Primary server name (used to access WMI) and the <br \/># IP address $SecondaryServer will use to access the Primary<br \/>$PrimaryServer = \"ThePrimaryServer\"; $PrimaryIP = \"1.2.3.4\"<br \/># A secondary server name (used to access WMI)<br \/>$SecondaryServer = \"TheSecondaryServer\"<br \/><br \/># Get all Primary Forward Lookup Zones from $PrimaryServer and create a<br \/># corresponding Secondary zone on $SecondaryServer<br \/>Get-DnsZone -ZoneType Primary -Filter \"Reverse=$False\" '<br \/>&nbsp;&nbsp;&nbsp; -Server $PrimaryServer -Credential $Credential |<br \/>&nbsp; New-DnsZone -ZoneType Secondary -MasterServer $PrimaryIP '<br \/>&nbsp;&nbsp;&nbsp; -Server $SecondaryServer -PassThru<\/span><\/pre>\n<\/blockquote>\n<p>A <b>ForEach<\/b> (either <b>ForEach<\/b> or <b>ForEach-Object<\/b>) loop is not necessary to accomplish this, however it may be added if greater control over the process is required.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\">Get-DnsZone -ZoneType Primary -Filter \"Reverse=$False\" '<br \/>\n&nbsp;&nbsp;&nbsp; -Server $PrimaryServer -Credential $Credential |\n\tForEach-Object {<br \/>\n\t<br \/>\n&nbsp; # See if the zone exists on $SecondaryServer first<br \/>\n&nbsp; If (!(Get-DnsZone $_.Name -Server $SecondaryServer)) {<br \/>\n&nbsp;&nbsp;&nbsp; # If it does not (!), create the zone<br \/>\n&nbsp;&nbsp;&nbsp; New-DnsZone $_.Name -ZoneType Secondary -MasterServer\n\t$PrimaryIP '<br \/>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -Server $SecondaryServer -PassThru<br \/>\n&nbsp; }<br \/>\n\t}<\/span><\/pre>\n<\/blockquote>\n<p>Strictly speaking, the <b>ForEach-Object<\/b> loop is still not an absolute requirement in this example. <b>Where-Object<\/b> can be used to filter down to zones that only do not exist on the Primary.<\/p>\n<h3>&nbsp;<\/h3>\n<h3>Adding A and PTR records from a CSV file<\/h3>\n<p>One common task is the addition of A and corresponding PTR records. To demonstrate this script a simple CSV file can be used.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\">Name,IPAddress<br \/>www,192.168.1.1<br \/>ftp,192.168.1.2<br \/>mail,192.168.1.3<\/span><\/pre>\n<\/blockquote>\n<p>The tricky part is adding the PTR records without having to manually define the Reverse Lookup Zone name. One possible way to deal with this is to send a DNS query; an approach is similar to that used by dynamic update, a query which attempts to find a server willing to accept an update.<\/p>\n<p>In the example below, <b>Get-Dns<\/b> is used to execute a query for the PTR record. The expected response is <b>NXDOMAIN<\/b> (doesn&#8217;t exist), but that response will contain an Authority section including the server and zone name. That zone name can be used to make the new record.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\">$ServerName = \"TheServer\"<br \/>$ForwardLookupZone = \"domain.example\"<br \/><br \/>Import-Csv Records.csv | ForEach-Object {<br \/>&nbsp; # Create the record in the Forward Lookup Zone<br \/>&nbsp; New-DnsRecord -Name $_.Name -IPAddress $_.IPAddress '<br \/>&nbsp; &nbsp;&nbsp;-Zone $ForwardLookupZone -Type A -Server $ServerName<br \/><br \/>&nbsp; # Find the name of the reverse lookup zone<br \/>&nbsp; $ReverseLookupZone = (Get-Dns $_.IPAddress -RecordType PTR '<br \/>&nbsp;&nbsp;&nbsp; -Server $ServerName).Authority[0].Name<br \/><br \/>&nbsp; # Create the record in the Reverse Lookup Zone<br \/>&nbsp; New-DnsRecord -Name $_.IPAddress -Hostname \"$($_.Name).$ForwardLookupZone\" '<br \/>&nbsp;&nbsp;&nbsp; -Zone $ReverseLookupZone -Type PTR -Server $ServerName<br \/>}<\/span><\/pre>\n<\/blockquote>\n<h3>&nbsp;<\/h3>\n<h3>Debugging name resolution with Get-Dns<\/h3>\n<p>Debugging name resolution under Microsoft Windows is typically limited to either nslookup or the client resolver (normally via ping). <b>Get-Dns<\/b> is more comprehensive, capable of both simple and complex queries.<\/p>\n<blockquote><p><b>Note:<\/b> The examples below make extensive use of <b>domain.example<\/b>. This should be replaced with a valid domain name, many of the examples will return nothing if taken literally.<\/p><\/blockquote>\n<p>The following command performs a Zone Transfer, used when a Secondary server picks up a copy of the zone from a Primary Server. This operation should not be confused with replication of zone data when Active Directory-integrated zones are in use. This command requires access to TCP Port 53 on the server holding the zone (most DNS traffic uses UDP Port 53).<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Equivalent of nslookup using ls -d domain.example<br \/>Get-Dns domain.example -Transfer<br \/># Or<br \/>Get-Dns domain.example axfr<\/span><\/pre>\n<\/blockquote>\n<p>Tracing a request from root using an iterative query; used to verify the delegation chain, to test that DNS requests are directed to the correct servers. The initial servers (Root Hints) are taken from the locally configured DNS service.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Performs a search for the record from the Root DNS servers (display all hops)<br \/># Equivalent of dig domain.example +trace<br \/>Get-Dns domain.example -Iterative<\/span><\/pre>\n<\/blockquote>\n<p><b>NsSearch<\/b> can be used to check that all DNS servers for a domain name reply with the same (or intended) answer. If the answers are different clients may experience problems accessing a resource.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Returns the A record for domain.example from all authoritative name servers<br \/># Equivalent for dig domain.example a +nssearch<br \/>Get-Dns domain.example -RecordType A -NsSearch<\/span><\/pre>\n<\/blockquote>\n<p><b>Get-Dns<\/b> returns all of the fields from a DNS packet by default; this makes the return value a complex nested object. Specific parts of the return value can be selected as demonstrated in the following examples.<\/p>\n<blockquote>\n<pre><span style=\"font-family: courier new,courier\"># Expanding the answer<br \/>Get-Dns www.domain.example a -NsSearch | Select-Object -ExpandProperty Answer<br \/># If only one response is returned<br \/>(Get-Dns www.domain.example a).Answer<br \/># Just the Header<br \/>Get-Dns domain.example | Select-Object -ExpandProperty Header<br \/># Question, Answer, Authority and Additional are always arrays. An element number <br \/># must be used when accessing fields even if only one item exists.<br \/>(Get-Dns www.domain.example a).Question[0].Name<br \/>(Get-Dns www.domain.example a).Question.Count<br \/>(Get-Dns www.domain.example a).Question.GetType()<br \/># Answer and Flags<br \/>Get-Dns domain.example | Select-Object Answer, @{n='Flags';e={ $_.Header.Flags }}<br \/># The server, status code (RCode) and time taken (in milliseconds) for <br \/># an Iterative query<br \/>Get-Dns domain.exmaple -Iterative | Select-Object Server, TimeTaken,<br \/>&nbsp; @{n='RCode';e={ $_.Header.RCode }}<\/span><\/pre>\n<\/blockquote>\n<p>&nbsp;<\/p>\n<p>Chris, thank you for that interesting overview of how to use DnsShell. It is a cool module that illustrates a great way to leverage WMI functionality in Windows PowerShell.<\/p>\n<p>I invite you to follow me on <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingguystwitter\">Twitter<\/a> or <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to me at <a target=\"_blank\" href=\"mailto:scripter@microsoft.com\">scripter@microsoft.com<\/a> or post them on the <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingforum\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p>&nbsp;<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; Summary: Learn how to use a free Windows PowerShell module to ease administration of Windows DNS. &nbsp; Microsoft Scripting Guy Ed Wilson here. I have been talking to Chris Dent for several months now, and I finally convinced him to write a guest blog for me. Guest blogger, Chris Dent, is with us today. [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[196,188,56,37,3,61,45],"class_list":["post-16721","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-chris-dent","tag-dns-server","tag-guest-blogger","tag-networking","tag-scripting-guy","tag-weekend-scripter","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>&nbsp; Summary: Learn how to use a free Windows PowerShell module to ease administration of Windows DNS. &nbsp; Microsoft Scripting Guy Ed Wilson here. I have been talking to Chris Dent for several months now, and I finally convinced him to write a guest blog for me. Guest blogger, Chris Dent, is with us today. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/16721","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=16721"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/16721\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=16721"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=16721"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=16721"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}