Search code examples
amazon-cloudfrontaws-cliamazon-route53

How do I replace a Policy Record with a simple A record that points to a CloudFront Distribution without traffic interruption?


My first question would be, what's the best way to do this? It looks to me like the only way to do it without interrupting traffic would be through the AWS CLI, but if there's an easier way I'm all ears! If the CLI is the way to do it, I'm running into issues with the following command:

aws route53 change-resource-record-sets --hosted-zone-id XXXXXXXXXXXX --change-batch file://update-record.json

The update-record.json contains the following:

{
  "Comment": "Swaps the Policy Record for a simple routing policy",
  "Changes": [
    {
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "www.example.com",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "XXXXXXXXXXXX",
          "DNSName": "xxxxxxxxxxxxx.cloudfront.net",
          "EvaluateTargetHealth": false
        }
      }
    }
  ]
}

The error I'm getting is:

An error occurred (InvalidChangeBatch) when calling the ChangeResourceRecordSets operation: [Tried to create an alias that targets xxxxxxxxxxxxx.cloudfront.net., type A in zone XXXXXXXXXXXX, but the alias target name does not lie within the target zone, Tried to create an alias that targets xxxxxxxxxxxxx.cloudfront.net., type A in zone XXXXXXXXXXXX, but that target was not found]

The Hosted Zone ID for the record in the account is accurate, as is the distribution DNS name. The record name, www.example.com, also exists in the account. The distribution has an alternate domain name (CNAME) of www.example.com.

I've also tried doing the change as a delete and create, rather than an upsert:

{
  "Comment": "Swaps the Policy Record for a simple routing policy",
  "Changes": [
    {
      "Action": "DELETE",
      "ResourceRecordSet": {
        "Name": "www.example.com.",
        "Type": "A",
        "TrafficPolicyInstanceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
      }
    },
    {
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "www.example.com.",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "XXXXXXXXXXXX",
          "DNSName": "xxxxxxxxxxxxx.cloudfront.net",
          "EvaluateTargetHealth": false
        }
      }
    }
  ]
}

Doing this in two steps yields the same error message.


Solution

  • Each domain and subdomain in Route 53 is considered a "Hosted Zone", and as such has a "Hosted Zone ID". However, while the change-resource-record-sets documentation is extensive, it's not very clear about Alias records that point to a CloudFront distribution. I had to dig deeper, and look into the documentation around AliasTarget specifically. There, the documentation states:

    Specify Z2FDTNDATAQYW2. This is always the hosted zone ID when you create an alias record that routes traffic to a CloudFront distribution.
    

    This means that even though I was trying to create an Alias record in the hosted zone with an id of XXXXXXXXXXXX, the ID to use for any Alias record pointing to any CloudFront distribution is Z2FDTNDATAQYW2. Changing the update-record.json to the following works:

    {
      "Comment": "Swaps the Policy Record for a simple routing policy",
      "Changes": [
        {
          "Action": "DELETE",
          "ResourceRecordSet": {
            "Name": "www.example.com.",
            "Type": "A",
            "TrafficPolicyInstanceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
          }
        },
        {
          "Action": "CREATE",
          "ResourceRecordSet": {
            "Name": "www.example.com.",
            "Type": "A",
            "AliasTarget": {
              "HostedZoneId": "Z2FDTNDATAQYW2",
              "DNSName": "xxxxxxxxxxxxx.cloudfront.net",
              "EvaluateTargetHealth": false
            }
          }
        }
      ]
    }
    

    Doing this as a change-resource-record-set prevents interruption in traffic, as:

    Change batches are considered transactional changes. Route 53 validates the changes in the request and then either makes all or none of the changes in the change batch request. This ensures that DNS routing isn't adversely affected by partial changes to the resource record sets in a hosted zone.