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 $1 per hosted zone per month, and this amount is not prorated. This means it costs you $1 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 is neither produced, endorsed nor supported by Amazon. I maintain this PHP class as a personal project, just for fun.

27 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.

 

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>

 
Easy AdSense by Unreal