Summary: Manage DNS in a Windows environment by using Windows PowerShell. Guest Blogger Richard Siddaway shows how to do it in a simple and effective manner.
Hey, Scripting Guy! What can I do with Windows PowerShell to manage my DNS infrastructure?
-- RS
Hello RS,
Microsoft Scripting Guy Ed Wilson here. I am happy to announce that this is Guest Blogger Week. I love to host these guest bloggers because they give our readers a glimpse into other professionals’ minds and knowledge. Today, we have Richard Siddaway. Richard is a technical architect for Serco in the United Kingdom. He works on transformation projects in the local government and commercial arena. With more than 20 years of experience in various aspects of information technology, Richard specializes in the Microsoft environment at an architectural level, especially around Active Directory, Exchange, SQL Server, and infrastructure optimization.
Richard is always looking for the opportunity to automate a process, preferably with Windows PowerShell. Richard founded and currently leads the UK Windows PowerShell User Group. Microsoft has recognized his technical expertise and community activities by presenting him with a Microsoft Most Valued Professional award. Richard has presented to the Directory Experts Conference, at various events at Microsoft in the UK and Europe, and for other UK User Groups. He has a number of articles and technical publications to his credit, and his first print book has recently been released. The subject? Windows PowerShell, of course.
Blog: http://msmvps.com/blogs/RichardSiddaway/Default.aspx
Book details: http://www.manning.com/siddaway
Take it away, Richard…
_______
RS, the short answer to the question about using Windows PowerShell to manage DNS is that you can do anything you want. The longer answer is that you have to work at it a bit. Some areas of working with DNS have already been blogged about by Ed Wilson of the Microsoft Scripting Guys. Here are links to his articles:
Windows PowerShell doesn’t have any way of directly working with DNS. However, there is a great set of WMI classes. They are installed automatically when DNS is installed. At this point, you might be groaning “I don’t like WMI; it’s too hard”. It used to be. Windows PowerShell has brilliant WMI support that makes using it easy and well worth spending a bit of time learning, as you will see.
A new namespace is created for the DNS WMI classes: root\MicrosoftDNS. Because we are using WMI, we can access the DNS server remotely (which makes administration easier). The classes available to work with DNS can be found by using Windows PowerShell:
Get-WmiObject -Namespace root\MicrosoftDNS -List
The DNS classes from a remote DNS server running on a Windows Server 2008 domain controller are shown in the following image.
In a modern Windows environment, most machines will register their own addresses with DNS (we may need to set reverse lookup—PTR—records through DHCP). There is still a need to create DNS records from time to time, such as if you need to create an alias record. You have a number of WMI classes for working with different DNS record types, which you can see here:
Get-WmiObject -Namespace root\MicrosoftDNS -List *type | Format-Wide -Column 3 MicrosoftDNS_MGType MicrosoftDNS_X25Type MicrosoftDNS_AFSDBType MicrosoftDNS_PTRType MicrosoftDNS_KEYType MicrosoftDNS_SRVType MicrosoftDNS_MDType MicrosoftDNS_MBType MicrosoftDNS_AAAAType MicrosoftDNS_ISDNType MicrosoftDNS_MINFOType MicrosoftDNS_RPType MicrosoftDNS_SIGType MicrosoftDNS_MFType MicrosoftDNS_AType MicrosoftDNS_WKSType MicrosoftDNS_WINSRType MicrosoftDNS_SOAType MicrosoftDNS_MXType MicrosoftDNS_WINSType MicrosoftDNS_ATMAType MicrosoftDNS_NSType MicrosoftDNS_NXTType MicrosoftDNS_RTType MicrosoftDNS_CNAMEType MicrosoftDNS_TXTType MicrosoftDNS_HINFOType MicrosoftDNS_MRType
I’ve written various scripts in the past to work with individual record types, and I’ve found that each class has slightly different syntax and requirements. This makes life awkward when you want to start automating this process, because you have to have a different script or function for each record type. I decided I wanted a universal script for creating records so that I could create multiple records at the same time from minimal information. The following script shows the function that I came up with to create A, PTR, MX, and CNAME records—these being the most common ones I have to deal with. We will be using the MicrosoftDNS_ResourceRecord class with varying inputs.
function new-dnsrecord { param( [string]$server, [string]$fzone, [string]$rzone, [string]$computer, [string]$address, [string]$alias, [string]$maildomain, [int]$priority, [switch]$arec, [switch]$ptr, [switch]$cname, [switch]$mx ) ## check DNS server contactable if (-not (Test-Connection -ComputerName $server)){Throw "DNS server not found"} ## split the server fqdn and address $srvr = $server -split "\." $addr = $address -split "\." $rec = [WmiClass]"\\$($srvr[0])\root\MicrosoftDNS:MicrosoftDNS_ResourceRecord" ## ## create records ## ## A if ($arec){ $text = "$computer IN A $address" $rec.CreateInstanceFromTextRepresentation($server, $fzone, $text) } ## CNAME if ($cname){ $text = "$alias IN CNAME $computer" $rec.CreateInstanceFromTextRepresentation($server, $fzone, $text) } ## PTR if ($ptr){ $text = "$($addr[3]).$rzone IN PTR $computer" $rec.CreateInstanceFromTextRepresentation($server, $rzone, $text) } ## MX if ($mx){ $text = "$maildomain IN MX $priority $computer" $rec.CreateInstanceFromTextRepresentation($server, $fzone, $text) } }
The script was written using Windows PowerShell 2.0, but it is usable in Windows PowerShell1.0 with suitable modifications as detailed in the text. The first change is in the section where we test the connectivity to the DNS server (the Test-Connection cmdlet was introduced in version 2.0 of Windows PowerShell). We can overcome this problem by replacing that line with this one:
if ((Get-WmiObject -Query "Select * from Win32_PingStatus WHERE Address = '$server'").StatusCode -ne 0){Throw "DNS server not found"}
More information about using Win32_PingStatus can be found on MSDN at http://msdn.microsoft.com/en-us/library/aa394350(VS.85).aspx#1
OK, let’s pick this function apart. I start out by defining a bunch of parameters. I haven’t used any of the advanced function capabilities here so as to ensure maximum usability of the script. A deliberate decision was made to avoid using parameter sets because this would cause additional complexity when I wanted to create multiple records. The parameters are as follows:
Parameter
Type
Meaning
$server
String
DNS Server FQDN
$fzone
Forward lookup zone
$rzone
Reverse lookup zone
$computer
Computer FQDN for which we are creating DNS entry
$address
Address of the computer
$alias
Alias of the computer
$maildomain
Mail domain
$priority
Int
Mail priority
$arec
Switch
Create A record
$ptr
Create PTR record
$cname
Create Alias record
$mx
Create MX (mail server) record
I haven’t defined any defaults for the parameters. After the parameter definitions, you can check whether the DNS server is contactable as mentioned earlier. It is possible to test if the DNS service was actually running by using one or the other of these Get commands:
Get-Service (Windows PowerShell 2.0 has a computername parameter) Get-Service -Name DNS
WMI (computername parameter in Windows PowerShell versions 2.0 and 1.0)
Get-WmiObject -Class Win32_Service -Filter "Name = 'DNS'"
The next task is to split the server and address into their separate parts. They both have a period (“.”) as the separator. Using the –split operator, you need to escape the dot using the back slash (“\”) character. An alternative, which will work in PowerShell 1.0 as well, is to use the Split() method of the string class:
$server = "server02.manticore.org" $address = "10.10.54.27" $srvr = $server.Split(".") $addr = $address.Split(".")
In this case, you don’t need the escape character.
The final preparatory step is to create an instance of the MicrosoftDNS_ResourceRecord class using the [wmiclass] type accelerator. A type accelerator is used as a shortcut to a .NET Framework class. In this case, we create an instance of the System.Management.ManagementClass. In other words, you are creating a WMI class. When you use Get-WmiObject, you are getting an instance of an existing class, and Windows PowerShell returns an object from the System.Management.ManagementObject class:
$rec = [WmiClass]\\server02\root\MicrosoftDNS:MicrosoftDNS_ResourceRecord $rec | gm
Using gm (an alias for Get-Member), we can view the class of object we have created:
TypeName: System.Management.ManagementClass#ROOT\MicrosoftDNS\MicrosoftDNS_ResourceRecord
You can also determine the methods that are available to this class:
CreateInstanceFromTextRepresentation GetObjectByTextRepresentation
You can use CreateInstanceFromTextRepresentation, which must be one of the longest method names I’ve come across. It’s not something I want to type on a regular basis, and at moments like this, I’m extremely grateful to the inventors of cut and paste.
The last step in the script is to create the DNS records:
if ($arec){ $text = "$computer IN A $address" $rec.CreateInstanceFromTextRepresentation($server, $fzone, $text) }
Now test to see if the switch for that record type is set (remember that switch parameters default to false so you can just ignore them if you don’t need to create a record of a particular type). If it is set, you create the appropriate text for the record and call the CreateInstanceFromTextRepresentation method, which takes three parameters:
Let’s create some records. There are a number of common parameters:
The only additional parameter is the switch to tell the script to create an A record:
new-dnsrecord -server server02 -fzone 'manticore.org' ` -computer 'test27.manticore.org' -address 10.10.54.241 -arec
The reverse lookup zone needs to be supplied as well as the –prt switch:
new-dnsrecord -server server02 -rzone '54.10.10.in-addr.arpa' ` -computer 'test27.manticore.org' -address 10.10.54.241 -ptr
This adds the -alias parameter and uses the –cname switch rather than the –arec switch that we saw with the A record:
new-dnsrecord -server server02 -fzone 'manticore.org' -computer 'test27.manticore.org' ` -alias 'mydnstest.manticore.org' -cname
This one is a bit different in that you add the mail domain along with a priority. The priority dictates which mail server is used; the lowest priority value wins. The example is completed by using an –mx switch:
new-dnsrecord -server server02 -fzone 'manticore.org' -computer 'test27.manticore.org' ` -maildomain 'manticore.org' -priority 10 -mx
You can put several of the previous examples together to use this function to create a number of records at the same time:
new-dnsrecord -server server02 -fzone 'manticore.org' -rzone '54.10.10.in-addr.arpa' ` -computer 'test27.manticore.org' -address 10.10.54.241 -alias 'mydnstest.manticore.org' ` -arec -cname -ptr
This example creates an A record, a PTR record, and an alias, all in one pass. You have to supply the reverse lookup zone, although that could be calculated from the address parameter if required, as well as the alias.
The function can be easily extended if other record types are required. If your environment has a single forward zone and a single reverse zone, it would be possible to set those values as defaults for the relevant parameters. This would save some typing and make the script easier to use.
It is also possible to use this script for the bulk creation of DNS records. Put the data in a CSV file and pipe the contents into foreach with the function call within the foreach.
As well as showing how to use the DNS WMI classes, this script has a number of useful techniques that can be used in other places.
Further information about using Windows PowerShell to administer DNS can be found in chapter 9 of PowerShell in Practice and in a recording of a PowerShell User Group meeting.
RS, that is all there is to using Windows PowerShell to work with DNS. And thank you, Richard, for writing such an informative post. Guest Blogger Week will continue tomorrow when we will talk about SharePoint with Josh Gavant as our guest.
We would love for you to follow us on Twitter and Facebook. If you have any questions, send email to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson and Craig Liebendorfer, Scripting Guys
How does this line work?
$rec.CreateInstanceFromTextRepresentation($server, $fzone, $text)
Isn't a fourth argument required? (msdn.microsoft.com/.../ms682714(v=VS.85).aspx)
Hi Laurence
Thanks for the question. The function in the post essentially does this.
$server = "server02"
$fzone = 'manticore.org'
$text = "test27.manticore.org IN A 10.10.54.241"
$rec = [WmiClass]"\\$server\root\MicrosoftDNS:MicrosoftDNS_ResourceRecord"
You are correct in that the documentation does show four arguments. The fourth is the output from the completion of the method. It isn't needed for the method to work. In fact if you try
$rec.CreateInstanceFromTextRepresentation($server, $fzone, $text, $rout)
You will get an error that states
Cannot find an overload for "CreateInstanceFromTextRepresentation" and the argument count: "4".
We can see how to use the output in this script
$rr = $rec.CreateInstanceFromTextRepresentation($server, $fzone, $text)
$rr.RR -split ","
We put the output into a variable and then take the RR property (a list divided by commas) and split it to make it readable. We get soemthing like this back
MicrosoftDNS_AType.RecordData="10.10.54.241"
RecordClass=1
DnsServerName="SERVER02.Manticore.org"
ContainerName="manticore.org"
DomainName="manticore.org"
OwnerName="test27.manticore.org"
In summary the fourth parameter in the documentation is the return information and we can ignore it if required.
I get the following error when trying to run the function:
Exception calling "CreateInstanceFromTextRepresentation" : "Generic failure "
At D:\Admin\ps\mwiest\DNS_RecordAdd.ps1:29 char:50
+ $rec.CreateInstanceFromTextRepresentation <<<< ($server, $fzone, $text)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WMIMethodException
Any ideas why?
Thanks.
I get "Access Denied" when using WMI with DNS on powershell. The DNS server is on a DC and since we don't want to run the script as a Domain Admin, WMI access is not allowed. How do you make this work so that only the required access to WMI is granted for the user to run the script against a DNS/DC?
Microsoft has an unreleased software utility called "DNS_Exporter_Application_V1.2" that does a great job of exporting and importing DNS entries, with user-supplied parameters like days to allow for "stale" calculations. Unfortunately, the tool is old and doesn't provide any command-line parameters so it can't be automated.
It supplies the following (info or examples in brackets)
Record Type (ie. "MicrosoftDNS_AType")
ContainerName (domain.name)
DnsServerName (server name)
DomainName (domain.name)
OwnerName (what registered it)
RecordData (IP address)
TextRepresentation (record data)
TimeStamp (epoch time)
TimeStamp (Friendly) (human readable time)
Static (T/F)
Stale (T/F)
TTL (time to live in seconds)
I have yet to find a way to emulate this with a Powershell query. Any suggestions? I've tried the user-supplied "DNSShell" module, but it doesn't have this functionality either. Thanks!