Skip to content

Instantly share code, notes, and snippets.

@abubaker417
Last active April 4, 2026 18:50
Show Gist options
  • Select an option

  • Save abubaker417/dfe2039ac58ca036666e2273e146e14a to your computer and use it in GitHub Desktop.

Select an option

Save abubaker417/dfe2039ac58ca036666e2273e146e14a to your computer and use it in GitHub Desktop.
How to Set Up Route 53 with GoDaddy Domain

Step 1 — Create Hosted Zone in Route 53:

Go to Route 53 → Hosted Zones → Create Hosted Zone Enter your domain e.g. yourcompany.com AWS gives you 4 nameservers like:

ns-123.awsdns-45.com ns-456.awsdns-67.net ns-789.awsdns-89.org ns-012.awsdns-34.co.uk Step 2 — Update Nameservers in GoDaddy:

GoDaddy → My Domains → DNS → Nameservers → Change to Custom Paste those 4 AWS nameservers Done. GoDaddy now just holds the domain name, Route 53 handles all DNS

Step 3 — Add your subdomain A records in Route 53: dev.yourcompany.com → A record → EC2 Dev IP qa.yourcompany.com → A record → EC2 QA IP staging.yourcompany.com → A record → EC2 Staging IP

=========================

Complete Step-by-Step Guide — Route 53 Setup

Part 1 — What to Ask Admin (Two Separate Things)

Thing 1 — Add Route 53 Access to Your IAM User Your admin has already given you EC2FullAccess, VPCFullAccess etc. on your user or group. He just needs to add one more managed policy the same way he added those. Tell admin:

"Please attach AmazonRoute53FullAccess managed policy to my IAM user (or the group I am in) — same way you added EC2FullAccess and VPCFullAccess."

Admin steps: AWS Console → IAM → Users → (your username) → Permissions tab → Add Permissions → Attach policies directly → Search: AmazonRoute53FullAccess → Tick it → Next → Add Permissions OR if he manages you via a Group: AWS Console → IAM → User Groups → (your group name) → Permissions tab → Add Permissions → Search: AmazonRoute53FullAccess → Tick it → Add Permissions ✅ This is an AWS built-in policy — no JSON needed, admin just searches and selects it.

Thing 2 — Add Inline Policy to Existing Lambda Role Your Lambda role lambda-ec2-start-stop-role already has:

AmazonEC2FullAccess AWSLambdaBasicExecutionRole

Admin just needs to add Route 53 permission to that same role. Tell admin:

"Please add an inline policy to the existing Lambda role lambda-ec2-start-stop-role"

Admin steps: AWS Console → IAM → Roles → Search: lambda-ec2-start-stop-role → Click on it → Permissions tab → Add Permissions → Create Inline Policy → Click JSON tab → Clear existing content, paste this: json{ "Version": "2012-10-17", "Statement": [ { "Sid": "LambdaRoute53Update", "Effect": "Allow", "Action": [ "route53:ChangeResourceRecordSets", "route53:ListResourceRecordSets", "route53:GetChange" ], "Resource": "*" } ] }

→ Next → Policy name: Route53DNSUpdatePolicy → Create Policy


✅ Done. Lambda can now update Route 53 DNS records.

---

### Difference Between the Two Policies — Why Not Same?

This confused you earlier, so let me clarify clearly:

| | Your IAM User | Lambda Role |
|---|---|---|
| **Who uses it** | You, logging into AWS Console | Lambda function running automatically |
| **What it needs** | Full Route 53 access — create zones, manage all records | Only update existing records — nothing else |
| **Policy to use** | `AmazonRoute53FullAccess` (AWS managed) | Small inline policy (only 3 actions) |
| **Why different** | You need to set everything up manually | Lambda only needs to update one A record |

The big JSON with `CreateHostedZone`, `DeleteHostedZone` etc. that I mentioned earlier — **that was for your user, not Lambda.** `AmazonRoute53FullAccess` already covers all of that, so no need to write custom JSON for your user. Admin just selects the managed policy.

---

## Part 2 — You Do This (After Admin Gives Access)

---

### Step 1 — Create Hosted Zone in Route 53

AWS Console → Route 53 → Hosted Zones → Create Hosted Zone → Domain name: yourcompany.com → Type: Public Hosted Zone → Create


AWS will give you **4 nameservers** like:

ns-123.awsdns-12.com ns-456.awsdns-34.net ns-789.awsdns-56.org ns-012.awsdns-78.co.uk


**Copy all 4 — you need these for GoDaddy.**

---

### Step 2 — Update Nameservers in GoDaddy

Ask whoever has GoDaddy login access:

> *"Please change nameservers for `yourcompany.com` to these 4 AWS nameservers"*

GoDaddy → My Products → DNS → Nameservers → Change → Custom → Enter all 4 nameservers from Route 53 → Save


⏱ Propagation takes **10 minutes to a few hours**. After this GoDaddy is done — you never touch it again for subdomains.

---

### Step 3 — Add Your Subdomain A Records in Route 53

Route 53 → Hosted Zones → yourcompany.com → Create Record

Record 1: Name: dev.yourcompany.com Type: A Value: (current IP of your dev EC2) TTL: 60

Record 2: Name: qa.yourcompany.com Type: A Value: (current IP of your QA EC2) TTL: 60


Keep TTL as **60 seconds** — this means when Lambda updates the IP, it will reflect in 60 seconds only.

---

### Step 4 — Add Tag on Each EC2

AWS Console → EC2 → Instances → (your dev instance) → Tags tab → Manage Tags → Add Tag

Key: dns_name Value: dev.yourcompany.com


Do same for QA instance:

Key: dns_name Value: qa.yourcompany.com

Step 5 — Update Your Start Lambda Function Replace your existing start Lambda with this: pythonimport boto3 import time from botocore.config import Config

ec2_config = Config(region_name='us-east-1') ec2 = boto3.client('ec2', config=ec2_config) route53 = boto3.client('route53', region_name='us-east-1')

HOSTED_ZONE_ID = 'YOUR_HOSTED_ZONE_ID' # paste from Route 53 console

def get_instances(): return [ item["Instances"][0] for item in ec2.describe_instances( Filters=[ {'Name': 'instance-state-name', 'Values': ['stopped']}, {'Name': 'tag:AutoSchedule', 'Values': ['true']} ] ).get("Reservations") ]

def start_instances(instance_ids): ec2.start_instances(InstanceIds=instance_ids)

def wait_for_public_ip(instance_id, retries=10, delay=6): for _ in range(retries): response = ec2.describe_instances(InstanceIds=[instance_id]) ip = response['Reservations'][0]['Instances'][0].get('PublicIpAddress') if ip: return ip time.sleep(delay) return None

def update_route53(dns_name, ip): route53.change_resource_record_sets( HostedZoneId=HOSTED_ZONE_ID, ChangeBatch={ 'Changes': [{ 'Action': 'UPSERT', 'ResourceRecordSet': { 'Name': dns_name, 'Type': 'A', 'TTL': 60, 'ResourceRecords': [{'Value': ip}] } }] } ) print(f"Route53 updated: {dns_name} → {ip}")

def lambda_handler(event, context): instances = get_instances() instance_ids = [i["InstanceId"] for i in instances]

print(f"Starting instances: {instance_ids}")
start_instances(instance_ids)

for instance in instances:
    instance_id = instance["InstanceId"]
    tags = {t['Key']: t['Value'] for t in instance.get('Tags', [])}
    dns_name = tags.get('dns_name')

    if not dns_name:
        print(f"No dns_name tag on {instance_id}, skipping Route53 update")
        continue

    print(f"Waiting for public IP on {instance_id}...")
    ip = wait_for_public_ip(instance_id)

    if ip:
        update_route53(dns_name, ip)
    else:
        print(f"Could not get public IP for {instance_id}, skipping")

**One thing to update in code:**

HOSTED_ZONE_ID = 'YOUR_HOSTED_ZONE_ID'

Get this from:

Route 53 → Hosted Zones → yourcompany.com → copy Hosted Zone ID


---

## Full Picture — Everything Together

ADMIN DOES: ├── AmazonRoute53FullAccess → attached to your IAM user/group └── Route53DNSUpdatePolicy → inline policy on lambda-ec2-start-stop-role

YOU DO: ├── Create Hosted Zone in Route 53 ├── Copy 4 nameservers → give to GoDaddy admin to update ├── Add A records in Route 53 for dev, qa subdomains ├── Add dns_name tag on each EC2 instance ├── Update HOSTED_ZONE_ID in Lambda code └── Release Elastic IPs (no longer needed)

AUTOMATIC AFTER THAT: └── EC2 starts → Lambda → Route 53 A record updated → subdomain resolves in 60 seconds

====================================

=================================== Safe Migration Checklist

✅ Step 1 — List all existing records from GoDaddy DNS page ✅ Step 2 — Create Hosted Zone in Route 53 ✅ Step 3 — Add ALL same records in Route 53 (copy exactly) ✅ Step 4 — Double check every subdomain is in Route 53 ✅ Step 5 — Now change nameservers in GoDaddy to Route 53 ✅ Step 6 — Wait 10-30 mins for propagation ✅ Step 7 — Test every subdomain still works ✅ Step 8 — Remove Elastic IPs after Lambda is working

import boto3
import time
from botocore.config import Config

ec2_config = Config(region_name='us-east-1')
ec2 = boto3.client('ec2', config=ec2_config)
route53 = boto3.client('route53')

# ---- CHANGE THESE VALUES ----
HOSTED_ZONE_ID = 'YOUR_HOSTED_ZONE_ID'
TTL            = 300

DOMAIN_NAMES = [
    'example.info',      # 
]
# ------------------------------

def get_instances():
    return [
        item["Instances"][0]["InstanceId"]
        for item in ec2.describe_instances(
            Filters=[
                {'Name': 'instance-state-name', 'Values': ['stopped']},
                {'Name': 'tag:AutoSchedule', 'Values': ['true']}
            ]
        ).get("Reservations")
    ]

def start_instances(instance_ids):
    ec2.start_instances(InstanceIds=instance_ids)

def wait_for_public_ip(instance_id, retries=10, delay=10):
    """Wait until EC2 instance gets a public IP after starting"""
    for i in range(retries):
        response = ec2.describe_instances(InstanceIds=[instance_id])
        public_ip = response['Reservations'][0]['Instances'][0].get('PublicIpAddress')
        if public_ip:
            print(f"Got public IP: {public_ip}")
            return public_ip
        print(f"Waiting for IP... attempt {i+1}/{retries}")
        time.sleep(delay)
    raise Exception(f"Could not get public IP for instance {instance_id}")

def update_route53(public_ip):
    """Update ALL domains/subdomains in Route 53 with new EC2 public IP"""
    changes = [
        {
            'Action': 'UPSERT',
            'ResourceRecordSet': {
                'Name': domain,
                'Type': 'A',
                'TTL': TTL,
                'ResourceRecords': [{'Value': public_ip}]
            }
        }
        for domain in DOMAIN_NAMES
    ]

    response = route53.change_resource_record_sets(
        HostedZoneId=HOSTED_ZONE_ID,
        ChangeBatch={
            'Comment': 'Auto updated by Lambda on EC2 start',
            'Changes': changes
        }
    )
    print(f"Route 53 updated: {response['ChangeInfo']['Status']}")
    for domain in DOMAIN_NAMES:
        print(f"  → {domain} = {public_ip}")

def lambda_handler(event, context):
    instances = get_instances()

    if not instances:
        print("No instances found to start")
        return

    print(f"Starting the instances {instances}")
    start_instances(instances)

    # Update Route 53 for each instance
    for instance_id in instances:
        try:
            public_ip = wait_for_public_ip(instance_id)
            update_route53(public_ip)
            print(f"DNS updated for {instance_id} → {public_ip}")
        except Exception as e:
            print(f"ERROR for {instance_id}: {str(e)}")
            raise
       

Parallal

import boto3
import time
import concurrent.futures
from botocore.config import Config

ec2_config = Config(region_name='us-east-1')
ec2 = boto3.client('ec2', config=ec2_config)
route53 = boto3.client('route53')

# ---- CHANGE THESE VALUES ----
HOSTED_ZONE_ID = 'YOUR_HOSTED_ZONE_ID'
TTL            = 300

DOMAIN_NAMES = [
    'example.info',
]
# ------------------------------

def get_instances():
    return [
        item["Instances"][0]["InstanceId"]
        for item in ec2.describe_instances(
            Filters=[
                {'Name': 'instance-state-name', 'Values': ['stopped']},
                {'Name': 'tag:AutoSchedule', 'Values': ['true']}
            ]
        ).get("Reservations")
    ]

def start_instances(instance_ids):
    ec2.start_instances(InstanceIds=instance_ids)

def wait_for_public_ip(instance_id, retries=10, delay=10):
    """Wait until EC2 instance gets a public IP after starting"""
    for i in range(retries):
        response = ec2.describe_instances(InstanceIds=[instance_id])
        public_ip = response['Reservations'][0]['Instances'][0].get('PublicIpAddress')
        if public_ip:
            print(f"Got public IP for {instance_id}: {public_ip}")
            return public_ip
        print(f"Waiting for IP {instance_id}... attempt {i+1}/{retries}")
        time.sleep(delay)
    raise Exception(f"Could not get public IP for instance {instance_id}")

def update_route53(public_ip):
    """Update ALL domains in Route 53 with new EC2 public IP"""
    changes = [
        {
            'Action': 'UPSERT',
            'ResourceRecordSet': {
                'Name': domain,
                'Type': 'A',
                'TTL': TTL,
                'ResourceRecords': [{'Value': public_ip}]
            }
        }
        for domain in DOMAIN_NAMES
    ]

    response = route53.change_resource_record_sets(
        HostedZoneId=HOSTED_ZONE_ID,
        ChangeBatch={
            'Comment': 'Auto updated by Lambda on EC2 start',
            'Changes': changes
        }
    )
    print(f"Route 53 updated: {response['ChangeInfo']['Status']}")
    for domain in DOMAIN_NAMES:
        print(f"  → {domain} = {public_ip}")

def process_instance(instance_id):
    """Wait for IP and update Route 53 for a single instance"""
    try:
        public_ip = wait_for_public_ip(instance_id)
        update_route53(public_ip)
        print(f"✅ Done: {instance_id} → {public_ip}")
    except Exception as e:
        print(f"❌ ERROR for {instance_id}: {str(e)}")

def lambda_handler(event, context):
    instances = get_instances()

    if not instances:
        print("No instances found to start")
        return

    print(f"Starting {len(instances)} instances: {instances}")
    start_instances(instances)

    # Process all instances in PARALLEL
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        executor.map(process_instance, instances)

    print("All instances processed!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment