How to Find and Use Route53 Hosted Zone ARN for Granular IAM Permissions


11 views

When initially setting up automated DNS management in AWS Route53, many developers (myself included) start with permissive IAM policies using Resource: "*". This works fine with one or two hosted zones, but becomes problematic when you need to:

  • Add new zones that shouldn't be modified by existing automation
  • Implement least-privilege security practices
  • Separate production and development DNS environments

The ARN format for Route53 hosted zones is:

arn:aws:route53:::hostedzone/<hosted-zone-id>

To get these values:

  1. Via AWS Console:
    1. Open Route53 dashboard
    2. Navigate to "Hosted zones"
    3. Note the "Hosted zone ID" column (format: ZXXXXXXXXXXXX)
  2. Via AWS CLI:
    aws route53 list-hosted-zones --query 'HostedZones[*].[Id,Name]' --output text

Here's an example policy that allows DNS updates only for specific hosted zones:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets",
        "route53:ListResourceRecordSets"
      ],
      "Resource": [
        "arn:aws:route53:::hostedzone/Z123456789ABC",
        "arn:aws:route53:::hostedzone/ZDEF987654321"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "route53:ListHostedZones",
      "Resource": "*"
    }
  ]
}

For automation scripts that need to discover zones dynamically, you can use AWS SDKs. Here's a Python example using Boto3:

import boto3

def get_hosted_zone_arn(domain_name):
    client = boto3.client('route53')
    response = client.list_hosted_zones()
    
    for zone in response['HostedZones']:
        if zone['Name'] == domain_name:
            return f"arn:aws:route53:::hostedzone/{zone['Id'].split('/')[-1]}"
    
    raise ValueError(f"Hosted zone not found for {domain_name}")

# Usage:
print(get_hosted_zone_arn("example.com."))  # Note: trailing dot required

When working with AWS Route53 hosted zones in IAM policies, you'll need to specify the Amazon Resource Name (ARN) in the format:

arn:aws:route53:::/

The complete ARN pattern for Route53 hosted zones follows this structure:

arn:aws:route53:::hostedzone/[HOSTED_ZONE_ID]

You can find your hosted zone ID through several methods:

Using AWS Management Console

Navigate to Route53 service > Hosted zones. The ID appears in the format similar to:

Z1PA6795UKMFR9

Using AWS CLI

Run this command to list all hosted zones:

aws route53 list-hosted-zones

The output will include entries like:

{
    "Id": "/hostedzone/Z1PA6795UKMFR9",
    "Name": "example.com.",
    ...
}

Once you have the hosted zone ID, construct the ARN by combining it with the static prefix:

arn:aws:route53:::hostedzone/Z1PA6795UKMFR9

Here's how to restrict access to specific hosted zones in your IAM policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "route53:ChangeResourceRecordSets"
            ],
            "Resource": [
                "arn:aws:route53:::hostedzone/Z1PA6795UKMFR9",
                "arn:aws:route53:::hostedzone/Z2PA6795XKMFQ8"
            ]
        }
    ]
}

For more granular control, you can combine hosted zone restrictions with record set conditions:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "route53:ChangeResourceRecordSets"
            ],
            "Resource": "arn:aws:route53:::hostedzone/Z1PA6795UKMFR9",
            "Condition": {
                "StringLike": {
                    "route53:ChangeResourceRecordSetsNormalizedRecordNames": [
                        "*.dev.example.com"
                    ]
                }
            }
        }
    ]
}

If you encounter permission issues:

  • Verify the hosted zone ID is correct (case-sensitive)
  • Ensure the ARN format matches exactly
  • Check for typos in the policy JSON
  • Remember that changes to IAM policies may take time to propagate