Domain Management Automation might sound like a big intimidating string of words, but actually it just means that you can solve the problems that are of importance to you. No matter how big or small they are.

I recently needed to automate some validation for my domains. I was creating a few CNAME records, and I wanted to know if they were available and ready for me to browse. My local Internet provider always plays tricks on me, so I either need to jump on my 3G/4G connection, or use a VPN to see if my new subdomain is available and ready to use. But last week I said enough! I wanted something I could run from my computer's terminal and see if my new records were available.

Basically, I want to type check test.example.com and either get a Ready! or Not yet available response.

Since I work at DNSimple, I have access to internal APIs that I could also use to take this tool even further. This example demonstrates that even without that, it's possible to develop useful tools that combine the DNSimple domain API with other third-party APIs to solve problems quickly.

So I created a small ruby script that uses the DNSimple API and the Google Resolver API. The first thing I did was to understand how to use the Google API. With HTTParty, it was a trivial call.

url = "https://dns.google.com/resolve?name=#{query}&type=ANY"
json = HTTParty.get(url, verify: false) # Skips SSL Certificate validation with `verify: false`
response = JSON.parse(json)

I am using ANY as the DNS record type to start with, but I could have settled down for CNAME since all my records that needed validation are CNAME records. #{query} is a variable that I am passing to the ruby script from the command line.

Here is an example of a response from a Google Public DNS API call:

{
  "Status": 0,
  "TC": false,
  "RD": true,
  "RA": true,
  "AD": false,
  "CD": false,
  "Question": [
    {
      "name": "test.example.com.",
      "type": 255
    }
  ],
  "Answer": [
    {
      "name": "test.example.com.",
      "type": 5,
      "TTL": 86399,
      "data": "test.example.com.s3-website-eu-west-1.amazonaws.com."
    }
  ],
  "Comment": "Response from 162.159.25.4"
}

Once I understood how to use Google's API, how to read the answer, and extract what I needed; I used the DNSimple API to see if the content of my DNS record matches the answer from Google.

dnsimple.zones.list_records(account.id, domain).data.each do |record|
  if record.name == subdomain
    check_with_google(record, query)
  end
end

I retrieve the zone records for the domain in DNSimple and check the content of the record with Google:

def check_with_google(record, query)
  if record.content == ask_google(query)
    puts "✔️ Ready"
  else
    puts "❌ Not yet available"
  end
end

The method ask_google encapsulates the logic that I first discovered with HTTParty. In 50 lines, I have a quick way to verify my CNAME records. It will definitely need some love to support other type of records and improve error handling if something goes wrong, but here is the full script:

#!/usr/bin/env ruby
require "dnsimple"
require "httparty"
require "json"

query = ARGV.last

# You can get a dnsimple access token following this guide: https://support.dnsimple.com/articles/api-access-token/
dnsimple = Dnsimple::Client.new(access_token: "xxx")

# Finds your current dnsimple account
account = dnsimple.identity.whoami.data.account

# Finds the record in dnsimple and asks google if they match
def check(dnsimple, query, account)
  domain, subdomain = extract_domain(query)

  dnsimple.zones.list_records(account.id, domain).data.each do |record|
    if record.name == subdomain
      check_with_google(record, query)
    end
  end
end

def check_with_google(record, query)
  if record.content == ask_google(query)
    puts "✔️ Ready"
  else
    puts "❌ Not yet available"
  end
end

def ask_google(query)
  url = "https://dns.google.com/resolve?name=#{query}&type=ANY"
  json = HTTParty.get(url, verify: false)
  response = JSON.parse(json)

  # Google adds an extra "." at the end. We remove it.
  response["Answer"].first["data"].chomp(".")
rescue NoMethodError
  "not-found-on-google-yet"
end

def extract_domain(query)
  arguments = query.split(".")
  domain = arguments.slice(-2..-1).join(".")
  subdomain = arguments[0..arguments.size - 3].join(".")
  return domain, subdomain
end

check(dnsimple, query, account)

Conclusion

No matter how trivial a small script like this one looks, it greatly improved my workflow and removed guesswork and frustrations. My local ISP likes to hold on to old caches too often, and this little script helps me work around that. It's a tiny but powerful cog that can be plugged into a bigger script if needed.