adplus-dvertising

Syncing AWS Route53 records to GoDaddy with Laravel

TLDR; Let's write an artisan command to sync AWS Route53 records to GoDaddy in a Laravel app.

Posted by Mohamed Fawzan on 2022-May-10

I was recently working in a project where I had to work with a lot of domains, I mean a lot... I was getting tired of constantly making DNS updates every time my client purchase a domain in GoDaddy. I do have terraform setup for the project, which means I can simply add a domain to AWS with couple of lines, but I couldn't find a terraform module to sync the nameserver records back to GoDaddy. So I ended up writing a simple artisan command in Laravel which can do this for me. There could be many ways (better ways) to do it, but I did in a pretty short time and it saves me a lot of time. Let's see how I did it.

Step 1 : Grab the API keys from Godaddy

  1. Go to https://developer.godaddy.com/
  2. Sign into your Go Daddy account.
  3. Select API Keys
  4. Select Create New API Key
  5. Give a name like AWS-GODADDY
  6. Select Production under Environment.
  7. Click Next and Grab the Keys

Step 2 : Create AWS Credentials [TLDR; version]

  1. Login to your AWS Account
  2. Go to IAM
  3. Create API keys with Route53 permissions

Now that we have what we need, let's prepare the Laravel side of the app.

Step 3: Install the required packages

composer require aws/aws-sdk-php-laravel

PS: If you have the league/flysystem-aws-s3-v3 package installed, then you don't need the above step.

Let's create the command next.

php artisan make:command SyncDNSRecordsCommand

Then I created a method to fetch the NS records from AWS.

private function getAWSHostedZoneDNS($domain)
{
    $key = 'YOUR_AWS_KEY_HERE';
    $secret = 'YOUR_AWS_SECRET_HERE';
    $region = 'YOUR_AWS_REGION_HERE';

    $credentials = [
        'region'      => $region,
        'version'     => 'latest',
        'credentials' => [
            'key'    => $key,
            'secret' => $secret,
        ],
    ];

    $client = new Route53Client($credentials);
    $awsDomain = $client->listHostedZones([
            'dnsname' => "$domain."
        ]
    )->toArray()['HostedZones'];


    $hostedZoneId = collect($awsDomain)
        ->filter(fn($zone) => $zone['Name'] == "$domain.")
        ->map(fn($zone) => $zone['Id'])
        ->values()
        ->get(0);

    return $client->getHostedZone(['Id' => $hostedZoneId])->toArray()['DelegationSet']['NameServers'];
}

and then another helper to update the DNS records to GoDaddy

private function updateGodaddyDNS($domain, $hostedZone)
{
    $url = "https://api.godaddy.com/v1/domains/{$domain}";
    $header = [
        'Authorization' => 'sso-key ' . $this->godaddyAPIKey . ':' . $this->godaddyAPISecret
    ];

    return \Http::withHeaders($header)->patch($url, [
        'nameServers' => $hostedZone
    ])->body();
}

I also made another method to fetch the updated DNS records, so that I can verify it once updated.

private function getGodaddyDNS($domain)
{
    $url = "https://api.godaddy.com/v1/domains/{$domain}";
    $header = [
        'Authorization' => 'sso-key ' . $this->godaddyAPIKey . ':' . $this->godaddyAPISecret
    ];

    $domainDetails = \Http::withHeaders($header)->get($url)->body();

    $domainDetails = json_decode($domainDetails, true);

    return $domainDetails['nameServers'];
}

With these methods in place, my final logic looked like this.

public function handle()
{
    $domain = $this->argument('domain');

    $this->godaddyAPIKey = 'GODADDY_KEY_HERE';
    $this->godaddyAPISecret = 'GODADDY_SECRET_HERE';

    $godaddyDNS = $this->getGodaddyDNS($domain);
    $AWSHostedZoneDNS = $this->getAWSHostedZoneDNS($domain);

    if (collect($godaddyDNS)->diff(collect($AWSHostedZoneDNS))->isNotEmpty()) {
        $this->info("NAMESERVERS Not Matching");
        $this->info(join(', ', $godaddyDNS));
        $this->info(join(', ', $AWSHostedZoneDNS));

        if ($this->confirm("Update Nameservers?")) {
            $resp = $this->updateGodaddyDNS($domain, $AWSHostedZoneDNS);
            $this->info($resp);
        }

    } else {
        $this->info("Nameservers matched");
    }

}

You can see that I have collection returned from AWS, and I had to filter the domain I am looking for to update, that's because I cannot get the correct hosted zone without the ID and I am looking to find the hosted zone with the domain.

I hope this helps someone. If you have any doubts or issues, you can leave a comment 🙃 . I will try to get back to you as soon as possible.