🚀 Executive Summary
TL;DR: Manual DNS migration from GoDaddy to AWS Route53 is prone to human error and time-consuming, especially for numerous records. This guide provides an automated Python script leveraging the GoDaddy API and AWS Boto3 to fetch, format, and push DNS records, significantly reducing errors and migration time.
🎯 Key Takeaways
- GoDaddy and AWS Route53 DNS record formats differ, requiring specific translation for elements like the root domain (‘@’ vs. domain name), TXT record quoting, and MX record priority/value combination.
- Route53 automatically creates its own NS and SOA records upon hosted zone creation; these specific record types must be explicitly skipped during migration from GoDaddy to avoid API errors.
- The migration process involves fetching records from GoDaddy, creating a public hosted zone in Route53 (handling idempotency), formatting the records into a Route53 ChangeBatch, pushing them via Boto3, and finally updating the domain’s nameservers at GoDaddy to point to AWS.
Migrate DNS Records from GoDaddy to AWS Route53 via API
Hey there, Darian Vance here. Let’s talk about DNS migrations. I remember one of my first big ones, years ago. I spent an entire afternoon manually copy-pasting over 100 records from GoDaddy to another provider. I triple-checked everything, but of course, I made a typo on a critical MX record. The client’s email went down for an hour. That’s a mistake you only make once.
Automating this isn’t just about saving a few hours; it’s about eliminating human error during a critical infrastructure change. Once you have this script, you can migrate domains with confidence in minutes. Let’s get this done right.
Prerequisites
Before we dive in, make sure you have the following ready to go:
- Your domain name (e.g.,
example.com). - GoDaddy API Key and Secret. You can generate these in the GoDaddy Developer Portal under “API Keys”.
- An AWS IAM User with programmatic access (Access Key ID and Secret Access Key).
- The IAM User must have the
AmazonRoute53FullAccessmanaged policy attached. In my production setups, I’d create a more granular policy, but this is fine for the migration task. - Python 3 installed on your machine.
The Guide: From GoDaddy to Route53, Step-by-Step
Step 1: Setting Up Your Python Environment
First, get your project folder ready. I’ll skip the standard virtual environment setup since you likely have your own workflow for that. The key is to create an isolated environment and install the necessary Python libraries. You’ll need `boto3` (the AWS SDK) and `requests` (for hitting the GoDaddy API). You can install them with pip: `pip install boto3 requests`.
I also recommend storing your credentials in a `config.env` file and using a library like `python-dotenv` to load them, rather than hardcoding them in the script. For this example, we’ll keep it simple and load them as script variables.
Step 2: The Migration Script
Here’s the complete Python script. Don’t just copy-paste it; read through the comments and my explanation below to understand what each part does. We’re going to fetch from GoDaddy, format the data, and then push it to AWS.
import boto3
import requests
import json
import time
# --- CONFIGURATION ---
# Replace with your actual credentials and domain
GODADDY_API_KEY = 'YOUR_GODADDY_API_KEY'
GODADDY_API_SECRET = 'YOUR_GODADDY_API_SECRET'
DOMAIN_NAME = 'your-domain.com'
# AWS Credentials should be configured via aws-cli or environment variables
# for Boto3 to pick them up automatically.
# Alternatively, you can configure them here:
# AWS_ACCESS_KEY_ID = 'YOUR_AWS_ACCESS_KEY_ID'
# AWS_SECRET_ACCESS_KEY = 'YOUR_AWS_SECRET_ACCESS_KEY'
# AWS_REGION = 'us-east-1' # Route53 is global, but API calls need a region
# --- SCRIPT LOGIC ---
def get_godaddy_records(domain, api_key, api_secret):
"""Fetches all DNS records for a domain from GoDaddy."""
print(f"Fetching DNS records for {domain} from GoDaddy...")
url = f"https://api.godaddy.com/v1/domains/{domain}/records"
headers = {
'Authorization': f'sso-key {api_key}:{api_secret}'
}
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx)
records = response.json()
print(f"Successfully fetched {len(records)} records from GoDaddy.")
return records
except requests.exceptions.RequestException as e:
print(f"Error fetching GoDaddy records: {e}")
return None
def create_route53_hosted_zone(domain_name):
"""Creates a new hosted zone in Route53 for the domain."""
print(f"Creating Route53 hosted zone for {domain_name}...")
route53_client = boto3.client('route53')
try:
response = route53_client.create_hosted_zone(
Name=domain_name,
CallerReference=f"gd-migration-{domain_name}-{int(time.time())}",
HostedZoneConfig={
'Comment': f'Migrated from GoDaddy on {time.strftime("%Y-%m-%d")}',
'PrivateZone': False
}
)
hosted_zone_id = response['HostedZone']['Id']
nameservers = response['DelegationSet']['NameServers']
print(f"Successfully created Hosted Zone with ID: {hosted_zone_id}")
print("IMPORTANT: Note down these nameservers for the final step:")
for ns in nameservers:
print(f"- {ns}")
return hosted_zone_id
except route53_client.exceptions.HostedZoneAlreadyExists:
print(f"Hosted zone for {domain_name} already exists. Finding it...")
# If it exists, we need to find its ID to proceed
hosted_zones = route53_client.list_hosted_zones_by_name(DNSName=domain_name)
for zone in hosted_zones['HostedZones']:
if zone['Name'] == f"{domain_name}.":
print(f"Found existing hosted zone ID: {zone['Id']}")
return zone['Id'].split('/')[-1]
print("Error: Zone exists but could not retrieve ID. Please check AWS Console.")
return None
except Exception as e:
print(f"An error occurred creating the hosted zone: {e}")
return None
def format_records_for_route53(records, domain_name):
"""Converts GoDaddy record format to Route53 change batch format."""
changes = []
print("Formatting records for Route53...")
for record in records:
# Route53 manages its own NS and SOA records, so we skip migrating them.
if record['type'] in ['NS', 'SOA']:
print(f"Skipping {record['type']} record for {record['name']}")
continue
# GoDaddy uses '@' for the root domain, Route53 uses the domain name itself.
record_name = record['name']
if record_name == '@':
record_name = domain_name
else:
record_name = f"{record_name}.{domain_name}"
# Special handling for TXT records which might need quoting
if record['type'] == 'TXT':
# Route53 requires TXT values to be enclosed in quotes
value = f'"{record["data"]}"'
else:
value = record['data']
# Route53 requires MX records to have priority and server name
if record['type'] == 'MX':
resource_records = [{'Value': f"{record['priority']} {record['data']}"}]
else:
resource_records = [{'Value': value}]
change = {
'Action': 'CREATE',
'ResourceRecordSet': {
'Name': record_name,
'Type': record['type'],
'TTL': record['ttl'],
'ResourceRecords': resource_records
}
}
changes.append(change)
print(f"Formatted {len(changes)} records for migration.")
return changes
def push_records_to_route53(hosted_zone_id, changes):
"""Pushes a batch of DNS record changes to a Route53 hosted zone."""
if not changes:
print("No changes to push.")
return True
print(f"Pushing {len(changes)} records to Hosted Zone ID: {hosted_zone_id}...")
route53_client = boto3.client('route53')
try:
# Route53 API has a limit of 1000 changes per request.
# For zones with more, you would need to implement batching.
if len(changes) > 1000:
print("Warning: More than 1000 records. This basic script doesn't support batching.")
# In a real scenario, you'd split `changes` into chunks of 1000.
return False
response = route53_client.change_resource_record_sets(
HostedZoneId=hosted_zone_id,
ChangeBatch={'Changes': changes}
)
print("Successfully submitted change batch. It may take a few minutes to propagate.")
print(f"Change Status: {response['ChangeInfo']['Status']}")
return True
except Exception as e:
print(f"An error occurred pushing records to Route53: {e}")
return False
def main():
"""Main execution function."""
print("--- Starting GoDaddy to Route53 DNS Migration ---")
# 1. Fetch records from GoDaddy
godaddy_records = get_godaddy_records(DOMAIN_NAME, GODADDY_API_KEY, GODADDY_API_SECRET)
if not godaddy_records:
print("Migration failed: Could not retrieve records from GoDaddy.")
return
# 2. Create Hosted Zone in Route53
# Note: This returns the ID without the '/hostedzone/' prefix
hosted_zone_id = create_route53_hosted_zone(DOMAIN_NAME)
if not hosted_zone_id:
print("Migration failed: Could not create or find the hosted zone in Route53.")
return
# 3. Format records for Route53
route53_changes = format_records_for_route53(godaddy_records, DOMAIN_NAME)
# 4. Push records to Route53
success = push_records_to_route53(hosted_zone_id, route53_changes)
if success:
print("\n--- Migration Script Completed Successfully! ---")
print("ACTION REQUIRED: Log in to your GoDaddy account and update the nameservers for")
print(f"{DOMAIN_NAME} to the ones provided by Route53 during the hosted zone creation.")
else:
print("\n--- Migration Script Encountered Errors. Please review the logs. ---")
if __name__ == "__main__":
main()
Step 3: Understanding the Logic
- get_godaddy_records: This is straightforward. We make a GET request to the GoDaddy API, passing our credentials in the `Authorization` header. It returns a JSON list of all DNS records.
- create_route53_hosted_zone: Here, we use Boto3 to create a public hosted zone. The `CallerReference` is crucial; it’s a unique string that makes the request idempotent. This means if you run the script twice, it won’t create a second, duplicate zone. If the zone already exists, my script handles that by finding the existing one. It then prints the new AWS nameservers—these are critical for the final step.
- format_records_for_route53: This is the most important part. GoDaddy’s API format is different from Route53’s. We loop through the GoDaddy records and build a `ChangeBatch` that Route53 understands.
- We explicitly skip NS and SOA records. Route53 creates its own when you make a hosted zone, and trying to migrate GoDaddy’s will cause an error.
- We translate GoDaddy’s use of `@` for the root domain to the actual domain name, which Route53 expects.
- We correctly format special record types like TXT (which needs quotes) and MX (which combines priority and value).
- push_records_to_route53: This function takes the formatted `ChangeBatch` and sends it to AWS using `change_resource_record_sets`. It’s a single API call to create all your records at once.
Pro Tip: The `change_resource_record_sets` API call has a limit of 1,000 “changes” per request. My script doesn’t handle batching for zones larger than that. If you’re migrating a massive domain, you’d need to add logic to split the `changes` list into chunks of 1000 and make multiple API calls.
Step 4: The Cutover – Updating Nameservers
Running the script only copies the records. Your domain is still live on GoDaddy’s DNS. The final step is to tell the internet to start looking at AWS for your DNS records.
- Log in to your GoDaddy account (as the domain registrar).
- Navigate to the DNS management page for your domain.
- Find the section to change your nameservers.
- Replace the existing GoDaddy nameservers with the four nameservers that the script printed out when it created the hosted zone.
- Save the changes.
DNS propagation can take anywhere from a few minutes to 48 hours, but in my experience, it’s usually on the faster side. You’ve successfully migrated your DNS!
Where I Usually Mess Up (Common Pitfalls)
- Forgetting to Skip NS/SOA Records: This is the number one error. Trying to create NS or SOA records in a new hosted zone will always fail because Route53 already created them. The script handles this, but it’s a common manual mistake.
- IAM Permissions: If the script fails at the AWS step, it’s almost always an IAM permissions issue. Double-check that your user has `AmazonRoute53FullAccess` and that your credentials are correctly configured for Boto3.
- TXT Record Formatting: TXT records, especially for SPF or DKIM, can be finicky. Route53 expects the entire value to be enclosed in double quotes. My script does this, but if you modify it, be mindful of this formatting rule.
- Incorrectly Handling the Root Domain: Remember that GoDaddy uses `@` and Route53 uses the apex domain name (e.g., `your-domain.com`). This mismatch can lead to records not being created for your root domain.
Conclusion
And that’s it. You now have a repeatable, automated process for moving DNS records from GoDaddy to AWS Route53. This not only saves a ton of time but also significantly reduces the risk of manual errors during a sensitive operation. In my opinion, any task you have to do more than twice should be automated, and critical infrastructure changes like this are at the top of that list. Keep this script in your toolbox; it’ll definitely come in handy again.
🤖 Frequently Asked Questions
âť“ How can I automate DNS record migration from GoDaddy to AWS Route53?
Automate DNS migration using a Python script that utilizes the GoDaddy API to fetch records and AWS Boto3 to create a Route53 hosted zone, format the records (skipping NS/SOA, handling TXT/MX specifics), and push them as a ChangeBatch to Route53.
âť“ How does this API-driven migration compare to manual DNS record transfer?
API-driven migration drastically reduces human error and saves significant time compared to manual copy-pasting, especially for large record sets. It ensures consistency and reliability during critical infrastructure changes, minimizing downtime risks associated with typos or missed records.
âť“ What are common implementation pitfalls during GoDaddy to Route53 DNS migration?
Common pitfalls include forgetting to skip NS/SOA records (Route53 generates its own), incorrect IAM permissions for Boto3, improper TXT record formatting (requiring double quotes), and misinterpreting GoDaddy’s ‘@’ for the root domain. The provided script addresses NS/SOA skipping, TXT formatting, and root domain translation.
Leave a Reply