August 12, 2008


In a previous article, I extolled the virtues of DNS on Windows. In particular, I love the scripting interface that DNSCMD provides. In that article, I claimed: “Need to create 500 host records, both forward and reverse, in different domains and subnets? DNSCMD can do it with a 1-line script… there is no *nix alternative that is this simple or powerful.” Well, enough people have been bugging me to provide it so here is is:

REM### Copyright 2008 William L. Dougherty
REM### Script for bulk uploading DNS records into Windows DNS
REM### Script requires hosts.txt file in format: FQDN,IPADDR 1 host per line
REM### Example:,,
for /F “tokens=1-7 delims=,. ” %%a in (hosts.txt) do dnscmd /recordadd %%b.%%c %%a a %%d.%%e.%%f.%%g && dnscmd /recordadd %%g ptr %%a.%%b.%%c

Just put it into a batch file and you are good to go. Simple, right? Well, maybe I should explain. The basic concept here is we use a for loop to iterate through a text file, parsing the components into variables. We then feed these variables into dnscmd to create the A (host) record, and feed them a second time into dnscmd to create the PTR record. I tend to use for loops in a lot of my scripts, because they are an easy way to execute repetitive commands in batch files.

The above script takes as input a file named hosts.txt that must be located in the same directory as the script being executed. This files should be in the format FQDN,IPADDRESS. This script will work with either Active Directory Integrated, or Primary zones, but it does require the zones already exist on the server.

So what do you do if the zones don’t already exist? Another simple script will take care of this. Just iterate through the same list, and create the zone files first. If the zone already exists, then Windows will throw an error to the console, but there are no other bad consequences, so running this every time is fairly benign. You just need to know in advance whether you want Primary or Active Directory Integrated zones. Here are the scripts for each:

Active Directory Integrated:
for /F “tokens=1-7 delims=,. ” %%a in (hosts.txt) do dnscmd /zoneadd %%b.%%c /dsprimary && dnscmd /zoneadd /dsprimary
for /F “tokens=1-7 delims=,. ” %%a in (hosts.txt) do dnscmd /zoneadd %%b.%%c /primary /file %%b.%%c.dns && dnscmd /zoneadd /primary /file

As one of my co-workers is fond of saying, “it’s too easy!” DNSCMD has many, many other options. Two of my favorites are /enumzones and /enumrecords. Using these two options, you can easily dump all your DNS records into text files. I’ve used this to back up my records every night, and I’ve also used this for change control. Dump the files nightly and diff them to the previous night, and you’ll know what changed. This script creates a seperate text record for each of your zones:

dnscmd /enumzones > zones.txt
for /f %%a in (zones.txt) do dnscmd /enumrecords %%a @ > %%a.txt

DNSCMD gives you a scriptable interface into anything/everything you need to administrate zones and records on Windows. Learn to love it, and you’ll never go back to Bind.

Thanks for stopping by.
If you found this article useful, please leave a tip.


  1. Russell Jackson said,

    December 22, 2008 @ 5:02 pm

    You do know this could easily be done with BIND and nsupdate –assuming all
    your zones are dynamic… right? In fact I could easily write a far more
    readable and maintainable python script that does both the RR and zone creation
    with actual error handling and everything. It would be reusable so I wouldn’t
    have to rely on error prone obfuscated one liners to boot.

    We also store our DNS records in a version control repository where all changes
    are audited, tested, and tagged before being pushed out to production. This is
    real change _control_. What you’re talking about is change monitoring. You
    don’t know about changes until after they wreaked havoc without the benefit of
    knowing who did it.

    The DNS records are stored in plain text in the VCS with a custom grammar that
    keeps the records normalized. We define the hostname -> IP address pairing once
    with additional meta-data that identifies what zones the forward and reverse
    belong to as well as if it should create static DHCP host entries. Using this
    we can generate the zone databases on the fly with triggers from a
    configuration management system that detects commits to the repository. The
    configuration management system also guarantees –within reason– that what is
    in the repository is what is on the production boxes. If we want another DNS
    server, we setup a minimally configured box, point the CMS at it, and ten
    minutes later, it’s serving up zones or anything else we need to have it doing.

    I can command the entire thing –without ever touching the production boxes by
    hand– with nothing but a text editor, programming environment of choice and a
    git/svn client. In fact, with TortoiseSVN, we even have our first-tier user
    support staff making changes to the repository. Even if they screw it up, the
    changes never make it to production.

    You keep talking about the right tool for the job, but if you’re maintaining a
    large BIND installation using pico, ssh and for-loops, you’re not following
    your own advice. No wonder you like AD.

    Frankly, I can’t imagine a scenario where UNIX doesn’t have the right _set_
    –not necessarily a single uber one– of tools for any particular job. The
    right-tool-for-the-job mantra is usually just personal bias thinly veiled as
    pragmatism. If you like MS junk, fine; keep using it. Your experience is still
    only anecdotal.

  2. Bill said,

    June 4, 2010 @ 7:11 am

    So what if the record already exists? How does /RecordAdd behave? Does it modify the current value of the record? Or fail?

    How can I capture the output of one dnscmd to use as input for another? What if I want to delete records? To delete the PTR record, I’d need to query for the value of the A record. Then issue another command to delete the PTR record from the appropriate backup zone? Then delete the A record. Examples?

  3. bill said,

    June 4, 2010 @ 7:36 am


    RecordAdd should ignore duplicates, but will add a record if some parameter is different. You can have 2 different A records for the same host with different IP addresses and use DNS round robin for poor man’s load balancing.

    To capture the output, like most windows scripts, you can just pipe it into a file. Example:

    dnscmd /enumzones > zones.txt
    for /f %%a in (zones.txt) do dnscmd /enumrecords %%a @ > %%a.txt

    There I am listing all the zones on my server and putting it into zones.txt. I am then using a FOR command to parse that file to be used by the /enumrecords command. I send the output for each zone into a file %zonename%.txt.

    In your case for deleting records, you could then parse the approprate zone records. Just do an enumrecords on the zone, pipe it to a file, and then parse that file into variables. After that, you issue 2 record delete commands. The syntax from M$ is:

    dnscmd ServerName /recorddelete ZoneName NodeName RRType RRData[/f]

    You’d need 1 line for the A record and one for the PTR record. This may seem like a lot of work, but write your script once and you’ll never have to think about it again.

    When using a FOR script, you can parse a multi-column text file with the value of each column being a seperate variable. You use the /delims switch to specify how the columns are seperated and the /tokens switch to specify which tokens you want. You can get the full syntax here:

    I hope this helps.


  4. Dynamic DNS update testingLife, network and security | Life, network and security said,

    June 1, 2011 @ 3:32 am

    […] […]

RSS feed for comments on this post · TrackBack URI

Leave a Comment