Route 53

Amazon Route 53 is a simple and robust DNS service. This PHP class is a REST-based interface to that service.

Some example code will show how easy it is to use this class. I will assume you are already familiar with how DNS and nameservers in general work, as well as what a hosted zone is and how a hosted zone differs from a domain name. If you need more information on that topic, please consult the official Route 53 documentation.

Once you’ve read these examples, feel free to leave a comment if you have any questions or comments.

Let’s get started! First, you’ll need to create a Route53 class object:

require_once('r53.php');
$r53 = new Route53('Access Key Here', 'Secret Key Here');

Next, create the DNS zone you want hosted:

$result = $r53->createHostedZone('example.com', 'unique identifier', 'An optional comment');

There are three parameters to createHostedZone:

  1. The hosted zone to create. This cannot be a top-level domain name (e.g. “com.”). Amazon Route53 requires that the zone name end with a period, but this class will add it for you if you forget.
  2. A unique identifier that you choose to identify this request. This will prevent accidentally creating two hosted zones for the same domain. (It is both possible and valid to do so, but if you do that you’ll need to provide different unique identifiers for each createHostedZone request.)
  3. An optional comment that you want attached to the hosted zone. This can be any text you choose, and is included with the zone information if you query for it later. You may omit this third parameter if you do not want to add a comment.

Be careful! Amazon Route 53 charges your account $0.50 per hosted zone per month, and this amount is not prorated. This means it costs you $0.50 to make a call to createHostedZone, even if you don’t use it for more than a few seconds, so only create a zone if you’re going to use it!

After this call, $result will contain a bunch of information about the new zone. Let’s take a look:

print_r($result);
-------
Array
(
    [HostedZone] => Array
        (
            [Id] => /hostedzone/Z1PA6795UKMFR9
            [Name] => example.com.
            [CallerReference] => unique identifier
            [Config] => Array
                (
                    [Comment] => An optional comment
                )
        )
    [ChangeInfo] => Array
        (
            [Id] => /change/C1PA6795UKMFR9
            [Status] => PENDING
            [SubmittedAt] => 2011-01-20T23:45:25.320Z
        )
    [NameServers] => Array
        (
            [0] => ns-415.awsdns-51.com
            [1] => ns-726.awsdns-26.net
            [2] => ns-1358.awsdns-41.org
            [3] => ns-1555.awsdns-02.co.uk
        )
)

When you create a zone, it is prepopulated with an SOA record and four NS records. If you did not provide a comment, you would not see the Config array under HostedZone. Under ChangeInfo, you can see that the status is ‘PENDING’. Let’s wait a few seconds,
then check if it’s done yet, using the getChange API:

print_r($r53->getChange('/change/C1PA6795UKMFR9'));
-------
Array
(
    [Id] => /change/C1PA6795UKMFR9
    [Status] => INSYNC
    [SubmittedAt] => 2011-01-20T23:45:25.320Z
)

As you can see, when the change has been propogated to the servers hosting your zone, the status will change to ‘INSYNC’.

Now let’s add an A record:

$change = $r53->prepareChange('CREATE', 'example.com.', 'A', 86400, '192.0.2.1');
print_r($r53->changeResourceRecordSets('/hostedzone/Z1PA6795UKMFR9', $change);
-------
Array
(
    [Id] => /change/C1PA6795UKMXZ9
    [Status] => PENDING
    [SubmittedAt] => 2011-01-20T23:59:59.572Z
)

The object returned by Route53::prepareChange() is not meant to be user-visible. Its contents may change at any time in future versions of this library, so if you poke around in it, your code may not work when you upgrade to a newer version of this library!

Note the period following the domain name — it is not optional! If you leave it off, then AWS will treat the value as relative to the zone root — that means you would be setting an A record for example.com.example.com instead of just for example.com, so be specific!

If you have more than one change to make, you should do them together in one call:

$changes = array();
$changes[] = $r53->prepareChange('CREATE', 'www.example.com.', 'A', 86400, '192.0.2.1');
$changes[] = $r53->prepareChange('CREATE', 'fake.example.com.', 'A', 86400, '192.0.2.1');
$result = $r53->changeResourceRecordSets('/hostedzone/Z1PA6795UKMFR9', $changes);

All of these changes are done in a single transaction — either all of them will fail, or all of them will succeed. Because of that, you will receive only a single change ID, no matter how many changes you make in a single call.

You can also delete records. Suppose you didn’t mean to create fake.example.com:

$change = $r53->prepareChange('DELETE', 'fake.example.com.', 'A', 86400, '192.0.2.1');
$result = $r53->changeResourceRecordSets('/hostedzone/Z1PA6795UKMFR9', $change);

Note that if you’re going to delete a record, all of the parameters to the delete change request must be identical to the record’s current values. The call will fail if any of them are wrong.

You can, of course, create other types of records. The trickiest one is MX records. For MX records, you need to set all of the MX records and their priorities in a single call, like this:

$mailservers = array('10 mx.example.com.', '20 mx2.example.com.', '30 mx3.example.com.');
$change = $r53->prepareChange('CREATE', 'example.com.', 'MX', 86400, $mailservers);
$result = $r53->changeResourceRecordSets('/hostedzone/Z1PA6795UKMFR9', $change);

You can find more information on creating records in the Amazon Route 53 API reference.

You can also list all the hosted zones on your account:

print_r($r53->listHostedZones());
-------
Array
(
    [HostedZone] => Array
        (
            [0] => Array
                (
                    [Id] => /hostedzone/Z1PA6795UKMFR9
                    [Name] => example.com.
                    [CallerReference] => unique identifier
                    [Config] => Array
                        (
                            [Comment] => An optional comment
                        )
                )
            [1] => Array
                (
                    [Id] => /hostedzone/Y1PA6795UKMFR1
                    [Name] => example.net.
                    [CallerReference] => some unique id
                )
        )
    [MaxItems] => 100
    [IsTruncated] => false
)

You can delete hosted zones:

print_r($r53->deleteHostedZone('/hostedzone/Y1PA6795UKMFR1'));
-------
Array
(
    [Id] => /change/C1PA6795UKMXY9
    [Status] => PENDING
    [SubmittedAt] => 2011-01-21T02:17:22.640Z
)

You can call getHostedZone to get more information on the zone, including its nameservers:

print_r($r53->getHostedZone('/hostedzone/Z1PA6795UKMFR9'));
-------
Array
(
    [HostedZone] => Array
        (
            [Id] => /hostedzone/Z1PA6795UKMFR9
            [Name] => example.com.
            [CallerReference] => unique identifier
            [Config] => Array
                (
                    [Comment] => An optional comment
                )
        )
    [NameServers] => Array
        (
            [0] => ns-415.awsdns-51.com
            [1] => ns-726.awsdns-26.net
            [2] => ns-1358.awsdns-41.org
            [3] => ns-1555.awsdns-02.co.uk
        )
)

If that looks familiar, it should: createHostedZone returned that information as well.

Finally, you can list all the records in a zone:

print_r($r53->listResourceRecordSets('/hostedzone/Z1PA6795UKMFR9'));
-------
Array
(
    [ResourceRecordSets] => Array
        (
            [0] => Array
                (
                    [Name] => example.com.
                    [Type] => A
                    [TTL] => 86400
                    [ResourceRecords] => Array
                        (
                            [0] => 192.168.0.2
                        )
                )
            [1] => Array
                (
                    [Name] => example.com.
                    [Type] => MX
                    [TTL] => 86400
                    [ResourceRecords] => Array
                        (
                            [0] => 10 mx1.example.com.
                            [1] => 20 mx2.example.com.
                            [2] => 30 mx3.example.com.
                        )
                )
            [2] => Array
                (
                    [Name] => example.com.
                    [Type] => NS
                    [TTL] => 172800
                    [ResourceRecords] => Array
                        (
                            [0] => ns-1358.awsdns-41.org.
                            [1] => ns-1555.awsdns-02.co.uk.
                            [2] => ns-726.awsdns-26.net.
                            [3] => ns-415.awsdns-51.com.
                        )
                )
            [3] => Array
                (
                    [Name] => example.com.
                    [Type] => SOA
                    [TTL] => 900
                    [ResourceRecords] => Array
                        (
                            [0] => ns-1358.awsdns-41.org. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400
                        )
                )
            [4] => Array
                (
                    [Name] => www.example.com.
                    [Type] => A
                    [TTL] => 86400
                    [ResourceRecords] => Array
                        (
                            [0] => 192.168.0.2
                        )
                )
        )
    [MaxItems] => 100
    [IsTruncated] => false
)

And that’s all there is to it!


Disclaimer: Although I work for Amazon (the RDS team, specifically), this class was neither produced, endorsed nor supported by Amazon.  I wrote this class as a personal project, just for fun.  While I no longer maintain it, I am happy to help resolve issues encountered while trying to use it.

37 Comments »

 
  • Trav says:

    Hey Dan,

    This is great, thank you so much. Just curious if you’re looking for help on this; I’ve been trying to extend the classes to create a rudimentary web-form-based interface for r53DNS. I’m reasonably good at PHP, but not so much of an OOP-er; this seems like an interesting project to get more familiar with it.

  • Alex Smith says:

    Hi Dan,

    Will you be updating this at some point in the near future to support AWS’ announcement today with WRR and ELB for the zone apex?

    Thanks! Great class!
    Alex

  • ckromero says:

    Per Alex Smith, this handles creation of a WRR, hope you find it useful!

    /**
    * Utility function to prepare a Change object for ChangeResourceRecordSets requests with weighted resources!
    * All fields are required.
    *
    * @param string action The action to perform. One of: CREATE, DELETE
    * @param string name The name to perform the action on.
    * If it does not end with ‘.’, then AWS treats the name as relative to the zone root.
    * @param string type The type of record being modified.
    * Must be one of: A, AAAA, CNAME, TXT !!!!!!
    * @param string setIdentifier A unique identifier for a weighted record.
    * @param string weight The weight determines what portion of traffic goes to this record set.
    * @param int ttl The time-to-live value for this record, in seconds.
    * @param array records An array of resource records to attach to this change.
    * Each member of this array can either be a string, or an array of strings.
    * Passing an array of strings will attach multiple values to a single resource record.
    * If a single string is passed as $records instead of an array,
    * it will be treated as a single-member array.
    * @return object An opaque object containing the change request.
    * Do not write code that depends on the contents of this object, as it may change at any time.
    */
    public function prepareWeightedChange($action, $name, $type, $setIdentifier, $weight, $ttl, $records) {
    switch($type){
    case ‘A’:
    case ‘AAAA’:
    case ‘CNAME’:
    case ‘TXT’:
    $change = “\n”;
    $change .= ”.$action.”\n”;
    $change .= “\n”;
    $change .= ”.$name.”\n”;
    $change .= ”.$type.”\n”;
    $change .= ”.$setIdentifier.”\n”;
    $change .= ”.$weight.”\n”;
    $change .= ”.$ttl.”\n”;
    $change .= “\n”;
    break;
    default:
    die(“type needs to be A, AAAA, CNAME, TXT\n”);
    }

    if(!is_array($records)) {
    $records = array($records);
    }

    foreach($records as $record) {
    $change .= “\n”;
    if(is_array($record)) {
    foreach($record as $value) {
    $change .= ”.$value.”\n”;
    }
    }
    else {
    $change .= ”.$record.”\n”;
    }
    $change .= “\n”;
    }

    $change .= “\n”;
    $change .= “\n”;
    $change .= “\n”;

    return $change;
    }

  • Zladivliba says:

    Great lib ! I love it !
    We really miss the possibility to update AMZ with root domains though, this would be a very big plus since amazon updated their API.

    Well, thanks a lot for this work anyway !!

  • Gator says:

    Thanks for sharing your class that provides a very nice abstraction to the Rt 53 API. Something not mentioned here, that I ran across, is that multiple TXT records work much like multiple MX records, eg:


    $txts = array('"text record1"', '"text record2"','"text record3"');
    $change = $r53->prepareChange('CREATE', 'example.com.', 'TXT', 86400, $txts);
    $result = $r53->changeResourceRecordSets('/hostedzone/Z1PA6795UKMFR9', $change);

  • Rick says:

    Great interface to Route53 thanks! Any chance you could add examples for TXT and SPF records?

  • Anna says:

    Thanks so much- great writeup and love the library. I’d love support for “elb-associate-route53-hosted-zone” as well, if you’re inclined.

  • angro says:

    $r53->listResourceRecordSets(ZONE_ID, ‘A’, ‘subdomain.example.com’)

    bug with different subdomains

  • pk says:

    Hi, i am getting problem while deleting the name servers with delete command..Error is : Warning: Route53::changeResourceRecordSets(): Sender – InvalidChangeBatch: Tried to delete resource record set XXXXX.xxx., type NS but the values provided do not match the current values Request Id
    >>these name servers are automatically created when domain zone is created,
    >>i can delete the other resource records which i added manually using changeResourceRecordSets() function. but i am not able to delete the 4 name servers and 1 SOA records.(these are the automatically created resource records.
    pls respond

    • Dan says:

      Deleting those records would defeat the purpose of using Route 53… In other words, if you want to use Route 53, you need those records. It makes sense that you can’t delete them, because you *shouldn’t* delete them.

  • Umar says:

    Hi,

    Is there any possibility to UPDATE A, CNAME or MX records ?

    and what about the TXT records like spf and domains keys

    Best Regards,

    Uamr

    • Dan says:

      If you want to update a record, send a DELETE change and then a CREATE change in the same request (in that order). For example, suppose I want to change the IP address of http://www.example.com from 192.0.2.1 to 192.0.2.2:


      $changes = array();
      $changes[] = $r53->prepareChange('DELETE', 'www.example.com.', 'A', 86400, '192.0.2.1');
      $changes[] = $r53->prepareChange('CREATE', 'www.example.com.', 'A', 86400, '192.0.2.2');
      $result = $r53->changeResourceRecordSets('/hostedzone/Z1PA6795UKMFR9', $changes);

      • Umar says:

        Hi Dan,

        Thanks for you reply,

        One more question about SPF and domainkeys?

        Best Regards,

        Umar

        • Dan says:

          SPF works through TXT records, so for that you’d just add a TXT record to your zone with the appropriate content.

          I don’t know anything about domainkeys, but it probably works more or less the same way.

          • Umar says:

            HI Another Question

            The “listResourceRecordSets” return everything e.g A,CNAME,NS,MX records.

            Is is possible to get specific TYPE record like I want to list only A Records.

            Best Regards,

            Umar

  • Umar says:

    No problem

    My This function done it

    function SearchRecords($records, $type) {
    $result = array();
    foreach($records as $record) {
    if(strtolower($record['Type']) == strtolower($type)) {
    $result [] = $record;
    }
    }
    return ($result) ? $result : false;
    }

    –usage:

    $records = $r53->listResourceRecordSets(‘/hostedzone/ZEF90B0K59IVWO’);

    if(false !== ($a_result = SearchRecords($records['ResourceRecordSets'], “A”)))
    {
    print_r($a_result)
    }

  • Jon Dering says:

    Just wanted to say thank you for writing this. You have saved me hours of headache. Job well done, really.

  • Arthur Wiebe says:

    I like your code. It is now integrated into http://code.google.com/p/emailapi/

  • Mark Rose says:

    Very handy. Thank you!

  • Dan says:

    How do you go about ALIASing a record to an ELB hosted zone?

    • Dan says:

      It looks like that requires using API version 2011-05-05; my class only supports the original 2010-10-01 API version. If this is just a one-time thing, you could use the AWS Console to set it up, otherwise you’ll need to modify the code in r53.php to use the newer API version and add the new operation.

      • Ian B says:

        Ignore my other question – the code is easy enough to understand that I was able to modify it easily enough to support the new API functions relating to weighted sets.

        -ian

    • Hi, this is very helpful class.
      Thank you, Mr. Myers.
      And I needed to create alias records recently, so add a method to do so.

      https://gist.github.com/KitaitiMakoto/4972686

      By using above file, you can create/delete alias record by:


      $changes[] = $r53->prepareChange(...);
      $changes[] = $r53->prepareAliasChange('CREATE', $domain, $aliasHostedZone, $dnsName);
      :
      :
      $result = $r53->changeResourceRecordSets('/hostedzone/' . $hostedZone, $changes);

      Could you try and consider it?
      And I’m very appreciated if you add the method to next version.
      Thanks.

  • Ian B says:

    What about weighted record sets? I get this when listing a record with weights:

    [0] => This resource record set includes an attribute that is unsupported on this Route 53 endpoint. Please consider using a newer endpoint or a tool that does so.

  • Brian H says:

    We’ve encountered a bug where the maxItems parameter, though being passed into the API, doesn’t seem to affect the number of reponses returned (always returns 100). Is this a hard api limitation or is there an easy fix?

  • Brian H says:

    Ah, apparently 100 is the hard max for records returned. Easy to get around due how this api is implemented. Thanks for writing such a well self-documented API!

    $myrecs=$myr53->listResourceRecordSets($myid);
    $nextrecords=$myrecs[NextRecordName];
    while ($nextrecords)
    {
    $newrecs=$myr53->listResourceRecordSets($myid,”,$myrecs[NextRecordName]);
    foreach ($newrecs[ResourceRecordSets] as $setid=>$set) $myrecs[ResourceRecordSets][]=$set;
    $nextrecords=$newrecs[NextRecordName];
    }

  • Sahil says:

    I was constantly getting following error.

    Warning: Route53::listHostedZones(): 60 SSL certificate problem, verify that the CA cert is OK. Details:error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in r53.php on line 539

    After debugging, found the error is coming from Curl with options (CURLOPT_SSL_VERIFYHOST, CURLOPT_SSL_VERIFYPEER) set to true

    To work around this, set following class variables to 0:
    protected $__verifyHost = 0;
    protected $__verifyPeer = 0;

    It worked for now but if you can help me figure out why it failed with options set to true.

    • Dan says:

      You probably need to configure PHP so it knows how to verify SSL certificates (it probably needs to know how to find the trusted certificate store). How you do this will depend on your operating system and (for Linux) the specific distribution you’re using.

  • archit says:

    Thank you very much.This article really helped me.

  • […] code for programming AWS Route53 DNS services. Documentation on the Route53 client can be found at Ordering Disorder. More on r53.php at SourceForge. I added a new function listAllResourceRecordSets to the Route32 […]

  • Hurrah, that’s what I was exploring for, what a stuff! existing here at this weblog,
    thanks admin of this website.

 

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>