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


1 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