Moving DNS from Rackspace to Amazon Route53 Using Fog

Had to move a few domains and knocked up this script. It mostly just glosses over some differences in the formatting of the records, A and CNAME records translate pretty well but TXT and MX are combined in R53 and have separate attributes in Rackspace. We didn’t have any other record types.

I opened an issue on Fog for strange behaviour if you have 100+ domains in Rackspace.

require 'fog'
require 'pp'

RACKSPACE = {
  :provider => 'Rackspace',
  :rackspace_api_key => 'EDIT' ,
  :rackspace_username => 'EDIT'
}

R53 = {
  :provider => 'AWS',
  :aws_access_key_id => 'EDIT',
  :aws_secret_access_key => 'EDIT'
}

class Provider
  def initialize(connection_spec)
    @connection = Fog::DNS.new(connection_spec)
  end

  def get_records(domain,types=nil)
    types = %w[CNAME A] unless types
    get_domain(domain).records.select{|r| types.include? r.type}
  end

  def get_domain(domain)
    @connection.zones.select{|z| z.domain == domain  or z.domain == add_dot(domain)}[0]
  end

  def create_zone(domain)
    unless get_domain(domain)
      @connection.zones.create(:domain => domain)
    end
  end

  def new_record(domain,name,value,ttl,type)
    name = name.downcase
    # some of the rackspace entries had uppercase but when added to R53 it went to downcase.
    existing =  get_records(domain,[type]).select{|r| add_dot(name) == r.name}
    unless existing.empty?
      puts "#{name} => #{value} (#{type}) EXISTS"
      return
    end
    puts "#{name} => #{value} (#{type})"
    get_domain(domain).records.create({
                                        :name => name,
                                        :value => value,
                                        :ttl => ttl,
                                        :type => type
                                      })
  end

  def add_dot(s)
    return s if s[-1] == '.'
    "#{s}."
  end
end

def migrate(domain)
  puts "MIGRATING #{domain}"
  r53 = Provider.new(R53)
  rs = Provider.new(RACKSPACE)
  r53.create_zone domain
  rs.get_records(domain).each do |record|
    r53.new_record(domain, record.name, record.value, record.ttl, record.type)
  end
  rs.get_records(domain,["TXT"]).group_by{|r| r.name}.each_pair do |name,records|
    texts = records.map(&:value)
    texts.map!{|t| "\"#{t}\""}
    r53.new_record(domain, name, texts , records[0].ttl, "TXT")
  end
  rs.get_records(domain,["MX"]).group_by{|r| r.name}.each_pair do |name,records|
    entries = records.map{|r| "#{r.priority} #{r.value}."}
    r53.new_record(domain, name, entries , records[0].ttl, "MX")
  end
end

DOMAINS = %w[something.com somethingelse.net]

DOMAINS.each do |d|
  migrate d
end